ipc.c: map_username is now a BOOL function.
[ira/wip.git] / source3 / smbd / server.c
index 2b2ebb5304e041cf87eb98fd7f26b8de943ea2f9..25ec11abaaf3e5ecc4ac16aeecc0a7a3c776f40b 100644 (file)
@@ -50,6 +50,7 @@ extern BOOL use_mangled_map;
 extern BOOL short_case_preserve;
 extern BOOL case_mangle;
 time_t smb_last_time=(time_t)0;
+extern BOOL global_machine_pasword_needs_changing;
 
 extern int smb_read_error;
 
@@ -1036,6 +1037,47 @@ static int fd_attempt_open(char *fname, int flags, int mode)
   return fd;
 }
 
+/****************************************************************************
+Cache a uid_t currently with this file open. This is an optimization only
+used when multiple sessionsetup's have been done to one smbd.
+****************************************************************************/
+static void fd_add_to_uid_cache(file_fd_struct *fd_ptr, uid_t u)
+{
+  if(fd_ptr->uid_cache_count >= sizeof(fd_ptr->uid_users_cache)/sizeof(uid_t))
+    return;
+  fd_ptr->uid_users_cache[fd_ptr->uid_cache_count++] = u;
+}
+
+/****************************************************************************
+Remove a uid_t that currently has this file open. This is an optimization only
+used when multiple sessionsetup's have been done to one smbd.
+****************************************************************************/
+static void fd_remove_from_uid_cache(file_fd_struct *fd_ptr, uid_t u)
+{
+  int i;
+  for(i = 0; i < fd_ptr->uid_cache_count; i++)
+    if(fd_ptr->uid_users_cache[i] == u) {
+      if(i < (fd_ptr->uid_cache_count-1))
+        memmove((char *)&fd_ptr->uid_users_cache[i], (char *)&fd_ptr->uid_users_cache[i+1],
+               sizeof(uid_t)*(fd_ptr->uid_cache_count-1-i) );
+      fd_ptr->uid_cache_count--;
+    }
+  return;
+}
+
+/****************************************************************************
+Check if a uid_t that currently has this file open is present. This is an
+optimization only used when multiple sessionsetup's have been done to one smbd.
+****************************************************************************/
+static BOOL fd_is_in_uid_cache(file_fd_struct *fd_ptr, uid_t u)
+{
+  int i;
+  for(i = 0; i < fd_ptr->uid_cache_count; i++)
+    if(fd_ptr->uid_users_cache[i] == u)
+      return True;
+  return False;
+}
+
 /****************************************************************************
 fd support routines - attempt to find an already open file by dev
 and inode - increments the ref_count of the returned file_fd_struct *.
@@ -1069,6 +1111,7 @@ Increments the ref_count of the returned entry.
 ****************************************************************************/
 static file_fd_struct *fd_get_new(void)
 {
+  extern struct current_user current_user;
   int i;
   file_fd_struct *fd_ptr;
 
@@ -1081,9 +1124,11 @@ static file_fd_struct *fd_get_new(void)
       fd_ptr->fd_readonly = -1;
       fd_ptr->fd_writeonly = -1;
       fd_ptr->real_open_flags = -1;
+      fd_ptr->uid_cache_count = 0;
+      fd_add_to_uid_cache(fd_ptr, (uid_t)current_user.uid);
       fd_ptr->ref_count++;
       /* Increment max used counter if neccessary, cuts down
-        on search time when re-using */
+         on search time when re-using */
       if(i > max_file_fd_used)
         max_file_fd_used = i;
       DEBUG(3,("Allocated new file_fd_struct %d, dev = %x, inode = %x\n",
@@ -1121,31 +1166,105 @@ Decrements the ref_count and returns it.
 ****************************************************************************/
 static int fd_attempt_close(file_fd_struct *fd_ptr)
 {
+  extern struct current_user current_user;
+
   DEBUG(3,("fd_attempt_close on file_fd_struct %d, fd = %d, dev = %x, inode = %x, open_flags = %d, ref_count = %d.\n",
-          fd_ptr - &FileFd[0],
-          fd_ptr->fd, fd_ptr->dev, fd_ptr->inode,
-          fd_ptr->real_open_flags,
-          fd_ptr->ref_count));
+          fd_ptr - &FileFd[0],
+          fd_ptr->fd, fd_ptr->dev, fd_ptr->inode,
+          fd_ptr->real_open_flags,
+          fd_ptr->ref_count));
   if(fd_ptr->ref_count > 0) {
     fd_ptr->ref_count--;
     if(fd_ptr->ref_count == 0) {
       if(fd_ptr->fd != -1)
         close(fd_ptr->fd);
       if(fd_ptr->fd_readonly != -1)
-       close(fd_ptr->fd_readonly);
+        close(fd_ptr->fd_readonly);
       if(fd_ptr->fd_writeonly != -1)
-       close(fd_ptr->fd_writeonly);
+        close(fd_ptr->fd_writeonly);
       fd_ptr->fd = -1;
       fd_ptr->fd_readonly = -1;
       fd_ptr->fd_writeonly = -1;
       fd_ptr->real_open_flags = -1;
       fd_ptr->dev = (uint32)-1;
       fd_ptr->inode = (uint32)-1;
-    }
+      fd_ptr->uid_cache_count = 0;
+    } else
+      fd_remove_from_uid_cache(fd_ptr, (uid_t)current_user.uid);
   } 
  return fd_ptr->ref_count;
 }
 
