fixed warnings (and potential errors) due to integer overflow when
[samba.git] / source3 / smbd / nttrans.c
index 81b3ff26d525d0172af2eb9f663fe5fad8965dcd..60620c98544f87b37af660b210813ba93dd2e25d 100644 (file)
@@ -25,7 +25,6 @@
 extern int DEBUGLEVEL;
 extern int Protocol;
 extern int Client;  
-extern int oplock_sock;
 extern int smb_read_error;
 extern int global_oplock_break;
 extern int chain_size;
@@ -45,6 +44,7 @@ static char *known_nt_pipes[] = {
   "\\ntsvcs",
   "\\lsass",
   "\\lsarpc",
+  "\\winreg",
   NULL
 };
 
@@ -232,6 +232,39 @@ static int send_nt_replies(char *outbuf, int bufsize, char *params,
   return 0;
 }
 
+/****************************************************************************
+ (Hopefully) temporary call to fix bugs in NT5.0beta2. This OS sends unicode
+ strings in NT calls AND DOESN'T SET THE UNICODE BIT !!!!!!!
+****************************************************************************/
+
+static void my_wcstombs(char *dst, uint16 *src, size_t len)
+{
+  size_t i;
+
+  for(i = 0; i < len; i++)
+    dst[i] = (char)SVAL(src,i*2);
+}
+
+static void get_filename( char *fname, char *inbuf, int data_offset, int data_len, int fname_len)
+{
+  if(data_len - fname_len > 1) {
+    /*
+     * NT 5.0 Beta 2 has kindly sent us a UNICODE string
+     * without bothering to set the unicode bit. How kind.
+     *
+     * Firstly - ensure that the data offset is aligned
+     * on a 2 byte boundary - add one if not.
+     */
+    fname_len = fname_len/2;
+    if(data_offset & 1)
+      data_offset++;
+    my_wcstombs( fname, (uint16 *)(inbuf+data_offset), fname_len);
+  } else {
+    StrnCpy(fname,inbuf+data_offset,fname_len);
+  }
+  fname[fname_len] = '\0';
+}
+
 /****************************************************************************
  Save case statics.
 ****************************************************************************/
@@ -279,87 +312,160 @@ static void restore_case_semantics(uint32 file_attributes)
 
 static int map_create_disposition( uint32 create_disposition)
 {
+  int ret;
+
   switch( create_disposition ) {
   case FILE_CREATE:
     /* create if not exist, fail if exist */
-    return 0x10;
+    ret = (FILE_CREATE_IF_NOT_EXIST|FILE_EXISTS_FAIL);
+    break;
   case FILE_SUPERSEDE:
   case FILE_OVERWRITE_IF:
     /* create if not exist, trunc if exist */
-    return 0x12;
+    ret = (FILE_CREATE_IF_NOT_EXIST|FILE_EXISTS_TRUNCATE);
+    break;
   case FILE_OPEN:
     /* fail if not exist, open if exists */
-    return 0x1;
+    ret = (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN);
+    break;
   case FILE_OPEN_IF:
     /* create if not exist, open if exists */
-    return 0x11;
+    ret = (FILE_CREATE_IF_NOT_EXIST|FILE_EXISTS_OPEN);
+    break;
   case FILE_OVERWRITE:
     /* fail if not exist, truncate if exists */
-    return 0x2;
+    ret = (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_TRUNCATE);
+    break;
   default:
     DEBUG(0,("map_create_disposition: Incorrect value for create_disposition = %d\n",
              create_disposition ));
     return -1;
   }
+
+  DEBUG(10,("map_create_disposition: Mapped create_disposition %lx to %x\n",
+        (unsigned long)create_disposition, ret ));
+
+  return ret;
 }
 
 /****************************************************************************
  Utility function to map share modes.
 ****************************************************************************/
 
