int main (int argc, char **argv) { char buf[BUFSIZ]; char *fields[8]; int nfields; char *cp; const struct passwd *pw; struct passwd newpw; int errors = 0; int line = 0; uid_t uid; gid_t gid; #ifdef USE_PAM int *lines = NULL; char **usernames = NULL; char **passwords = NULL; unsigned int nusers = 0; #endif /* USE_PAM */ Prog = Basename (argv[0]); (void) setlocale (LC_ALL, ""); (void) bindtextdomain (PACKAGE, LOCALEDIR); (void) textdomain (PACKAGE); /* FIXME: will not work with an input file */ process_root_flag ("-R", argc, argv); OPENLOG ("newusers"); process_flags (argc, argv); check_perms (); is_shadow = spw_file_present (); #ifdef SHADOWGRP is_shadow_grp = sgr_file_present (); #endif #ifdef ENABLE_SUBIDS is_sub_uid = sub_uid_file_present () && !rflg; is_sub_gid = sub_gid_file_present () && !rflg; #endif /* ENABLE_SUBIDS */ open_files (); /* * Read each line. The line has the same format as a password file * entry, except that certain fields are not constrained to be * numerical values. If a group ID is entered which does not already * exist, an attempt is made to allocate the same group ID as the * numerical user ID. Should that fail, the next available group ID * over 100 is allocated. The pw_gid field will be updated with that * value. */ while (fgets (buf, (int) sizeof buf, stdin) != (char *) 0) { line++; cp = strrchr (buf, '\n'); if (NULL != cp) { *cp = '\0'; } else { if (feof (stdin) == 0) { fprintf (stderr, _("%s: line %d: line too long\n"), Prog, line); errors++; continue; } } /* * Break the string into fields and screw around with them. * There MUST be 7 colon separated fields, although the * values aren't that particular. */ for (cp = buf, nfields = 0; nfields < 7; nfields++) { fields[nfields] = cp; cp = strchr (cp, ':'); if (NULL != cp) { *cp = '\0'; cp++; } else { break; } } if (nfields != 6) { fprintf (stderr, _("%s: line %d: invalid line\n"), Prog, line); errors++; continue; } /* * First check if we have to create or update an user */ pw = pw_locate (fields[0]); /* local, no need for xgetpwnam */ if ( (NULL == pw) && (getpwnam (fields[0]) != NULL)) { fprintf (stderr, _("%s: cannot update the entry of user %s (not in the passwd database)\n"), Prog, fields[0]); errors++; continue; } if ( (NULL == pw) && (get_user_id (fields[2], &uid) != 0)) { fprintf (stderr, _("%s: line %d: can't create user\n"), Prog, line); errors++; continue; } /* * Processed is the group name. A new group will be * created if the group name is non-numeric and does not * already exist. If the group name is a number (which is not * an existing GID), a group with the same name as the user * will be created, with the given GID. The given or created * group will be the primary group of the user. If * there is no named group to be a member of, the UID will * be figured out and that value will be a candidate for a * new group, if that group ID exists, a whole new group ID * will be made up. */ if ( (NULL == pw) && (add_group (fields[0], fields[3], &gid, uid) != 0)) { fprintf (stderr, _("%s: line %d: can't create group\n"), Prog, line); errors++; continue; } /* * Now we work on the user ID. It has to be specified either * as a numerical value, or left blank. If it is a numerical * value, that value will be used, otherwise the next * available user ID is computed and used. After this there * will at least be a (struct passwd) for the user. */ if ( (NULL == pw) && (add_user (fields[0], uid, gid) != 0)) { fprintf (stderr, _("%s: line %d: can't create user\n"), Prog, line); errors++; continue; } /* * The password, gecos field, directory, and shell fields * all come next. */ pw = pw_locate (fields[0]); if (NULL == pw) { fprintf (stderr, _("%s: line %d: user '%s' does not exist in %s\n"), Prog, line, fields[0], pw_dbname ()); errors++; continue; } newpw = *pw; #ifdef USE_PAM /* keep the list of user/password for later update by PAM */ nusers++; lines = realloc (lines, sizeof (lines[0]) * nusers); usernames = realloc (usernames, sizeof (usernames[0]) * nusers); passwords = realloc (passwords, sizeof (passwords[0]) * nusers); lines[nusers-1] = line; usernames[nusers-1] = strdup (fields[0]); passwords[nusers-1] = strdup (fields[1]); #endif /* USE_PAM */ if (add_passwd (&newpw, fields[1]) != 0) { fprintf (stderr, _("%s: line %d: can't update password\n"), Prog, line); errors++; continue; } if ('\0' != fields[4][0]) { newpw.pw_gecos = fields[4]; } if ('\0' != fields[5][0]) { newpw.pw_dir = fields[5]; } if ('\0' != fields[6][0]) { newpw.pw_shell = fields[6]; } if ( ('\0' != fields[5][0]) && (access (newpw.pw_dir, F_OK) != 0)) { /* FIXME: should check for directory */ mode_t msk = 0777 & ~getdef_num ("UMASK", GETDEF_DEFAULT_UMASK); if (mkdir (newpw.pw_dir, msk) != 0) { fprintf (stderr, _("%s: line %d: mkdir %s failed: %s\n"), Prog, line, newpw.pw_dir, strerror (errno)); } else if (chown (newpw.pw_dir, newpw.pw_uid, newpw.pw_gid) != 0) { fprintf (stderr, _("%s: line %d: chown %s failed: %s\n"), Prog, line, newpw.pw_dir, strerror (errno)); } } /* * Update the password entry with the new changes made. */ if (pw_update (&newpw) == 0) { fprintf (stderr, _("%s: line %d: can't update entry\n"), Prog, line); errors++; continue; } #ifdef ENABLE_SUBIDS /* * Add subordinate uids if the user does not have them. */ if (is_sub_uid && !sub_uid_assigned(fields[0])) { uid_t sub_uid_start = 0; unsigned long sub_uid_count = 0; if (find_new_sub_uids(fields[0], &sub_uid_start, &sub_uid_count) == 0) { if (sub_uid_add(fields[0], sub_uid_start, sub_uid_count) == 0) { fprintf (stderr, _("%s: failed to prepare new %s entry\n"), Prog, sub_uid_dbname ()); } } else { fprintf (stderr, _("%s: can't find subordinate user range\n"), Prog); errors++; } } /* * Add subordinate gids if the user does not have them. */ if (is_sub_gid && !sub_gid_assigned(fields[0])) { gid_t sub_gid_start = 0; unsigned long sub_gid_count = 0; if (find_new_sub_gids(fields[0], &sub_gid_start, &sub_gid_count) == 0) { if (sub_gid_add(fields[0], sub_gid_start, sub_gid_count) == 0) { fprintf (stderr, _("%s: failed to prepare new %s entry\n"), Prog, sub_uid_dbname ()); } } else { fprintf (stderr, _("%s: can't find subordinate group range\n"), Prog); errors++; } } #endif /* ENABLE_SUBIDS */ } /* * Any detected errors will cause the entire set of changes to be * aborted. Unlocking the password file will cause all of the * changes to be ignored. Otherwise the file is closed, causing the * changes to be written out all at once, and then unlocked * afterwards. */ if (0 != errors) { fprintf (stderr, _("%s: error detected, changes ignored\n"), Prog); fail_exit (EXIT_FAILURE); } close_files (); nscd_flush_cache ("passwd"); nscd_flush_cache ("group"); sssd_flush_cache (SSSD_DB_PASSWD | SSSD_DB_GROUP); #ifdef USE_PAM unsigned int i; /* Now update the passwords using PAM */ for (i = 0; i < nusers; i++) { if (do_pam_passwd_non_interactive ("newusers", usernames[i], passwords[i]) != 0) { fprintf (stderr, _("%s: (line %d, user %s) password not changed\n"), Prog, lines[i], usernames[i]); errors++; } } #endif /* USE_PAM */ return ((0 == errors) ? EXIT_SUCCESS : EXIT_FAILURE); }
int main( int argc, char **argv) { extern char *optarg; CLIENT *cl; int ch, ret; char *passwd; prog = argv[0]; version_check(); /* * Check whether another server is running or not. There * is a race condition where two servers could be racing to * register with the portmapper. The goal of this check is to * forbid running additional servers (like those started from * the test suite) if the user is already running one. * * XXX * This does not solve nor prevent two servers from being * started at the same time and running recovery at the same * time on the same environments. */ if ((cl = clnt_create("localhost", DB_RPC_SERVERPROG, DB_RPC_SERVERVERS, "tcp")) != NULL) { fprintf(stderr, "%s: Berkeley DB RPC server already running.\n", prog); clnt_destroy(cl); return (EXIT_FAILURE); } LIST_INIT(&__dbsrv_home); while ((ch = getopt(argc, argv, "h:I:L:P:t:T:Vv")) != EOF) switch (ch) { case 'h': (void)add_home(optarg); break; case 'I': if (__db_getlong(NULL, prog, optarg, 1, LONG_MAX, &__dbsrv_idleto)) return (EXIT_FAILURE); break; case 'L': logfile = optarg; break; case 'P': passwd = strdup(optarg); memset(optarg, 0, strlen(optarg)); if (passwd == NULL) { fprintf(stderr, "%s: strdup: %s\n", prog, strerror(errno)); return (EXIT_FAILURE); } if ((ret = add_passwd(passwd)) != 0) { fprintf(stderr, "%s: strdup: %s\n", prog, strerror(ret)); return (EXIT_FAILURE); } break; case 't': if (__db_getlong(NULL, prog, optarg, 1, LONG_MAX, &__dbsrv_defto)) return (EXIT_FAILURE); break; case 'T': if (__db_getlong(NULL, prog, optarg, 1, LONG_MAX, &__dbsrv_maxto)) return (EXIT_FAILURE); break; case 'V': printf("%s\n", db_version(NULL, NULL, NULL)); return (EXIT_SUCCESS); case 'v': __dbsrv_verbose = 1; break; default: usage(prog); } /* * Check default timeout against maximum timeout */ if (__dbsrv_defto > __dbsrv_maxto) __dbsrv_defto = __dbsrv_maxto; /* * Check default timeout against idle timeout * It would be bad to timeout environments sooner than txns. */ if (__dbsrv_defto > __dbsrv_idleto) fprintf(stderr, "%s: WARNING: Idle timeout %ld is less than resource timeout %ld\n", prog, __dbsrv_idleto, __dbsrv_defto); LIST_INIT(&__dbsrv_head); /* * If a client crashes during an RPC, our reply to it * generates a SIGPIPE. Ignore SIGPIPE so we don't exit unnecessarily. */ #ifdef SIGPIPE signal(SIGPIPE, SIG_IGN); #endif if (logfile != NULL && __db_util_logset("berkeley_db_svc", logfile)) return (EXIT_FAILURE); /* * Now that we are ready to start, run recovery on all the * environments specified. */ if (env_recover(prog) != 0) return (EXIT_FAILURE); /* * We've done our setup, now call the generated server loop */ if (__dbsrv_verbose) printf("%s: Ready to receive requests\n", prog); __dbsrv_main(); /* NOTREACHED */ abort(); }