fixed warnings (and potential errors) due to integer overflow when
[samba.git] / source / lib / util.c
index f1fae9155c8280d7cea93bbf18392cf9f0039085..e5ddda18916b096f344e15ed1956dce1833b7eea 100644 (file)
@@ -100,30 +100,6 @@ char *tmpdir(void)
   return "/tmp";
 }
 
-
-
-/****************************************************************************
-prompte a dptr (to make it recently used)
-****************************************************************************/
-static void array_promote(char *array,int elsize,int element)
-{
-  char *p;
-  if (element == 0)
-    return;
-
-  p = (char *)malloc(elsize);
-
-  if (!p)
-    {
-      DEBUG(5,("Ahh! Can't malloc\n"));
-      return;
-    }
-  memcpy(p,array + element * elsize, elsize);
-  memmove(array + elsize,array,elsize*element);
-  memcpy(array,p,elsize);
-  free(p);
-}
-
 /****************************************************************************
 determine whether we are in the specified group
 ****************************************************************************/
@@ -142,6 +118,21 @@ BOOL in_group(gid_t group, gid_t current_gid, int ngroups, gid_t *groups)
 }
 
 
+/****************************************************************************
+gets either a hex number (0xNNN) or decimal integer (NNN).
+****************************************************************************/
+uint32 get_number(const char *tmp)
+{
+       if (strnequal(tmp, "0x", 2))
+       {
+               return strtoul(tmp, (char**)NULL, 16);
+       }
+       else
+       {
+               return strtoul(tmp, (char**)NULL, 10);
+       }
+}
+
 /****************************************************************************
 like atoi but gets the value up to the separater character
 ****************************************************************************/
@@ -153,7 +144,12 @@ char *Atoic(char *p, int *n, char *c)
                return NULL;
        }
 
-       (*n) = atoi(p);
+       (*n) = (int)get_number(p);
+
+       if (strnequal(p, "0x", 2))
+       {
+               p += 2;
+       }
 
        while ((*p) && isdigit(*p))
        {
@@ -169,6 +165,19 @@ char *Atoic(char *p, int *n, char *c)
        return p;
 }
 
+uint32 *add_num_to_list(uint32 **num, int *count, int val)
+{
+       (*num) = Realloc((*num), ((*count)+1) * sizeof(uint32));
+       if ((*num) == NULL)
+       {
+               return NULL;
+       }
+       (*num)[(*count)] = val;
+       (*count)++;
+
+       return (*num);
+}
+
 /*************************************************************************
  reads a list of numbers
  *************************************************************************/
@@ -186,13 +195,10 @@ char *get_numlist(char *p, uint32 **num, int *count)
 
        while ((p = Atoic(p, &val, ":,")) != NULL && (*p) != ':')
        {
-               (*num) = Realloc((*num), ((*count)+1) * sizeof(uint32));
-               if ((*num) == NULL)
+               if (add_num_to_list(num, count, val) == NULL)
                {
                        return NULL;
                }
-               (*num)[(*count)] = val;
-               (*count)++;
                p++;
        }
 
@@ -354,12 +360,27 @@ BOOL file_exist(char *fname,SMB_STRUCT_STAT *sbuf)
   SMB_STRUCT_STAT st;
   if (!sbuf) sbuf = &st;
   
-  if (dos_stat(fname,sbuf) != 0) 
+  if (sys_stat(fname,sbuf) != 0) 
     return(False);
 
   return(S_ISREG(sbuf->st_mode));
 }
 
+/*******************************************************************
+  rename a unix file
+********************************************************************/
+int file_rename(char *from, char *to)
+{
+       int rcode = rename (from, to);
+
+       if (errno == EXDEV) 
+       {
+               /* Rename across filesystems needed. */
+               rcode = copy_reg (from, to);        
+       }
+       return rcode;
+}
+
 /*******************************************************************
 check a files mod time
 ********************************************************************/
@@ -367,7 +388,7 @@ time_t file_modtime(char *fname)
 {
   SMB_STRUCT_STAT st;
   
-  if (dos_stat(fname,&st) != 0) 
+  if (sys_stat(fname,&st) != 0) 
     return(0);
 
   return(st.st_mtime);
@@ -383,7 +404,7 @@ BOOL directory_exist(char *dname,SMB_STRUCT_STAT *st)
 
   if (!st) st = &st2;
 
-  if (dos_stat(dname,st) != 0) 
+  if (sys_stat(dname,st) != 0) 
     return(False);
 
   ret = S_ISDIR(st->st_mode);
@@ -399,7 +420,8 @@ SMB_OFF_T file_size(char *file_name)
 {
   SMB_STRUCT_STAT buf;
   buf.st_size = 0;
-  dos_stat(file_name,&buf);
+  if(sys_stat(file_name,&buf) != 0)
+    return (SMB_OFF_T)-1;
   return(buf.st_size);
 }
 
@@ -422,8 +444,6 @@ char *attrib_string(uint16 mode)
   return(attrstr);
 }
 