-static int map_share_mode( uint32 desired_access, uint32 share_access, uint32 file_attributes)
+static int map_share_mode( char *fname, uint32 desired_access, uint32 share_access, uint32 file_attributes)
 {
   int smb_open_mode = -1;
 
   switch( desired_access & (FILE_READ_DATA|FILE_WRITE_DATA) ) {
   case FILE_READ_DATA:
-    smb_open_mode = 0;
+    smb_open_mode = DOS_OPEN_RDONLY;
     break;
   case FILE_WRITE_DATA:
-    smb_open_mode = 1;
+    smb_open_mode = DOS_OPEN_WRONLY;
     break;
   case FILE_READ_DATA|FILE_WRITE_DATA:
-    smb_open_mode = 2;
+    smb_open_mode = DOS_OPEN_RDWR;
     break;
   }
 
+  /*
+   * NB. For DELETE_ACCESS we should really check the
+   * directory permissions, as that is what controls
+   * delete, and for WRITE_DAC_ACCESS we should really
+   * check the ownership, as that is what controls the
+   * chmod. Note that this is *NOT* a security hole (this
+   * note is for you, Andrew) as we are not *allowing*
+   * the access at this point, the actual unlink or
+   * chown or chmod call would do this. We are just helping
+   * clients out by telling them if they have a hope
+   * of any of this succeeding. POSIX acls may still
+   * deny the real call. JRA.
+   */
+
   if (smb_open_mode == -1) {
-    if(desired_access & (DELETE_ACCESS|FILE_WRITE_ATTRIBUTES))
-      smb_open_mode = 2;
-    else if( desired_access & FILE_EXECUTE)
-      smb_open_mode = 0;
+    if(desired_access & (DELETE_ACCESS|WRITE_DAC_ACCESS|WRITE_OWNER_ACCESS|
+                              FILE_EXECUTE|FILE_READ_ATTRIBUTES|
+                              FILE_WRITE_ATTRIBUTES|READ_CONTROL_ACCESS))
+      smb_open_mode = DOS_OPEN_RDONLY;
     else {
-      DEBUG(0,("map_share_mode: Incorrect value for desired_access = %x\n",
-             desired_access));
+      DEBUG(0,("map_share_mode: Incorrect value %lx for desired_access to file %s\n",
+             (unsigned long)desired_access, fname));
       return -1;
     }
   }
 
-  /* Add in the requested share mode - ignore FILE_SHARE_DELETE for now. */
+  /*
+   * Set the special bit that means allow share delete.
+   * This is held outside the normal share mode bits at 1<<15.
+   * JRA.
+   */
+
+  if(share_access & FILE_SHARE_DELETE)
+    smb_open_mode |= ALLOW_SHARE_DELETE;
+
+  /* Add in the requested share mode. */
   switch( share_access & (FILE_SHARE_READ|FILE_SHARE_WRITE)) {
   case FILE_SHARE_READ:
-    smb_open_mode |= (DENY_WRITE<<4);
+    smb_open_mode |= SET_DENY_MODE(DENY_WRITE);
     break;
   case FILE_SHARE_WRITE:
-    smb_open_mode |= (DENY_READ<<4);
+    smb_open_mode |= SET_DENY_MODE(DENY_READ);
     break;
   case (FILE_SHARE_READ|FILE_SHARE_WRITE):
-    smb_open_mode |= (DENY_NONE<<4);
+    smb_open_mode |= SET_DENY_MODE(DENY_NONE);
     break;
   case FILE_SHARE_NONE:
-    smb_open_mode |= (DENY_ALL<<4);
+    smb_open_mode |= SET_DENY_MODE(DENY_ALL);
     break;
   }
 
   /*
-   * Handle a O_SYNC request.
+   * Handle an O_SYNC request.
    */
+
   if(file_attributes & FILE_FLAG_WRITE_THROUGH)
-    smb_open_mode |= (1<<14);
+    smb_open_mode |= FILE_SYNC_OPENMODE;
 
+  DEBUG(10,("map_share_mode: Mapped desired access %lx, share access %lx, file attributes %lx \
+to open_mode %x\n", (unsigned long)desired_access, (unsigned long)share_access,
+                    (unsigned long)file_attributes, smb_open_mode ));
   return smb_open_mode;
 }
 