+/****************************************************************************
+fd support routines - check that current user has permissions
+to open this file. Used when uid not found in optimization cache.
+This is really ugly code, as due to POSIX locking braindamage we must
+fork and then attempt to open the file, and return success or failure
+via an exit code.
+****************************************************************************/
+static BOOL check_access_allowed_for_current_user( char *fname, int accmode )
+{
+  pid_t child_pid;
+
+  if((child_pid = fork()) < 0) {
+    DEBUG(0,("check_access_allowed_for_current_user: fork failed.\n"));
+    return False;
+  }
+
+  if(child_pid) {
+    /*
+     * Parent.
+     */
+    pid_t wpid;
+    int status_code;
+    if ((wpid = sys_waitpid(child_pid, &status_code, 0)) < 0) {
+      DEBUG(0,("check_access_allowed_for_current_user: The process is no longer waiting!\n"));
+      return(False);
+    }
+
+    if (child_pid != wpid) {
+      DEBUG(0,("check_access_allowed_for_current_user: We were waiting for the wrong process ID\n"));
+      return(False);
+    }
+#if defined(WIFEXITED) && defined(WEXITSTATUS)
+    if (WIFEXITED(status_code) == 0) {
+      DEBUG(0,("check_access_allowed_for_current_user: The process exited while we were waiting\n"));
+      return(False);
+    }
+    if (WEXITSTATUS(status_code) != 0) {
+      DEBUG(9,("check_access_allowed_for_current_user: The status of the process exiting was %d. Returning access denied.\n", status_code));
+      return(False);
+    }
+#else /* defined(WIFEXITED) && defined(WEXITSTATUS) */
+    if(status_code != 0) {
+      DEBUG(9,("check_access_allowed_for_current_user: The status of the process exiting was %d. Returning access denied.\n", status_code));
+      return(False);
+    }
+#endif /* defined(WIFEXITED) && defined(WEXITSTATUS) */
+
+    /*
+     * Success - the child could open the file.
+     */
+    DEBUG(9,("check_access_allowed_for_current_user: The status of the process exiting was %d. Returning access allowed.\n", status_code));
+    return True;
+  } else {
+    /*
+     * Child.
+     */
+    int fd;
+    DEBUG(9,("check_access_allowed_for_current_user: Child - attempting to open %s with mode %d.\n", fname, accmode ));
+    if((fd = fd_attempt_open( fname, accmode, 0)) < 0) {
+      /* Access denied. */
+      _exit(EACCES);
+    }
+    close(fd);
+    DEBUG(9,("check_access_allowed_for_current_user: Child - returning ok.\n"));
+    _exit(0);
+  }
+
+  return False;
+}
+
 /****************************************************************************
 open a file
 ****************************************************************************/