-
-
 /****************************************************************************
   make a file into unix format
 ****************************************************************************/
@@ -638,141 +658,6 @@ void unix_clean_name(char *s)
   trim_string(s,NULL,"/..");
 }
 
-
-/*******************************************************************
-a wrapper for the normal chdir() function
-********************************************************************/
-int ChDir(char *path)
-{
-  int res;
-  static pstring LastDir="";
-
-  if (strcsequal(path,".")) return(0);
-
-  if (*path == '/' && strcsequal(LastDir,path)) return(0);
-  DEBUG(3,("chdir to %s\n",path));
-  res = dos_chdir(path);
-  if (!res)
-    pstrcpy(LastDir,path);
-  return(res);
-}
-
-/* number of list structures for a caching GetWd function. */
-#define MAX_GETWDCACHE (50)
-
-struct
-{
-  SMB_DEV_T dev; /* These *must* be compatible with the types returned in a stat() call. */
-  SMB_INO_T inode; /* These *must* be compatible with the types returned in a stat() call. */
-  char *text; /* The pathname in DOS format. */
-  BOOL valid;
-} ino_list[MAX_GETWDCACHE];
-
-BOOL use_getwd_cache=True;
-
-/*******************************************************************
-  return the absolute current directory path - given a UNIX pathname.
-  Note that this path is returned in DOS format, not UNIX
-  format.
-********************************************************************/
-char *GetWd(char *str)
-{
-  pstring s;
-  static BOOL getwd_cache_init = False;
-  SMB_STRUCT_STAT st, st2;
-  int i;
-
-  *s = 0;
-
-  if (!use_getwd_cache)
-    return(dos_getwd(str));
-
-  /* init the cache */
-  if (!getwd_cache_init)
-  {
-    getwd_cache_init = True;
-    for (i=0;i<MAX_GETWDCACHE;i++)
-    {
-      string_init(&ino_list[i].text,"");
-      ino_list[i].valid = False;
-    }
-  }
-
-  /*  Get the inode of the current directory, if this doesn't work we're
-      in trouble :-) */
-
-  if (dos_stat(".",&st) == -1) 
-  {
-    DEBUG(0,("Very strange, couldn't stat \".\"\n"));
-    return(dos_getwd(str));
-  }
-
-
-  for (i=0; i<MAX_GETWDCACHE; i++)
-    if (ino_list[i].valid)
-    {
-
-      /*  If we have found an entry with a matching inode and dev number
-          then find the inode number for the directory in the cached string.
-          If this agrees with that returned by the stat for the current
-          directory then all is o.k. (but make sure it is a directory all
-          the same...) */
-      
-      if (st.st_ino == ino_list[i].inode &&
-          st.st_dev == ino_list[i].dev)
-      {
-        if (dos_stat(ino_list[i].text,&st2) == 0)
-        {
-          if (st.st_ino == st2.st_ino &&
-              st.st_dev == st2.st_dev &&
-              (st2.st_mode & S_IFMT) == S_IFDIR)
-          {
-            pstrcpy (str, ino_list[i].text);
-
-            /* promote it for future use */
-            array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
-            return (str);
-          }
-          else
-          {
-            /*  If the inode is different then something's changed, 
-                scrub the entry and start from scratch. */
-            ino_list[i].valid = False;
-          }
-        }
-      }
-    }
-
-
-  /*  We don't have the information to hand so rely on traditional methods.
-      The very slow getcwd, which spawns a process on some systems, or the
-      not quite so bad getwd. */
-
-  if (!dos_getwd(s))
-  {
-    DEBUG(0,("Getwd failed, errno %s\n",strerror(errno)));
-    return (NULL);
-  }
-
-  pstrcpy(str,s);
-
-  DEBUG(5,("GetWd %s, inode %d, dev %x\n",s,(int)st.st_ino,(int)st.st_dev));
-
-  /* add it to the cache */
-  i = MAX_GETWDCACHE - 1;
-  string_set(&ino_list[i].text,s);
-  ino_list[i].dev = st.st_dev;
-  ino_list[i].inode = st.st_ino;
-  ino_list[i].valid = True;
-
-  /* put it at the top of the list */
-  array_promote((char *)&ino_list[0],sizeof(ino_list[0]),i);
-
-  return (str);
-}
-
-
-
 /*******************************************************************
 reduce a file name, removing .. elements and checking that 
 it is below dir in the heirachy. This uses GetWd() and so must be run
@@ -780,6 +665,7 @@ on the system that has the referenced file system.
 
 widelinks are allowed if widelinks is true
 ********************************************************************/
