--- shadow-980403_work/src/useradd.c Fri Jun 25 15:59:53 1999 +++ shadow-980403_work/src/useradd.c.noreuse Mon Jun 28 17:38:22 1999 @@ -62,6 +62,10 @@ #define NEW_USER_FILE "/etc/default/nuaddXXXXXX" #endif +#ifndef NEXT_UID_FILE +#define NEXT_UID_FILE "/etc/nextuid" +#endif + /* * Needed for MkLinux DR1/2/2.1 - J. */ @@ -182,6 +186,8 @@ #define E_NAME_IN_USE 9 /* username already in use */ #define E_GRP_UPDATE 10 /* can't update group file */ #define E_HOMEDIR 12 /* can't create home directory */ +#define E_NXUID_FILE 14 /* can't update the next-uid file */ +#define E_SYSTEM 99 /* malloc failure etc. */ #ifdef SVR4 #define DGROUP "defgroup=" @@ -934,12 +940,42 @@ * uniqueness. */ + static void find_new_uid(void) { + int fd1; + unsigned int scan_uid; + const int buflen = 20; + char buf[buflen]; + const int nbitbytes = 1<<13; + int nbits, nints; + int * pbits = 0; + int shift1, mask1; /* used to address bits */ + int ii, jj; + uid_t xbase, xtop, x1; + uid_t nuids, uoff1, uoff2; + uid_t ulo1, ulo2, uhi1, uhi2; + uid_t uid1; + int bitadr, bbceil; + const struct passwd *pwd; uid_t uid_min, uid_max; + if (! uflg) { + /* Initialize stuff for bitmap */ + nbits = nbitbytes << 3; + nints = nbitbytes / sizeof(int); + pbits = (int*) malloc(nbitbytes); + if (!pbits) { + fprintf (stderr, "%s: memory allocation failed\n", Prog, user_name); + exit(E_SYSTEM); + } + for (ii=sizeof(int), shift1=2; ii; ii>>=1) shift1++; + mask1 = (1 << shift1) - 1; + } + + if (!rflg) { uid_min = getdef_num("UID_MIN", 500); uid_max = getdef_num("UID_MAX", 60000); @@ -949,67 +985,149 @@ } /* - * Start with some UID value if the user didn't provide us with - * one already. + * Grab the nextuid value and start here, if the user didn't + * provide us with a uid already. */ - if (! uflg) - user_id = uid_min; + if (! uflg) { + fd1 = open(NEXT_UID_FILE, O_RDONLY); + if (fd1 == -1){ + user_id = 0; + } else { + ii = read(fd1, buf, buflen-1); + buf[ii] = '\0'; + ii = sscanf(buf, "%u", &scan_uid); + user_id = (ii == 1) ? scan_uid : 0; + close(fd1); + } + if ( user_id < uid_min || user_id > uid_max ) + user_id = uid_min; + } /* - * Search the entire password file, either looking for this - * UID (if the user specified one with -u) or looking for the - * largest unused value. - */ + * If the bit buffer is too small to cover the entire UID range, + * segment the range. Normally an available UID will be found in the + * first segment. Otherwise search segments as needed. The segments + * begin at user_id and cover the range circularly. The segment that + * covers the top of the range wraps and covers the bottom too. If the + * bit buffer is large enough then the first segment, by wrapping, + * will always cover the entire range regardless of user_id. -- rwh + */ + + nuids = uid_max - uid_min + 1; + for(xbase=0; xbase < nuids ;xbase += nbits){ + if (! uflg ) { + + xtop = xbase + nbits -1; + if (xtop >= nuids) xtop = nuids-1; + /* This segment is x = xbase..xtop */ + ulo2 = 1; + uhi2 = 0; + if (xbase > uid_max - user_id) { + /* The segment is entirely past the wrap point */ + ulo1 = xbase - nuids + user_id; + uhi1 = xtop - nuids + user_id; + } else if (xtop <= uid_max - user_id) { + /* The segment is entirely before the wrap point */ + ulo1 = xbase + user_id; + uhi1 = xtop + user_id; + } else { + /* The segment crosses the wrap point */ + ulo1 = xbase + user_id; + uhi1 = uid_max; + ulo2 = uid_min; + uhi2 = xtop - nuids + user_id; + } + uoff1 = ulo1; + uoff2 = xbase - nuids + user_id; + } + + /* Clear the bits buffer. */ + for (ii=0; ii < nints; ii++) pbits[ii] = 0; + + /* + * Search the entire password file, either looking for this + * UID (if the user specified one with -u) or building a set + * of used UIDs in the bit buffer. + */ #ifdef NO_GETPWENT - pw_rewind(); - while ((pwd = pw_next())) { + pw_rewind(); + while ((pwd = pw_next())) { #else /* using getpwent() we can check against NIS users etc. */ - setpwent(); - while ((pwd = getpwent())) { + setpwent(); + while ((pwd = getpwent())) { #endif - if (strcmp(user_name, pwd->pw_name) == 0) { - fprintf (stderr, "%s: name %s is not unique\n", - Prog, user_name); - exit(E_NAME_IN_USE); - } - if (uflg && user_id == pwd->pw_uid) { - fprintf (stderr, "%s: uid %d is not unique\n", - Prog, (int) user_id); - exit(E_UID_IN_USE); + if (xbase==0 && strcmp(user_name, pwd->pw_name) == 0) { + fprintf (stderr, "%s: name %s is not unique\n", + Prog, user_name); + exit(E_NAME_IN_USE); + } + if (uflg) { + if (user_id == pwd->pw_uid) { + fprintf (stderr, "%s: uid %d is not unique\n", + Prog, (int) user_id); + exit(E_UID_IN_USE); + } + } else { + + /* + * If this uid is in the current segment, set its bit. + */ + + uid1 = pwd->pw_uid; + if (uid1 >= ulo1 && uid1 <= uhi1) { + bitadr = uid1 - uoff1; + pbits[bitadr>>shift1] |= 1 << (bitadr & mask1); + } + else if (uid1 >= ulo2 && uid1 <= uhi2) { + bitadr = uid1 - uoff2; + pbits[bitadr>>shift1] |= 1 << (bitadr & mask1); + } + } } - if (! uflg && pwd->pw_uid >= user_id) { - if (pwd->pw_uid > uid_max) - continue; - user_id = pwd->pw_uid + 1; + + if(uflg) return; + + /* + * Search the bit buffer for an available uid. + */ + + for (ii = 0; ii < nints; ii++) { + if (~pbits[ii]) { + bitadr = ii << shift1; + for (jj = pbits[ii]; jj & 1; jj >>= 1) bitadr++ ; + if (bitadr > xtop - xbase) + break; + + /* We have an empty bit -- an available uid */ + + x1= bitadr + xbase; + user_id += x1 - (x1 > uid_max - user_id ? nuids : 0); + uid1 = user_id >= uid_max ? uid_min : user_id + 1; + + fd1 = open(NEXT_UID_FILE, O_WRONLY | O_CREAT | O_TRUNC, 00644); + if (fd1 != -1) { + jj = sprintf(buf, "%d\n", uid1); + jj -= write(fd1, buf, jj); + if (!close(fd1) && !jj) + return; + } + fprintf (stderr, "%s: Could not write file %s\n", + Prog, NEXT_UID_FILE); + exit(E_NXUID_FILE); + } } - } + /* + * Found no empty bits. Do another segment of uid range. + */ + } + /* - * If a user with uid equal to UID_MAX exists, the above algorithm - * will give us UID_MAX+1 even if not unique. Search for the first - * free uid starting with UID_MIN (it's O(n*n) but can be avoided - * by not having users with uid equal to UID_MAX). --marekm + * All uid's in the range uid_min to uid_max are taken. */ - if (!uflg && user_id == uid_max + 1) { - for (user_id = uid_min; user_id < uid_max; user_id++) { -#ifdef NO_GETPWENT - pw_rewind(); - while ((pwd = pw_next()) && pwd->pw_uid != user_id) - ; - if (!pwd) - break; -#else - if (!getpwuid(user_id)) - break; -#endif - } - if (user_id == uid_max) { - fprintf(stderr, "%s: can't get unique uid\n", - Prog); - exit(E_UID_IN_USE); - } - } + fprintf(stderr, "%s: can't get unique uid\n", Prog); + exit(E_UID_IN_USE); } /*