@@ -1228,14 +1347,41 @@ static void open_file(int fnum,int cnum,char *fname1,int flags,int mode, struct
    * reference count (fd_get_already_open increments the ref_count).
    */
   if((fd_ptr = fd_get_already_open(sbuf))!= 0) {
+    /*
+     * File was already open.
+     */
 
-    /* File was already open. */
+    /* 
+     * Check it wasn't open for exclusive use.
+     */
     if((flags & O_CREAT) && (flags & O_EXCL)) {
       fd_ptr->ref_count--;
       errno = EEXIST;
       return;
     }
 
+    /*
+     * Ensure that the user attempting to open
+     * this file has permissions to do so, if
+     * the user who originally opened the file wasn't
+     * the same as the current user.
+     */
+
+    if(!fd_is_in_uid_cache(fd_ptr, (uid_t)current_user.uid)) {
+      if(!check_access_allowed_for_current_user( fname, accmode )) {
+        /* Error - permission denied. */
+        DEBUG(3,("Permission denied opening file %s (flags=%d, accmode = %d)\n",
+              fname, flags, accmode));
+        /* Ensure the ref_count is decremented. */
+        fd_ptr->ref_count--;
+        fd_remove_from_uid_cache(fd_ptr, (uid_t)current_user.uid);
+        errno = EACCES;
+        return;
+      }
+    }
+
+    fd_add_to_uid_cache(fd_ptr, (uid_t)current_user.uid);
+
     /* 
      * If not opened O_RDWR try
      * and do that here - a chmod may have been done
@@ -1255,6 +1401,7 @@ static void open_file(int fnum,int cnum,char *fname1,int flags,int mode, struct
       DEBUG(3,("Error opening (already open for flags=%d) file %s (%s) (flags=%d)\n",
                fd_ptr->real_open_flags, fname,strerror(EACCES),flags));
       check_for_pipe(fname);
+      fd_remove_from_uid_cache(fd_ptr, (uid_t)current_user.uid);
       fd_ptr->ref_count--;
       return;
     }
@@ -1292,7 +1439,7 @@ static void open_file(int fnum,int cnum,char *fname1,int flags,int mode, struct
         fd_ptr->fd = fd_attempt_open(fname, open_flags|O_WRONLY, mode);
         fd_ptr->real_open_flags = O_WRONLY;
       } else {
-       fd_ptr->fd = fd_attempt_open(fname, open_flags|O_RDONLY, mode);
+        fd_ptr->fd = fd_attempt_open(fname, open_flags|O_RDONLY, mode);
         fd_ptr->real_open_flags = O_RDONLY;
       }
     }
@@ -1318,90 +1465,91 @@ static void open_file(int fnum,int cnum,char *fname1,int flags,int mode, struct
   }
     
   if (fd_ptr->fd < 0)
-    {
-      DEBUG(3,("Error opening file %s (%s) (flags=%d)\n",
-              fname,strerror(errno),flags));
-      /* Ensure the ref_count is decremented. */
-      fd_attempt_close(fd_ptr);
-      check_for_pipe(fname);
-      return;
-    }
+  {
+    DEBUG(3,("Error opening file %s (%s) (flags=%d)\n",
+      fname,strerror(errno),flags));
+    /* Ensure the ref_count is decremented. */
+    fd_attempt_close(fd_ptr);
+    check_for_pipe(fname);
+    return;
+  }
 
   if (fd_ptr->fd >= 0)
-    {
-      if(sbuf == 0) {
-        /* Do the fstat */
-        if(fstat(fd_ptr->fd, &statbuf) == -1) {
-          /* Error - backout !! */
-          DEBUG(3,("Error doing fstat on fd %d, file %s (%s)\n",
-                   fd_ptr->fd, fname,strerror(errno)));
-          /* Ensure the ref_count is decremented. */
-          fd_attempt_close(fd_ptr);
-          return;
-        }
-        sbuf = &statbuf;
+  {
+    if(sbuf == 0) {
+      /* Do the fstat */
+      if(fstat(fd_ptr->fd, &statbuf) == -1) {
+        /* Error - backout !! */
+        DEBUG(3,("Error doing fstat on fd %d, file %s (%s)\n",
+                 fd_ptr->fd, fname,strerror(errno)));
+        /* Ensure the ref_count is decremented. */
+        fd_attempt_close(fd_ptr);
+        return;
       }
-      /* Set the correct entries in fd_ptr. */
-      fd_ptr->dev = (uint32)sbuf->st_dev;
-      fd_ptr->inode = (uint32)sbuf->st_ino;
-
-      fsp->fd_ptr = fd_ptr;
-      Connections[cnum].num_files_open++;
-      fsp->mode = sbuf->st_mode;
-      GetTimeOfDay(&fsp->open_time);
-      fsp->vuid = current_user.vuid;
-      fsp->size = 0;
-      fsp->pos = -1;
-      fsp->open = True;
-      fsp->mmap_ptr = NULL;
-      fsp->mmap_size = 0;
-      fsp->can_lock = True;
-      fsp->can_read = ((flags & O_WRONLY)==0);
-      fsp->can_write = ((flags & (O_WRONLY|O_RDWR))!=0);
-      fsp->share_mode = 0;
-      fsp->print_file = Connections[cnum].printer;
-      fsp->modified = False;
-      fsp->granted_oplock = False;
-      fsp->sent_oplock_break = False;
-      fsp->cnum = cnum;
-      string_set(&fsp->name,dos_to_unix(fname,False));
-      fsp->wbmpx_ptr = NULL;      
+      sbuf = &statbuf;
+    }
 
-      /*
-       * If the printer is marked as postscript output a leading
-       * file identifier to ensure the file is treated as a raw
-       * postscript file.
-       * This has a similar effect as CtrlD=0 in WIN.INI file.
-       * tim@fsg.com 09/06/94
-       */
-      if (fsp->print_file && POSTSCRIPT(cnum) && 
-         fsp->can_write) 
-       {
-         DEBUG(3,("Writing postscript line\n"));
-         write_file(fnum,"%!\n",3);
-       }
-      
-      DEBUG(2,("%s %s opened file %s read=%s write=%s (numopen=%d fnum=%d)\n",
-              timestring(),Connections[cnum].user,fname,
-              BOOLSTR(fsp->can_read),BOOLSTR(fsp->can_write),
-              Connections[cnum].num_files_open,fnum));
+    /* Set the correct entries in fd_ptr. */
+    fd_ptr->dev = (uint32)sbuf->st_dev;
+    fd_ptr->inode = (uint32)sbuf->st_ino;
+
+    fsp->fd_ptr = fd_ptr;
+    Connections[cnum].num_files_open++;
+    fsp->mode = sbuf->st_mode;
+    GetTimeOfDay(&fsp->open_time);
+    fsp->vuid = current_user.vuid;
+    fsp->size = 0;
+    fsp->pos = -1;
+    fsp->open = True;
+    fsp->mmap_ptr = NULL;
+    fsp->mmap_size = 0;
+    fsp->can_lock = True;
+    fsp->can_read = ((flags & O_WRONLY)==0);
+    fsp->can_write = ((flags & (O_WRONLY|O_RDWR))!=0);
+    fsp->share_mode = 0;
+    fsp->print_file = Connections[cnum].printer;
+    fsp->modified = False;
+    fsp->granted_oplock = False;
+    fsp->sent_oplock_break = False;
+    fsp->cnum = cnum;
+    string_set(&fsp->name,dos_to_unix(fname,False));
+    fsp->wbmpx_ptr = NULL;      
 
+    /*
+     * If the printer is marked as postscript output a leading
+     * file identifier to ensure the file is treated as a raw
+     * postscript file.
+     * This has a similar effect as CtrlD=0 in WIN.INI file.
+     * tim@fsg.com 09/06/94
+     */
+    if (fsp->print_file && POSTSCRIPT(cnum) && fsp->can_write) 
+    {
+      DEBUG(3,("Writing postscript line\n"));
+      write_file(fnum,"%!\n",3);
     }
+      
+    DEBUG(2,("%s %s opened file %s read=%s write=%s (numopen=%d fnum=%d)\n",
+          timestring(),
+          *sesssetup_user ? sesssetup_user : Connections[cnum].user,fname,
+          BOOLSTR(fsp->can_read),BOOLSTR(fsp->can_write),
+          Connections[cnum].num_files_open,fnum));
+
+  }
 
 #if USE_MMAP
   /* mmap it if read-only */
   if (!fsp->can_write)
-    {
-      fsp->mmap_size = file_size(fname);
-      fsp->mmap_ptr = (char *)mmap(NULL,fsp->mmap_size,
-                                         PROT_READ,MAP_SHARED,fsp->fd_ptr->fd,0);
+  {
+    fsp->mmap_size = file_size(fname);
+    fsp->mmap_ptr = (char *)mmap(NULL,fsp->mmap_size,
+                                 PROT_READ,MAP_SHARED,fsp->fd_ptr->fd,0);
 
-      if (fsp->mmap_ptr == (char *)-1 || !fsp->mmap_ptr)
-       {
-         DEBUG(3,("Failed to mmap() %s - %s\n",fname,strerror(errno)));
-         fsp->mmap_ptr = NULL;
-       }
+    if (fsp->mmap_ptr == (char *)-1 || !fsp->mmap_ptr)
+    {
+      DEBUG(3,("Failed to mmap() %s - %s\n",fname,strerror(errno)));
+      fsp->mmap_ptr = NULL;
     }
+  }
 #endif
 }
 
@@ -2192,16 +2340,28 @@ int find_service(char *service)
    if (iService < 0)
    {
       char *phome_dir = get_home_dir(service);
+
+      if(!phome_dir)
+      {
+        /*
+         * Try mapping the servicename, it may
+         * be a Windows to unix mapped user name.
+         */
+        if(map_username(service))
+          phome_dir = get_home_dir(service);
+      }
+
       DEBUG(3,("checking for home directory %s gave %s\n",service,
-           phome_dir?phome_dir:"(NULL)"));
+            phome_dir?phome_dir:"(NULL)"));
+
       if (phome_dir)
       {   
-        int iHomeService;
-        if ((iHomeService = lp_servicenumber(HOMES_NAME)) >= 0)
-        {
-           lp_add_home(service,iHomeService,phome_dir);
-           iService = lp_servicenumber(service);
-        }
+        int iHomeService;
+        if ((iHomeService = lp_servicenumber(HOMES_NAME)) >= 0)
+        {
+          lp_add_home(service,iHomeService,phome_dir);
+          iService = lp_servicenumber(service);
+        }
       }
    }
 
@@ -2232,34 +2392,36 @@ int find_service(char *service)
 
    /* just possibly it's a default service? */
    if (iService < 0) 
+   {
+     char *pdefservice = lp_defaultservice();
+     if (pdefservice && *pdefservice && !strequal(pdefservice,service))
      {
-       char *pdefservice = lp_defaultservice();
-       if (pdefservice && *pdefservice && !strequal(pdefservice,service)) {
-         /*
-          * We need to do a local copy here as lp_defaultservice() 
-          * returns one of the rotating lp_string buffers that
-          * could get overwritten by the recursive find_service() call
-          * below. Fix from Josef Hinteregger <joehtg@joehtg.co.at>.
-          */
-         pstring defservice;
-         pstrcpy(defservice, pdefservice);
-        iService = find_service(defservice);
-        if (iService >= 0) {
-          string_sub(service,"_","/");
-          iService = lp_add_service(service,iService);
-        }
+       /*
+        * We need to do a local copy here as lp_defaultservice() 
+        * returns one of the rotating lp_string buffers that
+        * could get overwritten by the recursive find_service() call
+        * below. Fix from Josef Hinteregger <joehtg@joehtg.co.at>.
+        */
+       pstring defservice;
+       pstrcpy(defservice, pdefservice);
+       iService = find_service(defservice);
+       if (iService >= 0)
+       {
+         string_sub(service,"_","/");
+         iService = lp_add_service(service,iService);
        }
      }
+   }
 
    if (iService >= 0)
-      if (!VALID_SNUM(iService))
-      {
-         DEBUG(0,("Invalid snum %d for %s\n",iService,service));
-        iService = -1;
-      }
+     if (!VALID_SNUM(iService))
+     {
+       DEBUG(0,("Invalid snum %d for %s\n",iService,service));
+       iService = -1;
+     }
 
    if (iService < 0)
-      DEBUG(3,("find_service() failed to find service %s\n", service));
+     DEBUG(3,("find_service() failed to find service %s\n", service));
 
    return (iService);
 }
@@ -2866,6 +3028,7 @@ pid %d, port %d, dev = %x, inode = %x\n", remotepid, from_port, dev, inode));
 ****************************************************************************/
 BOOL oplock_break(uint32 dev, uint32 inode, struct timeval *tval)
 {
+  extern struct current_user current_user;
   extern int Client;
   char *inbuf = NULL;
   char *outbuf = NULL;
@@ -2873,6 +3036,9 @@ BOOL oplock_break(uint32 dev, uint32 inode, struct timeval *tval)
   int fnum;
   time_t start_time;
   BOOL shutdown_server = False;
+  int saved_cnum;
+  int saved_vuid;
+  pstring saved_dir; 
 
   DEBUG(3,("%s oplock_break: called for dev = %x, inode = %x. Current \
 global_oplocks_open = %d\n", timestring(), dev, inode, global_oplocks_open));
@@ -2978,6 +3144,15 @@ allowing break to succeed.\n", timestring(), dev, inode, fnum));
 
   start_time = time(NULL);
 
+  /*
+   * Save the information we need to re-become the
+   * user, then unbecome the user whilst we're doing this.
+   */
+  saved_cnum = fsp->cnum;
+  saved_vuid = current_user.vuid;
+  GetWd(saved_dir);
+  unbecome_user();
+
   while(OPEN_FNUM(fnum) && fsp->granted_oplock)
   {
     if(receive_smb(Client,inbuf,OPLOCK_BREAK_TIMEOUT * 1000) == False)
@@ -3029,6 +3204,21 @@ inode = %x).\n", timestring(), fsp->name, fnum, dev, inode));
     }
   }
 