+
 BOOL reduce_name(char *s,char *dir,BOOL widelinks)
 {
 #ifndef REDUCE_PATHS
@@ -821,22 +707,22 @@ BOOL reduce_name(char *s,char *dir,BOOL widelinks)
   if (!p)
     return(True);
 
-  if (!GetWd(wd))
+  if (!dos_GetWd(wd))
     {
       DEBUG(0,("couldn't getwd for %s %s\n",s,dir));
       return(False);
     }
 
-  if (ChDir(dir) != 0)
+  if (dos_ChDir(dir) != 0)
     {
       DEBUG(0,("couldn't chdir to %s\n",dir));
       return(False);
     }
 
-  if (!GetWd(dir2))
+  if (!dos_GetWd(dir2))
     {
       DEBUG(0,("couldn't getwd for %s\n",dir));
-      ChDir(wd);
+      dos_ChDir(wd);
       return(False);
     }
 
@@ -850,16 +736,16 @@ BOOL reduce_name(char *s,char *dir,BOOL widelinks)
          *p = '/';
       }
 
-  if (ChDir(base_name) != 0)
+  if (dos_ChDir(base_name) != 0)
     {
-      ChDir(wd);
+      dos_ChDir(wd);
       DEBUG(3,("couldn't chdir for %s %s basename=%s\n",s,dir,base_name));
       return(False);
     }
 
-  if (!GetWd(newname))
+  if (!dos_GetWd(newname))
     {
-      ChDir(wd);
+      dos_ChDir(wd);
       DEBUG(2,("couldn't get wd for %s %s\n",s,dir2));
       return(False);
     }
@@ -871,13 +757,13 @@ BOOL reduce_name(char *s,char *dir,BOOL widelinks)
     }
 
   {
-    int l = strlen(dir2);    
+    size_t l = strlen(dir2);    
     if (dir2[l-1] == '/')
       l--;
 
     if (strncmp(newname,dir2,l) != 0)
       {
-       ChDir(wd);
+       dos_ChDir(wd);
        DEBUG(2,("Bad access attempt? s=%s dir=%s newname=%s l=%d\n",s,dir2,newname,l));
        return(False);
       }
@@ -893,7 +779,7 @@ BOOL reduce_name(char *s,char *dir,BOOL widelinks)
       pstrcpy(s,newname);
   }
 
-  ChDir(wd);
+  dos_ChDir(wd);
 
   if (strlen(s) == 0)
     pstrcpy(s,"./");