+/*
+ * This is a *disgusting* hack.
+ * This is *so* bad that even I'm embarrassed (and I
+ * have no shame). Here's the deal :
+ * Until we get the correct SPOOLSS code into smbd
+ * then when we're running with NT SMB support then
+ * NT makes this call with a level of zero, and then
+ * immediately follows it with an open request to
+ * the \\SRVSVC pipe. If we allow that open to
+ * succeed then NT barfs when it cannot open the
+ * \\SPOOLSS pipe immediately after and continually
+ * whines saying "Printer name is invalid" forever
+ * after. If we cause *JUST THIS NEXT OPEN* of \\SRVSVC
+ * to fail, then NT downgrades to using the downlevel code
+ * and everything works as well as before. I hate
+ * myself for adding this code.... JRA.
+ *
+ * The HACK_FAIL_TIME define allows only a 2
+ * second window for this to occur, just in
+ * case...
+ */
+
+static BOOL fail_next_srvsvc = False;
+static time_t fail_time;
+#define HACK_FAIL_TIME 2 /* In seconds. */
+
+void fail_next_srvsvc_open(void)
+{
+  fail_next_srvsvc = True;
+  fail_time = time(NULL);
+  DEBUG(10,("fail_next_srvsvc_open: setting up timeout close of \\srvsvc pipe for print fix.\n"));
+}
+
 /****************************************************************************
  Reply to an NT create and X call on a pipe.
 ****************************************************************************/