+  /*
+   * Go back to being the user who requested the oplock
+   * break.
+   */
+  if(!become_user(&Connections[saved_cnum], saved_cnum, saved_vuid))
+  {
+    DEBUG(0,("%s oplock_break: unable to re-become user ! Shutting down server\n",
+          timestring()));
+    close_sockets();
+    close(oplock_sock);
+    exit_server("unable to re-become user");
+  }
+  /* Including the directory. */
+  ChDir(saved_dir);
+
   /* Free the buffers we've been using to recurse. */
   free(inbuf);
   free(outbuf);
@@ -4950,6 +5140,52 @@ static void process(void)
         DEBUG(2,("%s Closing idle connection 2\n",timestring()));
         return;
       }
+
+      if(global_machine_pasword_needs_changing)
+      {
+        unsigned char trust_passwd_hash[16];
+        time_t lct;
+        pstring remote_machine_list;
+
+        /*
+         * We're in domain level security, and the code that
+         * read the machine password flagged that the machine
+         * password needs changing.
+         */
+
+        /*
+         * First, open the machine password file with an exclusive lock.
+         */
+
+        if(!trust_password_lock( global_myworkgroup, global_myname, True)) {
+          DEBUG(0,("process: unable to open the machine account password file for \
+machine %s in domain %s.\n", global_myname, global_myworkgroup ));
+          continue;
+        }
+
+        if(!get_trust_account_password( trust_passwd_hash, &lct)) {
+          DEBUG(0,("process: unable to read the machine account password for \
+machine %s in domain %s.\n", global_myname, global_myworkgroup ));
+          trust_password_unlock();
+          continue;
+        }
+
+        /*
+         * Make sure someone else hasn't already done this.
+         */
+
+        if(t < lct + lp_machine_password_timeout()) {
+          trust_password_unlock();
+          global_machine_pasword_needs_changing = False;
+          continue;
+        }
+
+        pstrcpy(remote_machine_list, lp_passwordserver());
+
+        change_trust_account_password( global_myworkgroup, remote_machine_list);
+        trust_password_unlock();
+        global_machine_pasword_needs_changing = False;
+      }
     }
 
     if(got_smb)