@@ -1039,8 +925,8 @@ void close_low_fds(void)
   /* try and use up these file descriptors, so silly
      library routines writing to stdout etc won't cause havoc */
   for (i=0;i<3;i++) {
-    fd = open("/dev/null",O_RDWR,0);
-    if (fd < 0) fd = open("/dev/null",O_WRONLY,0);
+    fd = sys_open("/dev/null",O_RDWR,0);
+    if (fd < 0) fd = sys_open("/dev/null",O_WRONLY,0);
     if (fd < 0) {
       DEBUG(0,("Can't open /dev/null\n"));
       return;
@@ -1685,7 +1571,7 @@ void become_daemon(void)
        setsid();
 #elif defined(TIOCNOTTY)
        {
-               int i = open("/dev/tty", O_RDWR);
+               int i = sys_open("/dev/tty", O_RDWR, 0);
                if (i != -1) {
                        ioctl(i, (int) TIOCNOTTY, (char *)0);      
                        close(i);
@@ -2314,33 +2200,18 @@ void standard_sub(connection_struct *conn,char *str)
 {
        char *p, *s, *home;
 
-       for (s=str; (p=strchr(s, '%'));s=p) {
-               switch (*(p+1)) {
-               case 'H': 
-                       if ((home = get_home_dir(conn->user))) {
-                               string_sub(p,"%H",home);
-                       } else {
-                               p += 2;
-                       }
-                       break;
-                       
-               case 'P': 
-                       string_sub(p,"%P",conn->connectpath); 
-                       break;
-                       
-               case 'S': 
-                       string_sub(p,"%S",
-                                  lp_servicename(SNUM(conn))); 
-                       break;
-                       
-               case 'g': 
-                       string_sub(p,"%g",
-                                  gidtoname(conn->gid)); 
-                       break;
-               case 'u': 
-                       string_sub(p,"%u",conn->user); 
-                       break;
-                       
+       for (s=str; (p=strchr(s, '%'));s=p)
+       {
+               switch (*(p+1))
+               {
+                       case 'H': 
+                               if ((home = get_home_dir(conn->user)) != NULL) {
+                                       string_sub(p,"%H",home);
+                               } else {
+                                       p += 2;
+                               }
+                               break;
+                               
                        /* Patch from jkf@soton.ac.uk Left the %N (NIS
                         * server name) in standard_sub_basic as it is
                         * a feature for logon servers, hence uses the
@@ -2348,17 +2219,14 @@ void standard_sub(connection_struct *conn,char *str)
                         * here as it is used instead of the default
                         * "path =" string in [homes] and so needs the
                         * service name, not the username.  */
-               case 'p': 
-                       string_sub(p,"%p",
-                                  automount_path(lp_servicename(SNUM(conn)))); 
-                       break;
-               case '\0': 
-                       p++; 
-                       break; /* don't run off the end of the string 
-                               */
-                       
-               default: p+=2; 
-                       break;
+                       case 'p': string_sub(p,"%p", automount_path(lp_servicename(SNUM(conn)))); break;
+                       case 'P': string_sub(p,"%P",conn->connectpath); break; 
+                       case 'S': string_sub(p,"%S", lp_servicename(SNUM(conn))); break; 
+                       case 'g': string_sub(p,"%g", gidtoname(conn->gid)); break;
+                       case 'u': string_sub(p,"%u", conn->user); break;
+                               
+                       case '\0': p++; break; /* don't run off the end of the string */ 
+                       default  : p+=2; break;
                }
        }
        
@@ -2386,7 +2254,7 @@ BOOL same_net(struct in_addr ip1,struct in_addr ip2,struct in_addr mask)
 a wrapper for gethostbyname() that tries with all lower and all upper case 
 if the initial name fails
 ****************************************************************************/
-struct hostent *Get_Hostbyname(char *name)
+struct hostent *Get_Hostbyname(const char *name)
 {
   char *name2 = strdup(name);
   struct hostent *ret;
@@ -2453,6 +2321,55 @@ BOOL process_exists(int pid)
 }
 
 
+/****************************************************************************
+Setup the groups a user belongs to.
+****************************************************************************/
+int get_unixgroups(char *user, uid_t uid, gid_t gid, int *p_ngroups, gid_t **p_groups)
+{
+       int i,ngroups;
+       gid_t grp = 0;
+       gid_t *groups = NULL;
+
+       if (-1 == initgroups(user,gid))
+       {
+               if (getuid() == 0)
+               {
+                       DEBUG(0,("Unable to initgroups!\n"));
+                       if (gid < 0 || gid > 16000 || uid < 0 || uid > 16000)
+                       {
+                               DEBUG(0,("This is probably a problem with the account %s\n", user));
+                       }
+               }
+               return -1;
+       }
+
+       ngroups = sys_getgroups(0,&grp);
+       if (ngroups <= 0)
+       {
+               ngroups = 32;
+       }
+
+       if((groups = (gid_t *)malloc(sizeof(gid_t)*ngroups)) == NULL)
+       {
+               DEBUG(0,("get_unixgroups malloc fail !\n"));
+               return -1;
+       }
+
+       ngroups = sys_getgroups(ngroups,groups);
+
+       (*p_ngroups) = ngroups;
+       (*p_groups) = groups;
+
+       DEBUG( 3, ( "%s is in %d groups: ", user, ngroups ) );
+       for (i = 0; i < ngroups; i++ )
+       {
+               DEBUG( 3, ( "%s%d", (i ? ", " : ""), (int)groups[i] ) );
+       }
+       DEBUG( 3, ( "\n" ) );
+
+       return 0;
+}
+
 /*******************************************************************
 turn a uid into a user name
 ********************************************************************/
@@ -2479,14 +2396,49 @@ char *gidtoname(gid_t gid)
        return(name);
 }
 
+/*******************************************************************
+turn a group name into a gid
+********************************************************************/
+
+BOOL nametogid(const char *name, gid_t *gid)
+{
+       struct group *grp = getgrnam(name);
+       if (grp)
+       {
+               *gid = grp->gr_gid;
+               return True;
+       }
+       else if (isdigit(name[0]))
+       {
+               *gid = (gid_t)get_number(name);
+               return True;
+       }
+       else
+       {
+               return False;
+       }
+}
+
 /*******************************************************************
 turn a user name into a uid
 ********************************************************************/
-uid_t nametouid(const char *name)
+BOOL nametouid(const char *name, uid_t *uid)
 {
-       struct passwd *pass = getpwnam(name);
-       if (pass) return(pass->pw_uid);
-       return (uid_t)-1;
+       struct passwd *pass = Get_Pwnam(name, False);
+       if (pass)
+       {
+               *uid = pass->pw_uid;
+               return True;
+       }
+       else if (isdigit(name[0]))
+       {
+               *uid = (uid_t)get_number(name);
+               return True;
+       }
+       else
+       {
+               return False;
+       }
 }
 
 /*******************************************************************
@@ -2507,7 +2459,7 @@ void smb_panic(char *why)
 /*******************************************************************
 a readdir wrapper which just returns the file name
 ********************************************************************/
-char *readdirname(void *p)
+char *readdirname(DIR *p)
 {
        struct dirent *ptr;
        char *dname;
@@ -2531,7 +2483,6 @@ char *readdirname(void *p)
        {
                static pstring buf;
                memcpy(buf, dname, NAMLEN(ptr)+1);
-               unix_to_dos(buf, True);
                dname = buf;
        }
 
@@ -2715,8 +2666,8 @@ BOOL fcntl_lock(int fd, int op, SMB_OFF_T offset, SMB_OFF_T count, int type)
   int ret;
 
   if(lp_ole_locking_compat()) {
-    SMB_OFF_T mask = ((SMB_OFF_T)0xC) << (SMB_OFF_T_BITS-4);
     SMB_OFF_T mask2= ((SMB_OFF_T)0x3) << (SMB_OFF_T_BITS-4);
+    SMB_OFF_T mask = (mask2<<2);
 
     /* make sure the count is reasonable, we might kill the lockd otherwise */
     count &= ~mask;
@@ -2728,7 +2679,8 @@ BOOL fcntl_lock(int fd, int op, SMB_OFF_T offset, SMB_OFF_T count, int type)
     if ((offset & mask) != 0)
       offset = (offset & ~mask) | (((offset & mask) >> 2) & mask2);
   } else {
-    SMB_OFF_T mask = ((SMB_OFF_T)0x8) << (SMB_OFF_T_BITS-4);
+    SMB_OFF_T mask2 = ((SMB_OFF_T)0x4) << (SMB_OFF_T_BITS-4);
+    SMB_OFF_T mask = (mask2<<1);
     SMB_OFF_T neg_mask = ~mask;
 
     /* interpret negative counts as large numbers */
@@ -3095,3 +3047,56 @@ BOOL reg_split_key(char *full_keyname, uint32 *reg_type, char *key_name)
 
        return True;
 }
+
+/****************************************************************************
+  become the specified uid - permanently !
+****************************************************************************/
+BOOL become_user_permanently(uid_t uid, gid_t gid)
+{
+       /* now completely lose our privilages. This is a fairly paranoid
+          way of doing it, but it does work on all systems that I know of */
+
+#ifdef HAVE_SETRESUID
+       /*
+        * Firstly ensure all our uids are set to root.
+        */
+       setresgid(0,0,0);
+       setresuid(0,0,0);
+
+       /*
+        * Now ensure we change all our gids.
+        */
+       setresgid(gid,gid,gid);
+       
+       /*
+        * Now ensure all the uids are the user.
+        */
+       setresuid(uid,uid,uid);
+#else
+       /*
+        * Firstly ensure all our uids are set to root.
+        */
+       setuid(0);
+       seteuid(0);
+       
+       /*
+        * Now ensure we change all our gids.
+        */
+       setgid(gid);
+       setegid(gid);
+       
+       /*
+        * Now ensure all the uids are the user.
+        */
+       setuid(uid);
+       seteuid(uid);
+#endif
+       
+       if (getuid() != uid || geteuid() != uid ||
+           getgid() != gid || getegid() != gid) {
+               /* We failed to lose our privilages. */
+               return False;
+       }
+       
+       return(True);
+}