@@ -378,6 +484,26 @@ static int nt_open_pipe(char *fname, connection_struct *conn,
                if( strequal(fname,known_nt_pipes[i]))
                        break;
     
+       /*
+        * HACK alert.... see above - JRA.
+        */
+
+       if(fail_next_srvsvc && (time(NULL) > fail_time + HACK_FAIL_TIME)) {
+               fail_next_srvsvc = False;
+               fail_time = (time_t)0;
+               DEBUG(10,("nt_open_pipe: End of timeout close of \\srvsvc pipe for print fix.\n"));
+       }
+
+       if(fail_next_srvsvc && strequal(fname, "\\srvsvc")) {
+               fail_next_srvsvc = False;
+               DEBUG(10,("nt_open_pipe: Deliberately failing open of \\srvsvc pipe for print fix.\n"));
+               return(ERROR(ERRSRV,ERRaccess));
+       }
+
+       /*
+        * End hack alert.... see above - JRA.
+        */
+
        if ( known_nt_pipes[i] == NULL )
                return(ERROR(ERRSRV,ERRaccess));
     
@@ -398,6 +524,7 @@ static int nt_open_pipe(char *fname, connection_struct *conn,
 /****************************************************************************
  Reply to an NT create and X call.
 ****************************************************************************/
+
 int reply_ntcreate_and_X(connection_struct *conn,
                         char *inbuf,char *outbuf,int length,int bufsize)
 {  
@@ -407,16 +534,19 @@ int reply_ntcreate_and_X(connection_struct *conn,
        uint32 file_attributes = IVAL(inbuf,smb_ntcreate_FileAttributes);
        uint32 share_access = IVAL(inbuf,smb_ntcreate_ShareAccess);
        uint32 create_disposition = IVAL(inbuf,smb_ntcreate_CreateDisposition);
+       uint32 create_options = IVAL(inbuf,smb_ntcreate_CreateOptions);
        uint32 fname_len = MIN(((uint32)SVAL(inbuf,smb_ntcreate_NameLength)),
                               ((uint32)sizeof(fname)-1));
+       uint16 root_dir_fid = (uint16)IVAL(inbuf,smb_ntcreate_RootDirectoryFid);
        int smb_ofun;
        int smb_open_mode;
        int smb_attr = (file_attributes & SAMBA_ATTRIBUTES_MASK);
        /* Breakout the oplock request bits so we can set the
           reply bits separately. */
        int oplock_request = 0;
-       int unixmode, pnum = -1;
-       int fmode=0,mtime=0,rmode=0;
+    mode_t unixmode;
+       int pnum = -1;
+       int fmode=0,rmode=0;
        SMB_OFF_T file_len = 0;
        SMB_STRUCT_STAT sbuf;
        int smb_action = 0;
@@ -432,27 +562,61 @@ int reply_ntcreate_and_X(connection_struct *conn,
        if((smb_ofun = map_create_disposition( create_disposition )) == -1)
                return(ERROR(ERRDOS,ERRbadaccess));
 
-       /*
-        * Now contruct the smb_open_mode value from the desired access
-        * and the share access.
-        */
-       
-       if((smb_open_mode = map_share_mode(desired_access, 
-                                          share_access, 
-                                          file_attributes)) == -1) {
-               return(ERROR(ERRDOS,ERRbadaccess));
-       }
-
        /*
         * Get the file name.
         */
-       StrnCpy(fname,smb_buf(inbuf),fname_len);
-       fname[fname_len] = '\0';
+
+    if(root_dir_fid != 0) {
+      /*
+       * This filename is relative to a directory fid.
+       */
+      files_struct *dir_fsp = file_fsp(inbuf,smb_ntcreate_RootDirectoryFid);
+      size_t dir_name_len;
+
+      if(!dir_fsp || !dir_fsp->is_directory)
+        return(ERROR(ERRDOS,ERRbadfid));
+
+      /*
+       * Copy in the base directory name.
+       */
+
+      pstrcpy( fname, dir_fsp->fsp_name );
+      dir_name_len = strlen(fname);
+
+      /*
+       * Ensure it ends in a '\'.
+       */
+
+      if(fname[dir_name_len-1] != '\\' && fname[dir_name_len-1] != '/') {
+        pstrcat(fname, "\\");
+        dir_name_len++;
+      }
+
+      if(fname_len + dir_name_len >= sizeof(pstring))
+        return(ERROR(ERRSRV,ERRfilespecs));
+
+      get_filename(&fname[dir_name_len], inbuf, smb_buf(inbuf)-inbuf, 
+                   smb_buflen(inbuf),fname_len);
+#if 0
+      StrnCpy(&fname[dir_name_len], smb_buf(inbuf),fname_len);
+      fname[dir_name_len+fname_len] = '\0';
+#endif
+
+    } else {
+      
+      get_filename(fname, inbuf, smb_buf(inbuf)-inbuf, 
+                   smb_buflen(inbuf),fname_len);
+
+#if 0
+         StrnCpy(fname,smb_buf(inbuf),fname_len);
+      fname[fname_len] = '\0';
+#endif
+    }
        
        /* If it's an IPC, use the pipe handler. */
 
-       if (IS_IPC(conn)) {
-
+       if (IS_IPC(conn) && lp_nt_pipe_support() && lp_security() != SEC_SHARE)
+       {
                int ret = nt_open_pipe(fname, conn, inbuf, outbuf, &pnum);
                if(ret != 0)
                        return ret;
@@ -482,6 +646,17 @@ int reply_ntcreate_and_X(connection_struct *conn,
                return chain_reply(inbuf,outbuf,length,bufsize);
        }
 
+       /*
+        * Now contruct the smb_open_mode value from the filename, 
+     * desired access and the share access.
+        */
+       
+       if((smb_open_mode = map_share_mode(fname, desired_access, 
+                                          share_access, 
+                                          file_attributes)) == -1) {
+               return(ERROR(ERRDOS,ERRbadaccess));
+       }
+
        /*
         * Ordinary file or directory.
         */
@@ -521,7 +696,7 @@ int reply_ntcreate_and_X(connection_struct *conn,
         * If it's a request for a directory open, deal with it separately.
         */
 
-       if(flags & OPEN_DIRECTORY) {
+       if(create_options & FILE_DIRECTORY_FILE) {
                oplock_request = 0;
                
                open_directory(fsp, conn, fname, smb_ofun, 
@@ -617,7 +792,6 @@ int reply_ntcreate_and_X(connection_struct *conn,
        fmode = dos_mode(conn,fname,&sbuf);
        if(fmode == 0)
                fmode = FILE_ATTRIBUTE_NORMAL;
-       mtime = sbuf.st_mtime;
        if (!fsp->is_directory && (fmode & aDIR)) {
                close_file(fsp,False);
                return(ERROR(ERRDOS,ERRnoaccess));
@@ -661,13 +835,10 @@ int reply_ntcreate_and_X(connection_struct *conn,
        put_long_date(p,sbuf.st_mtime); /* change time */
        p += 8;
        SIVAL(p,0,fmode); /* File Attributes. */
-       p += 12;
-#ifdef LARGE_SMB_OFF_T
-       SIVAL(p,0, file_len & 0xFFFFFFFF);
-       SIVAL(p,4, file_len >> 32);
-#else /* LARGE_SMB_OFF_T */
-       SIVAL(p,0,file_len);
-#endif /* LARGE_SMB_OFF_T */
+       p += 4;
+       SOFF_T(p, 0, file_len);
+       p += 8;
+       SOFF_T(p,0,file_len);
        p += 12;
        SCVAL(p,0,fsp->is_directory ? 1 : 0);
        
@@ -692,17 +863,20 @@ static int call_nt_transact_create(connection_struct *conn,
   uint32 file_attributes = IVAL(params,20);
   uint32 share_access = IVAL(params,24);
   uint32 create_disposition = IVAL(params,28);
+  uint32 create_options = IVAL(params,32);
   uint32 fname_len = MIN(((uint32)IVAL(params,44)),
                          ((uint32)sizeof(fname)-1));
+  uint16 root_dir_fid = (uint16)IVAL(params,4);
   int smb_ofun;
   int smb_open_mode;
   int smb_attr = (file_attributes & SAMBA_ATTRIBUTES_MASK);
   /* Breakout the oplock request bits so we can set the
      reply bits separately. */
   int oplock_request = 0;
-  int unixmode, pnum = -1;
-  int fmode=0,mtime=0,rmode=0;
-  off_t file_len = 0;
+  mode_t unixmode;
+  int pnum = -1;
+  int fmode=0,rmode=0;
+  SMB_OFF_T file_len = 0;
   SMB_STRUCT_STAT sbuf;
   int smb_action = 0;
   BOOL bad_path = False;
@@ -718,19 +892,46 @@ static int call_nt_transact_create(connection_struct *conn,
     return(ERROR(ERRDOS,ERRbadaccess));
 
   /*
-   * Now contruct the smb_open_mode value from the desired access
-   * and the share access.
+   * Get the file name.
    */
 
-  if((smb_open_mode = map_share_mode( desired_access, share_access, file_attributes)) == -1)
-    return(ERROR(ERRDOS,ERRbadaccess));
+  if(root_dir_fid != 0) {
+    /*
+     * This filename is relative to a directory fid.
+     */
 
-  /*
-   * Get the file name.
-   */
+    files_struct *dir_fsp = file_fsp(params,4);
+    size_t dir_name_len;
 
-  StrnCpy(fname,params+53,fname_len);
-  fname[fname_len] = '\0';
+    if(!dir_fsp || !dir_fsp->is_directory)
+      return(ERROR(ERRDOS,ERRbadfid));
+
+    /*
+     * Copy in the base directory name.
+     */
+
+    pstrcpy( fname, dir_fsp->fsp_name );
+    dir_name_len = strlen(fname);
+
+    /*
+     * Ensure it ends in a '\'.
+     */
+
+    if((fname[dir_name_len-1] != '\\') && (fname[dir_name_len-1] != '/')) {
+      pstrcat(fname, "\\");
+      dir_name_len++;
+    }
+
+    if(fname_len + dir_name_len >= sizeof(pstring))
+      return(ERROR(ERRSRV,ERRfilespecs));
+
+    StrnCpy(&fname[dir_name_len], params+53, fname_len);
+    fname[dir_name_len+fname_len] = '\0';
+
+  } else {
+    StrnCpy(fname,params+53,fname_len);
+    fname[fname_len] = '\0';
+  }
 
   /* If it's an IPC, use the pipe handler. */
   if (IS_IPC(conn)) {
@@ -770,11 +971,19 @@ static int call_nt_transact_create(connection_struct *conn,
     oplock_request = (flags & REQUEST_OPLOCK) ? EXCLUSIVE_OPLOCK : 0;
     oplock_request |= (flags & REQUEST_BATCH_OPLOCK) ? BATCH_OPLOCK : 0;
 
+    /*
+     * Now contruct the smb_open_mode value from the desired access
+     * and the share access.
+     */
+
+    if((smb_open_mode = map_share_mode( fname, desired_access, share_access, file_attributes)) == -1)
+      return(ERROR(ERRDOS,ERRbadaccess));
+
     /*
      * If it's a request for a directory open, deal with it separately.
      */
 
-    if(flags & OPEN_DIRECTORY) {
+    if(create_options & FILE_DIRECTORY_FILE) {
 
       oplock_request = 0;
 
@@ -823,7 +1032,6 @@ static int call_nt_transact_create(connection_struct *conn,
       fmode = dos_mode(conn,fname,&sbuf);
       if(fmode == 0)
         fmode = FILE_ATTRIBUTE_NORMAL;
-      mtime = sbuf.st_mtime;
 
       if (fmode & aDIR) {
         close_file(fsp,False);
@@ -889,13 +1097,10 @@ static int call_nt_transact_create(connection_struct *conn,
     put_long_date(p,sbuf.st_mtime); /* change time */
     p += 8;
     SIVAL(p,0,fmode); /* File Attributes. */
-    p += 12;
-#ifdef LARGE_SMB_OFF_T
-    SIVAL(p,0, file_len & 0xFFFFFFFF);
-    SIVAL(p,4, (file_len >> 32));
-#else /* LARGE_SMB_OFF_T */
-    SIVAL(p,0,file_len);
-#endif /* LARGE_SMB_OFF_T */
+    p += 4;
+    SOFF_T(p,0,file_len);
+    p += 8;
+    SOFF_T(p,0,file_len);
   }
 
   /* Send the required number of replies */
@@ -1241,7 +1446,13 @@ static int call_nt_transact_query_security_desc(connection_struct *conn,
                                                 int bufsize, 
                                                 char **ppsetup, char **ppparams, char **ppdata)
 {
-  DEBUG(0,("call_nt_transact_query_security_desc: Currently not implemented.\n"));
+  static BOOL logged_message = False;
+
+  if(!logged_message) {
+    DEBUG(0,("call_nt_transact_query_security_desc: Currently not implemented.\n"));
+    logged_message = True; /* Only print this once... */
+  }
+
   return(ERROR(ERRSRV,ERRnosupport));
 }
    
@@ -1256,8 +1467,13 @@ static int call_nt_transact_set_security_desc(connection_struct *conn,
                                               char **ppsetup, 
                                              char **ppparams, char **ppdata)
 {
-       DEBUG(0,("call_nt_transact_set_security_desc: Currently not implemented.\n"));
-       return(ERROR(ERRSRV,ERRnosupport));
+  static BOOL logged_message = False;
+
+  if(!logged_message) {
+    DEBUG(0,("call_nt_transact_set_security_desc: Currently not implemented.\n"));
+    logged_message = True; /* Only print this once... */
+  }
+  return(ERROR(ERRSRV,ERRnosupport));
 }
    
 /****************************************************************************
@@ -1268,8 +1484,13 @@ static int call_nt_transact_ioctl(connection_struct *conn,
                                   int bufsize, 
                                   char **ppsetup, char **ppparams, char **ppdata)
 {
-       DEBUG(0,("call_nt_transact_ioctl: Currently not implemented.\n"));
-       return(ERROR(ERRSRV,ERRnosupport));
+  static BOOL logged_message = False;
+
+  if(!logged_message) {
+    DEBUG(0,("call_nt_transact_ioctl: Currently not implemented.\n"));
+    logged_message = True; /* Only print this once... */
+  }
+  return(ERROR(ERRSRV,ERRnosupport));
 }
    
 /****************************************************************************
@@ -1368,8 +1589,7 @@ due to being in oplock break state.\n" ));
     while( num_data_sofar < total_data_count || num_params_sofar < total_parameter_count) {
       BOOL ret;
 
-      ret = receive_next_smb(Client,oplock_sock,inbuf,bufsize,
-                             SMB_SECONDARY_WAIT);
+      ret = receive_next_smb(inbuf,bufsize,SMB_SECONDARY_WAIT);
 
       if((ret && (CVAL(inbuf, smb_com) != SMBnttranss)) || !ret) {
         outsize = set_message(outbuf,0,0,True);