void userdel_main(void) { struct passwd *pwd = NULL; pwd = xgetpwnam(*toys.optargs); update_password("/etc/passwd", pwd->pw_name, NULL); update_password("/etc/shadow", pwd->pw_name, NULL); // delete the group named USER, and remove user from group. // could update_password() be used for this? // not a good idea, as update_passwd() updates one entry at a time // in this case it will be modifying the files as many times the // USER appears in group database files. So the customized version // of update_passwd() is here. update_groupfiles("/etc/group", *toys.optargs); update_groupfiles("/etc/gshadow", *toys.optargs); if (toys.optflags & FLAG_r) { char *arg[] = {"rm", "-fr", pwd->pw_dir, NULL, NULL}; sprintf(toybuf, "/var/spool/mail/%s",pwd->pw_name); arg[3] = toybuf; xexec(arg); } }
void do_id(char *username) { int flags, i, ngroups, cmd_groups = toys.which->name[0] == 'g'; struct passwd *pw; struct group *grp; uid_t uid = getuid(), euid = geteuid(); gid_t gid = getgid(), egid = getegid(), *groups; if (cmd_groups) toys.optflags |= FLAG_G | FLAG_n; flags = toys.optflags; // check if a username is given if (username) { pw = xgetpwnam(username); uid = euid = pw->pw_uid; gid = egid = pw->pw_gid; if (cmd_groups) printf("%s : ", pw->pw_name); } i = flags & FLAG_r; pw = xgetpwuid(i ? uid : euid); if (flags & FLAG_u) s_or_u(pw->pw_name, pw->pw_uid, 1); grp = xgetgrgid(i ? gid : egid); if (flags & FLAG_g) s_or_u(grp->gr_name, grp->gr_gid, 1); if (!(flags & FLAG_G)) { showid("uid=", pw->pw_uid, pw->pw_name); showid(" gid=", grp->gr_gid, grp->gr_name); if (!i) { if (uid != euid) { pw = xgetpwuid(euid); showid(" euid=", pw->pw_uid, pw->pw_name); } if (gid != egid) { grp = xgetgrgid(egid); showid(" egid=", grp->gr_gid, grp->gr_name); } } showid(" groups=", grp->gr_gid, grp->gr_name); } groups = (gid_t *)toybuf; i = sizeof(toybuf)/sizeof(gid_t); ngroups = username ? getgrouplist(username, gid, groups, &i) : getgroups(i, groups); if (ngroups<0) perror_exit(0); for (i = 0; i<ngroups; i++) { if (i) xputc(' '); if (!(grp = getgrgid(groups[i]))) perror_msg(0); else if (flags & FLAG_G) s_or_u(grp->gr_name, grp->gr_gid, 0); else if (grp->gr_gid != egid) showid("", grp->gr_gid, grp->gr_name); } xputc('\n'); }
long FAST_FUNC xuname2uid(const char *name) { struct passwd *myuser; myuser = xgetpwnam(name); return myuser->pw_uid; }
void groupdel_main(void) { struct group *grp = xgetgrnam(toys.optargs[toys.optc-1]); char *entry = 0; // delete user from group if (toys.optc == 2) { int i, len = 0, found = 0; char *s; xgetpwnam(*toys.optargs); if (grp->gr_mem) for (i = 0; grp->gr_mem[i]; i++) { if (!found && !strcmp(*toys.optargs, grp->gr_mem[i])) found++; else len += strlen(grp->gr_mem[i]) + 1; } if (!found) error_exit("user '%s' not in group '%s'", *toys.optargs, toys.optargs[1]); entry = s = xmalloc(len); for (i = 0; grp->gr_mem[i]; ) { if (i) *(s++) = ','; s = stpcpy(s, grp->gr_mem[i]); } // delete group } else { struct passwd *pw; for (endpwent(); (pw = getpwent());) if (pw->pw_gid == grp->gr_gid) break; if (pw) error_exit("can't remove primary group of user '%s'", pw->pw_name); if (CFG_TOYBOX_FREE) endpwent(); } update_password("/etc/group", grp->gr_name, entry); update_password("/etc/gshadow", grp->gr_name, entry); if (CFG_TOYBOX_FREE) free(entry); }
int id_main(int argc UNUSED_PARAM, char **argv) { uid_t ruid; gid_t rgid; uid_t euid; gid_t egid; unsigned opt; int i; int status = EXIT_SUCCESS; const char *prefix; const char *username; #if ENABLE_SELINUX security_context_t scontext = NULL; #endif if (ENABLE_GROUPS && (!ENABLE_ID || applet_name[0] == 'g')) { /* TODO: coreutils groups prepend "USER : "******"") | JUST_ALL_GROUPS | NAME_NOT_NUMBER; } else { /* Don't allow -n -r -nr -ug -rug -nug -rnug -uZ -gZ -GZ*/ /* Don't allow more than one username */ opt = getopt32(argv, "^" "rnugG" IF_SELINUX("Z") "\0" "?1:u--g:g--u:G--u:u--G:g--G:G--g:r?ugG:n?ugG" IF_SELINUX(":u--Z:Z--u:g--Z:Z--g:G--Z:Z--G") ); } username = argv[optind]; if (username) { struct passwd *p = xgetpwnam(username); euid = ruid = p->pw_uid; egid = rgid = p->pw_gid; } else { egid = getegid(); rgid = getgid(); euid = geteuid(); ruid = getuid(); } /* JUST_ALL_GROUPS ignores -r PRINT_REAL flag even if man page for */ /* id says: print the real ID instead of the effective ID, with -ugG */ /* in fact in this case egid is always printed if egid != rgid */ if (!opt || (opt & JUST_ALL_GROUPS)) { gid_t *groups; int n; if (!opt) { /* Default Mode */ status |= print_user(ruid, "uid="); status |= print_group(rgid, " gid="); if (euid != ruid) status |= print_user(euid, " euid="); if (egid != rgid) status |= print_group(egid, " egid="); } else { /* JUST_ALL_GROUPS */ status |= print_group(rgid, NULL); if (egid != rgid) status |= print_group(egid, " "); } /* We are supplying largish buffer, trying * to not run get_groups() twice. That might be slow * ("user database in remote SQL server" case) */ groups = xmalloc(64 * sizeof(groups[0])); n = 64; if (get_groups(username, rgid, groups, &n) < 0) { /* Need bigger buffer after all */ groups = xrealloc(groups, n * sizeof(groups[0])); get_groups(username, rgid, groups, &n); } if (n > 0) { /* Print the list */ prefix = " groups="; for (i = 0; i < n; i++) { if (opt && (groups[i] == rgid || groups[i] == egid)) continue; status |= print_group(groups[i], opt ? " " : prefix); prefix = ","; } } else if (n < 0) { /* error in get_groups() */ if (ENABLE_DESKTOP) bb_error_msg_and_die("can't get groups"); return EXIT_FAILURE; } if (ENABLE_FEATURE_CLEAN_UP) free(groups); #if ENABLE_SELINUX if (is_selinux_enabled()) { if (getcon(&scontext) == 0) printf(" context=%s", scontext); } #endif } else if (opt & PRINT_REAL) { euid = ruid; egid = rgid; } if (opt & JUST_USER) status |= print_user(euid, NULL); else if (opt & JUST_GROUP) status |= print_group(egid, NULL); #if ENABLE_SELINUX else if (opt & JUST_CONTEXT) { selinux_or_die(); if (username || getcon(&scontext)) { bb_error_msg_and_die("can't get process context%s", username ? " for a different user" : ""); } fputs(scontext, stdout); } /* freecon(NULL) seems to be harmless */ if (ENABLE_FEATURE_CLEAN_UP) freecon(scontext); #endif bb_putchar('\n'); fflush_stdout_and_exit(status); }
/* * chfn - change a user's password file information * * This command controls the GECOS field information in the password * file entry. * * The valid options are * * -f full name * -r room number * -w work phone number * -h home phone number * -o other information (*) * * (*) requires root permission to execute. */ int main (int argc, char **argv) { const struct passwd *pw; /* password file entry */ char new_gecos[BUFSIZ]; /* buffer for new GECOS fields */ char *user; /* * Get the program name. The program name is used as a * prefix to most error messages. */ Prog = Basename (argv[0]); sanitize_env (); (void) setlocale (LC_ALL, ""); (void) bindtextdomain (PACKAGE, LOCALEDIR); (void) textdomain (PACKAGE); process_root_flag ("-R", argc, argv); /* * This command behaves different for root and non-root * users. */ amroot = (getuid () == 0); OPENLOG ("chfn"); /* parse the command line options */ process_flags (argc, argv); /* * Get the name of the user to check. It is either the command line * name, or the name getlogin() returns. */ if (optind < argc) { user = argv[optind]; pw = xgetpwnam (user); if (NULL == pw) { fprintf (stderr, _("%s: user '%s' does not exist\n"), Prog, user); fail_exit (E_NOPERM); } } else { pw = get_my_pwent (); if (NULL == pw) { fprintf (stderr, _("%s: Cannot determine your user name.\n"), Prog); SYSLOG ((LOG_WARN, "Cannot determine the user name of the caller (UID %lu)", (unsigned long) getuid ())); fail_exit (E_NOPERM); } user = xstrdup (pw->pw_name); } #ifdef USE_NIS /* * Now we make sure this is a LOCAL password entry for this user ... */ if (__ispwNIS ()) { char *nis_domain; char *nis_master; fprintf (stderr, _("%s: cannot change user '%s' on NIS client.\n"), Prog, user); if (!yp_get_default_domain (&nis_domain) && !yp_master (nis_domain, "passwd.byname", &nis_master)) { fprintf (stderr, _ ("%s: '%s' is the NIS master for this client.\n"), Prog, nis_master); } fail_exit (E_NOPERM); } #endif /* Check that the caller is allowed to change the gecos of the * specified user */ check_perms (pw); /* If some fields were not set on the command line, load the value from * the old gecos fields. */ get_old_fields (pw->pw_gecos); /* * If none of the fields were changed from the command line, let the * user interactively change them. */ if (!fflg && !rflg && !wflg && !hflg && !oflg) { printf (_("Changing the user information for %s\n"), user); new_fields (); } /* * Check all of the fields for valid information */ check_fields (); /* * Build the new GECOS field by plastering all the pieces together, * if they will fit ... */ if ((strlen (fullnm) + strlen (roomno) + strlen (workph) + strlen (homeph) + strlen (slop)) > (unsigned int) 80) { fprintf (stderr, _("%s: fields too long\n"), Prog); fail_exit (E_NOPERM); } snprintf (new_gecos, sizeof new_gecos, "%s,%s,%s,%s%s%s", fullnm, roomno, workph, homeph, ('\0' != slop[0]) ? "," : "", slop); /* Rewrite the user's gecos in the passwd file */ update_gecos (user, new_gecos); SYSLOG ((LOG_INFO, "changed user '%s' information", user)); nscd_flush_cache ("passwd"); closelog (); exit (E_SUCCESS); }
/* * login - create a new login session for a user * * login is typically called by getty as the second step of a * new user session. getty is responsible for setting the line * characteristics to a reasonable set of values and getting * the name of the user to be logged in. login may also be * called to create a new user session on a pty for a variety * of reasons, such as X servers or network logins. * * the flags which login supports are * * -p - preserve the environment * -r - perform autologin protocol for rlogin * -f - do not perform authentication, user is preauthenticated * -h - the name of the remote host */ int main (int argc, char **argv) { const char *tmptty; char tty[BUFSIZ]; #ifdef RLOGIN char term[128] = ""; #endif /* RLOGIN */ #if defined(HAVE_STRFTIME) && !defined(USE_PAM) char ptime[80]; #endif unsigned int delay; unsigned int retries; bool failed; bool subroot = false; #ifndef USE_PAM bool is_console; #endif int err; const char *cp; char *tmp; char fromhost[512]; struct passwd *pwd = NULL; char **envp = environ; const char *failent_user; /*@null@*/struct utmp *utent; #ifdef USE_PAM int retcode; pid_t child; char *pam_user = NULL; #else struct spwd *spwd = NULL; #endif /* * Some quick initialization. */ sanitize_env (); (void) setlocale (LC_ALL, ""); (void) bindtextdomain (PACKAGE, LOCALEDIR); (void) textdomain (PACKAGE); initenv (); amroot = (getuid () == 0); Prog = Basename (argv[0]); if (geteuid() != 0) { fprintf (stderr, _("%s: Cannot possibly work without effective root\n"), Prog); exit (1); } process_flags (argc, argv); if ((isatty (0) == 0) || (isatty (1) == 0) || (isatty (2) == 0)) { exit (1); /* must be a terminal */ } utent = get_current_utmp (); /* * Be picky if run by normal users (possible if installed setuid * root), but not if run by root. This way it still allows logins * even if your getty is broken, or if something corrupts utmp, * but users must "exec login" which will use the existing utmp * entry (will not overwrite remote hostname). --marekm */ if (!amroot && (NULL == utent)) { (void) puts (_("No utmp entry. You must exec \"login\" from the lowest level \"sh\"")); exit (1); } /* NOTE: utent might be NULL afterwards */ tmptty = ttyname (0); if (NULL == tmptty) { tmptty = "UNKNOWN"; } STRFCPY (tty, tmptty); #ifndef USE_PAM is_console = console (tty); #endif if (rflg || hflg) { /* * Add remote hostname to the environment. I think * (not sure) I saw it once on Irix. --marekm */ addenv ("REMOTEHOST", hostname); } if (fflg) { preauth_flag = true; } if (hflg) { reason = PW_RLOGIN; } #ifdef RLOGIN if (rflg) { assert (NULL == username); username = xmalloc (USER_NAME_MAX_LENGTH + 1); username[USER_NAME_MAX_LENGTH] = '\0'; if (do_rlogin (hostname, username, USER_NAME_MAX_LENGTH, term, sizeof term)) { preauth_flag = true; } else { free (username); username = NULL; } } #endif /* RLOGIN */ OPENLOG ("login"); setup_tty (); #ifndef USE_PAM (void) umask (getdef_num ("UMASK", GETDEF_DEFAULT_UMASK)); { /* * Use the ULIMIT in the login.defs file, and if * there isn't one, use the default value. The * user may have one for themselves, but otherwise, * just take what you get. */ long limit = getdef_long ("ULIMIT", -1L); if (limit != -1) { set_filesize_limit (limit); } } #endif /* * The entire environment will be preserved if the -p flag * is used. */ if (pflg) { while (NULL != *envp) { /* add inherited environment, */ addenv (*envp, NULL); /* some variables change later */ envp++; } } #ifdef RLOGIN if (term[0] != '\0') { addenv ("TERM", term); } else #endif /* RLOGIN */ { /* preserve TERM from getty */ if (!pflg) { tmp = getenv ("TERM"); if (NULL != tmp) { addenv ("TERM", tmp); } } } init_env (); if (optind < argc) { /* now set command line variables */ set_env (argc - optind, &argv[optind]); } if (rflg || hflg) { cp = hostname; #ifdef HAVE_STRUCT_UTMP_UT_HOST } else if ((NULL != utent) && ('\0' != utent->ut_host[0])) { cp = utent->ut_host; #endif /* HAVE_STRUCT_UTMP_UT_HOST */ } else { cp = ""; } if ('\0' != *cp) { snprintf (fromhost, sizeof fromhost, " on '%.100s' from '%.200s'", tty, cp); } else { snprintf (fromhost, sizeof fromhost, " on '%.100s'", tty); } top: /* only allow ALARM sec. for login */ (void) signal (SIGALRM, alarm_handler); timeout = getdef_unum ("LOGIN_TIMEOUT", ALARM); if (timeout > 0) { (void) alarm (timeout); } environ = newenvp; /* make new environment active */ delay = getdef_unum ("FAIL_DELAY", 1); retries = getdef_unum ("LOGIN_RETRIES", RETRIES); #ifdef USE_PAM retcode = pam_start ("login", username, &conv, &pamh); if (retcode != PAM_SUCCESS) { fprintf (stderr, _("login: PAM Failure, aborting: %s\n"), pam_strerror (pamh, retcode)); SYSLOG ((LOG_ERR, "Couldn't initialize PAM: %s", pam_strerror (pamh, retcode))); exit (99); } /* * hostname & tty are either set to NULL or their correct values, * depending on how much we know. We also set PAM's fail delay to * ours. * * PAM_RHOST and PAM_TTY are used for authentication, only use * information coming from login or from the caller (e.g. no utmp) */ retcode = pam_set_item (pamh, PAM_RHOST, hostname); PAM_FAIL_CHECK; retcode = pam_set_item (pamh, PAM_TTY, tty); PAM_FAIL_CHECK; #ifdef HAS_PAM_FAIL_DELAY retcode = pam_fail_delay (pamh, 1000000 * delay); PAM_FAIL_CHECK; #endif /* if fflg, then the user has already been authenticated */ if (!fflg) { unsigned int failcount = 0; char hostn[256]; char loginprompt[256]; /* That's one hell of a prompt :) */ /* Make the login prompt look like we want it */ if (gethostname (hostn, sizeof (hostn)) == 0) { snprintf (loginprompt, sizeof (loginprompt), _("%s login: "******"login: "******"TOO MANY LOGIN TRIES (%u)%s FOR '%s'", failcount, fromhost, failent_user)); fprintf(stderr, _("Maximum number of tries exceeded (%u)\n"), failcount); PAM_END; exit(0); } else if (retcode == PAM_ABORT) { /* Serious problems, quit now */ (void) fputs (_("login: abort requested by PAM\n"), stderr); SYSLOG ((LOG_ERR,"PAM_ABORT returned from pam_authenticate()")); PAM_END; exit(99); } else if (retcode != PAM_SUCCESS) { SYSLOG ((LOG_NOTICE,"FAILED LOGIN (%u)%s FOR '%s', %s", failcount, fromhost, failent_user, pam_strerror (pamh, retcode))); failed = true; } if (!failed) { break; } #ifdef WITH_AUDIT audit_fd = audit_open (); audit_log_acct_message (audit_fd, AUDIT_USER_LOGIN, NULL, /* Prog. name */ "login", failent_user, AUDIT_NO_ID, hostname, NULL, /* addr */ tty, 0); /* result */ close (audit_fd); #endif /* WITH_AUDIT */ (void) puts (""); (void) puts (_("Login incorrect")); if (failcount >= retries) { SYSLOG ((LOG_NOTICE, "TOO MANY LOGIN TRIES (%u)%s FOR '%s'", failcount, fromhost, failent_user)); fprintf(stderr, _("Maximum number of tries exceeded (%u)\n"), failcount); PAM_END; exit(0); } /* * Let's give it another go around. * Even if a username was given on the command * line, prompt again for the username. */ retcode = pam_set_item (pamh, PAM_USER, NULL); PAM_FAIL_CHECK; } /* We don't get here unless they were authenticated above */ (void) alarm (0); } /* Check the account validity */ retcode = pam_acct_mgmt (pamh, 0); if (retcode == PAM_NEW_AUTHTOK_REQD) { retcode = pam_chauthtok (pamh, PAM_CHANGE_EXPIRED_AUTHTOK); } PAM_FAIL_CHECK; /* Open the PAM session */ get_pam_user (&pam_user); retcode = pam_open_session (pamh, hushed (pam_user) ? PAM_SILENT : 0); PAM_FAIL_CHECK; /* Grab the user information out of the password file for future usage * First get the username that we are actually using, though. * * From now on, we will discard changes of the user (PAM_USER) by * PAM APIs. */ get_pam_user (&pam_user); if (NULL != username) { free (username); } username = pam_user; failent_user = get_failent_user (username); pwd = xgetpwnam (username); if (NULL == pwd) { SYSLOG ((LOG_ERR, "cannot find user %s", failent_user)); exit (1); } /* This set up the process credential (group) and initialize the * supplementary group access list. * This has to be done before pam_setcred */ if (setup_groups (pwd) != 0) { exit (1); } retcode = pam_setcred (pamh, PAM_ESTABLISH_CRED); PAM_FAIL_CHECK; /* NOTE: If pam_setcred changes PAM_USER, this will not be taken * into account. */ #else /* ! USE_PAM */ while (true) { /* repeatedly get login/password pairs */ /* user_passwd is always a pointer to this constant string * or a passwd or shadow password that will be memzero by * pw_free / spw_free. * Do not free() user_passwd. */ const char *user_passwd = "!"; /* Do some cleanup to avoid keeping entries we do not need * anymore. */ if (NULL != pwd) { pw_free (pwd); pwd = NULL; } if (NULL != spwd) { spw_free (spwd); spwd = NULL; } failed = false; /* haven't failed authentication yet */ if (NULL == username) { /* need to get a login id */ if (subroot) { closelog (); exit (1); } preauth_flag = false; username = xmalloc (USER_NAME_MAX_LENGTH + 1); username[USER_NAME_MAX_LENGTH] = '\0'; login_prompt (_("\n%s login: "******"!", * the account is locked and the user cannot * login, even if they have been * "pre-authenticated." */ if ( ('!' == user_passwd[0]) || ('*' == user_passwd[0])) { failed = true; } } if (strcmp (user_passwd, SHADOW_PASSWD_STRING) == 0) { spwd = xgetspnam (username); if (NULL != spwd) { user_passwd = spwd->sp_pwdp; } else { /* The user exists in passwd, but not in * shadow. SHADOW_PASSWD_STRING indicates * that the password shall be in shadow. */ SYSLOG ((LOG_WARN, "no shadow password for '%s'%s", username, fromhost)); } } /* * The -r and -f flags provide a name which has already * been authenticated by some server. */ if (preauth_flag) { goto auth_ok; } if (pw_auth (user_passwd, username, reason, (char *) 0) == 0) { goto auth_ok; } SYSLOG ((LOG_WARN, "invalid password for '%s' %s", failent_user, fromhost)); failed = true; auth_ok: /* * This is the point where all authenticated users wind up. * If you reach this far, your password has been * authenticated and so on. */ if ( !failed && (NULL != pwd) && (0 == pwd->pw_uid) && !is_console) { SYSLOG ((LOG_CRIT, "ILLEGAL ROOT LOGIN %s", fromhost)); failed = true; } if ( !failed && !login_access (username, ('\0' != *hostname) ? hostname : tty)) { SYSLOG ((LOG_WARN, "LOGIN '%s' REFUSED %s", username, fromhost)); failed = true; } if ( (NULL != pwd) && getdef_bool ("FAILLOG_ENAB") && !failcheck (pwd->pw_uid, &faillog, failed)) { SYSLOG ((LOG_CRIT, "exceeded failure limit for '%s' %s", username, fromhost)); failed = true; } if (!failed) { break; } /* don't log non-existent users */ if ((NULL != pwd) && getdef_bool ("FAILLOG_ENAB")) { failure (pwd->pw_uid, tty, &faillog); } if (getdef_str ("FTMP_FILE") != NULL) { #ifdef USE_UTMPX struct utmpx *failent = prepare_utmpx (failent_user, tty, /* FIXME: or fromhost? */hostname, utent); #else /* !USE_UTMPX */ struct utmp *failent = prepare_utmp (failent_user, tty, hostname, utent); #endif /* !USE_UTMPX */ failtmp (failent_user, failent); free (failent); } retries--; if (retries <= 0) { SYSLOG ((LOG_CRIT, "REPEATED login failures%s", fromhost)); } /* * If this was a passwordless account and we get here, login * was denied (securetty, faillog, etc.). There was no * password prompt, so do it now (will always fail - the bad * guys won't see that the passwordless account exists at * all). --marekm */ if (user_passwd[0] == '\0') { pw_auth ("!", username, reason, (char *) 0); } /* * Authentication of this user failed. * The username must be confirmed in the next try. */ free (username); username = NULL; /* * Wait a while (a la SVR4 /usr/bin/login) before attempting * to login the user again. If the earlier alarm occurs * before the sleep() below completes, login will exit. */ if (delay > 0) { (void) sleep (delay); } (void) puts (_("Login incorrect")); /* allow only one attempt with -r or -f */ if (rflg || fflg || (retries <= 0)) { closelog (); exit (1); } } /* while (true) */ #endif /* ! USE_PAM */ assert (NULL != username); assert (NULL != pwd); (void) alarm (0); /* turn off alarm clock */ #ifndef USE_PAM /* PAM does this */ /* * porttime checks moved here, after the user has been * authenticated. now prints a message, as suggested * by Ivan Nejgebauer <*****@*****.**>. --marekm */ if ( getdef_bool ("PORTTIME_CHECKS_ENAB") && !isttytime (username, tty, time ((time_t *) 0))) { SYSLOG ((LOG_WARN, "invalid login time for '%s'%s", username, fromhost)); closelog (); bad_time_notify (); exit (1); } check_nologin (pwd->pw_uid == 0); #endif if (getenv ("IFS")) { /* don't export user IFS ... */ addenv ("IFS= \t\n", NULL); /* ... instead, set a safe IFS */ } if (pwd->pw_shell[0] == '*') { /* subsystem root */ pwd->pw_shell++; /* skip the '*' */ subsystem (pwd); /* figure out what to execute */ subroot = true; /* say I was here again */ endpwent (); /* close all of the file which were */ endgrent (); /* open in the original rooted file */ endspent (); /* system. they will be re-opened */ #ifdef SHADOWGRP endsgent (); /* in the new rooted file system */ #endif goto top; /* go do all this all over again */ } #ifdef WITH_AUDIT audit_fd = audit_open (); audit_log_acct_message (audit_fd, AUDIT_USER_LOGIN, NULL, /* Prog. name */ "login", username, AUDIT_NO_ID, hostname, NULL, /* addr */ tty, 1); /* result */ close (audit_fd); #endif /* WITH_AUDIT */ #ifndef USE_PAM /* pam_lastlog handles this */ if (getdef_bool ("LASTLOG_ENAB")) { /* give last login and log this one */ dolastlog (&ll, pwd, tty, hostname); } #endif #ifndef USE_PAM /* PAM handles this as well */ /* * Have to do this while we still have root privileges, otherwise we * don't have access to /etc/shadow. */ if (NULL != spwd) { /* check for age of password */ if (expire (pwd, spwd)) { /* The user updated her password, get the new * entries. * Use the x variants because we need to keep the * entry for a long time, and there might be other * getxxyy in between. */ pw_free (pwd); pwd = xgetpwnam (username); if (NULL == pwd) { SYSLOG ((LOG_ERR, "cannot find user %s after update of expired password", username)); exit (1); } spw_free (spwd); spwd = xgetspnam (username); } } setup_limits (pwd); /* nice, ulimit etc. */ #endif /* ! USE_PAM */ chown_tty (pwd); #ifdef USE_PAM /* * We must fork before setuid() because we need to call * pam_close_session() as root. */ (void) signal (SIGINT, SIG_IGN); child = fork (); if (child < 0) { /* error in fork() */ fprintf (stderr, _("%s: failure forking: %s"), Prog, strerror (errno)); PAM_END; exit (0); } else if (child != 0) { /* * parent - wait for child to finish, then cleanup * session */ wait (NULL); PAM_END; exit (0); } /* child */ #endif /* If we were init, we need to start a new session */ if (getppid() == 1) { setsid(); if (ioctl(0, TIOCSCTTY, 1) != 0) { fprintf (stderr, _("TIOCSCTTY failed on %s"), tty); } } /* * The utmp entry needs to be updated to indicate the new status * of the session, the new PID and SID. */ update_utmp (username, tty, hostname, utent); /* The pwd and spwd entries for the user have been copied. * * Close all the files so that unauthorized access won't occur. */ endpwent (); /* stop access to password file */ endgrent (); /* stop access to group file */ endspent (); /* stop access to shadow passwd file */ #ifdef SHADOWGRP endsgent (); /* stop access to shadow group file */ #endif /* Drop root privileges */ #ifndef USE_PAM if (setup_uid_gid (pwd, is_console)) #else /* The group privileges were already dropped. * See setup_groups() above. */ if (change_uid (pwd)) #endif { exit (1); } setup_env (pwd); /* set env vars, cd to the home dir */ #ifdef USE_PAM { const char *const *env; env = (const char *const *) pam_getenvlist (pamh); while ((NULL != env) && (NULL != *env)) { addenv (*env, NULL); env++; } } #endif (void) setlocale (LC_ALL, ""); (void) bindtextdomain (PACKAGE, LOCALEDIR); (void) textdomain (PACKAGE); if (!hushed (username)) { addenv ("HUSHLOGIN=FALSE", NULL); /* * pam_unix, pam_mail and pam_lastlog should take care of * this */ #ifndef USE_PAM motd (); /* print the message of the day */ if ( getdef_bool ("FAILLOG_ENAB") && (0 != faillog.fail_cnt)) { failprint (&faillog); /* Reset the lockout times if logged in */ if ( (0 != faillog.fail_max) && (faillog.fail_cnt >= faillog.fail_max)) { (void) puts (_("Warning: login re-enabled after temporary lockout.")); SYSLOG ((LOG_WARN, "login '%s' re-enabled after temporary lockout (%d failures)", username, (int) faillog.fail_cnt)); } } if ( getdef_bool ("LASTLOG_ENAB") && (ll.ll_time != 0)) { time_t ll_time = ll.ll_time; #ifdef HAVE_STRFTIME (void) strftime (ptime, sizeof (ptime), "%a %b %e %H:%M:%S %z %Y", localtime (&ll_time)); printf (_("Last login: %s on %s"), ptime, ll.ll_line); #else printf (_("Last login: %.19s on %s"), ctime (&ll_time), ll.ll_line); #endif #ifdef HAVE_LL_HOST /* __linux__ || SUN4 */ if ('\0' != ll.ll_host[0]) { printf (_(" from %.*s"), (int) sizeof ll.ll_host, ll.ll_host); } #endif printf (".\n"); } agecheck (spwd); mailcheck (); /* report on the status of mail */ #endif /* !USE_PAM */ } else { addenv ("HUSHLOGIN=TRUE", NULL); } ttytype (tty); (void) signal (SIGQUIT, SIG_DFL); /* default quit signal */ (void) signal (SIGTERM, SIG_DFL); /* default terminate signal */ (void) signal (SIGALRM, SIG_DFL); /* default alarm signal */ (void) signal (SIGHUP, SIG_DFL); /* added this. --marekm */ (void) signal (SIGINT, SIG_DFL); /* default interrupt signal */ if (0 == pwd->pw_uid) { SYSLOG ((LOG_NOTICE, "ROOT LOGIN %s", fromhost)); } else if (getdef_bool ("LOG_OK_LOGINS")) { SYSLOG ((LOG_INFO, "'%s' logged in %s", username, fromhost)); } closelog (); tmp = getdef_str ("FAKE_SHELL"); if (NULL != tmp) { err = shell (tmp, pwd->pw_shell, newenvp); /* fake shell */ } else { /* exec the shell finally */ err = shell (pwd->pw_shell, (char *) 0, newenvp); } return ((err == ENOENT) ? E_CMD_NOTFOUND : E_CMD_NOEXEC); }
int crontab_main(int argc UNUSED_PARAM, char **argv) { const struct passwd *pas; const char *crontab_dir = CRONTABS; char *tmp_fname; char *new_fname; char *user_name; /* -u USER */ int fd; int src_fd; int opt_ler; /* file [opts] Replace crontab from file * - [opts] Replace crontab from stdin * -u user User * -c dir Crontab directory * -l List crontab for user * -e Edit crontab for user * -r Delete crontab for user * bbox also supports -d == -r, but most other crontab * implementations do not. Deprecated. */ enum { OPT_u = (1 << 0), OPT_c = (1 << 1), OPT_l = (1 << 2), OPT_e = (1 << 3), OPT_r = (1 << 4), OPT_ler = OPT_l + OPT_e + OPT_r, }; opt_complementary = "?1:dr"; /* max one argument; -d implies -r */ opt_ler = getopt32(argv, "u:c:lerd", &user_name, &crontab_dir); argv += optind; if (sanitize_env_if_suid()) { /* Clears dangerous stuff, sets PATH */ /* Run by non-root */ if (opt_ler & (OPT_u|OPT_c)) bb_error_msg_and_die(bb_msg_you_must_be_root); } if (opt_ler & OPT_u) { pas = xgetpwnam(user_name); } else { pas = xgetpwuid(getuid()); } #define user_name DONT_USE_ME_BEYOND_THIS_POINT /* From now on, keep only -l, -e, -r bits */ opt_ler &= OPT_ler; if ((opt_ler - 1) & opt_ler) /* more than one bit set? */ bb_show_usage(); /* Read replacement file under user's UID/GID/group vector */ src_fd = STDIN_FILENO; if (!opt_ler) { /* Replace? */ if (!argv[0]) bb_show_usage(); if (NOT_LONE_DASH(argv[0])) { src_fd = open_as_user(pas, argv[0]); if (src_fd < 0) bb_error_msg_and_die("user %s cannot read %s", pas->pw_name, argv[0]); } } /* cd to our crontab directory */ xchdir(crontab_dir); tmp_fname = NULL; /* Handle requested operation */ switch (opt_ler) { default: /* case OPT_r: Delete */ unlink(pas->pw_name); break; case OPT_l: /* List */ { char *args[2] = { pas->pw_name, NULL }; return bb_cat(args); /* list exits, * the rest go play with cron update file */ } case OPT_e: /* Edit */ tmp_fname = xasprintf("%s.%u", crontab_dir, (unsigned)getpid()); /* No O_EXCL: we don't want to be stuck if earlier crontabs * were killed, leaving stale temp file behind */ src_fd = xopen3(tmp_fname, O_RDWR|O_CREAT|O_TRUNC, 0600); fchown(src_fd, pas->pw_uid, pas->pw_gid); fd = open(pas->pw_name, O_RDONLY); if (fd >= 0) { bb_copyfd_eof(fd, src_fd); close(fd); xlseek(src_fd, 0, SEEK_SET); } close_on_exec_on(src_fd); /* don't want editor to see this fd */ edit_file(pas, tmp_fname); /* fall through */ case 0: /* Replace (no -l, -e, or -r were given) */ new_fname = xasprintf("%s.new", pas->pw_name); fd = open(new_fname, O_WRONLY|O_CREAT|O_TRUNC|O_APPEND, 0600); if (fd >= 0) { bb_copyfd_eof(src_fd, fd); close(fd); xrename(new_fname, pas->pw_name); } else { bb_error_msg("can't create %s/%s", crontab_dir, new_fname); } if (tmp_fname) unlink(tmp_fname); /*free(tmp_fname);*/ /*free(new_fname);*/ } /* switch */ /* Bump notification file. Handle window where crond picks file up * before we can write our entry out. */ while ((fd = open(CRONUPDATE, O_WRONLY|O_CREAT|O_APPEND, 0600)) >= 0) { struct stat st; fdprintf(fd, "%s\n", pas->pw_name); if (fstat(fd, &st) != 0 || st.st_nlink != 0) { /*close(fd);*/ break; } /* st.st_nlink == 0: * file was deleted, maybe crond missed our notification */ close(fd); /* loop */ } if (fd < 0) { bb_error_msg("can't append to %s/%s", crontab_dir, CRONUPDATE); } return 0; }
/* * passwd - change a user's password file information * * This command controls the password file and commands which are used * to modify it. * * The valid options are * * -d delete the password for the named account (*) * -e expire the password for the named account (*) * -f execute chfn command to interpret flags * -g execute gpasswd command to interpret flags * -i # set sp_inact to # days (*) * -k change password only if expired * -l lock the password of the named account (*) * -n # set sp_min to # days (*) * -r # change password in # repository * -s execute chsh command to interpret flags * -S show password status of named account * -u unlock the password of the named account (*) * -w # set sp_warn to # days (*) * -x # set sp_max to # days (*) * * (*) requires root permission to execute. * * All of the time fields are entered in days and converted to the * appropriate internal format. For finer resolute the chage * command must be used. */ int main (int argc, char **argv) { const struct passwd *pw; /* Password file entry for user */ #ifndef USE_PAM char *cp; /* Miscellaneous character pointing */ const struct spwd *sp; /* Shadow file entry for user */ #endif /* !USE_PAM */ (void) setlocale (LC_ALL, ""); (void) bindtextdomain (PACKAGE, LOCALEDIR); (void) textdomain (PACKAGE); /* * The program behaves differently when executed by root than when * executed by a normal user. */ amroot = (getuid () == 0); /* * Get the program name. The program name is used as a prefix to * most error messages. */ Prog = Basename (argv[0]); sanitize_env (); OPENLOG ("passwd"); { /* * Parse the command line options. */ int option_index = 0; int c; static struct option long_options[] = { {"all", no_argument, NULL, 'a'}, {"delete", no_argument, NULL, 'd'}, {"expire", no_argument, NULL, 'e'}, {"help", no_argument, NULL, 'h'}, {"inactive", required_argument, NULL, 'i'}, {"keep-tokens", no_argument, NULL, 'k'}, {"lock", no_argument, NULL, 'l'}, {"mindays", required_argument, NULL, 'n'}, {"quiet", no_argument, NULL, 'q'}, {"root", required_argument, NULL, 'R'}, {"repository", required_argument, NULL, 'r'}, {"status", no_argument, NULL, 'S'}, {"unlock", no_argument, NULL, 'u'}, {"warndays", required_argument, NULL, 'w'}, {"maxdays", required_argument, NULL, 'x'}, {NULL, 0, NULL, '\0'} }; while ((c = getopt_long (argc, argv, "adei:kln:qR:r:Suw:x:", long_options, &option_index)) != -1) { switch (c) { case 'a': aflg = true; break; case 'd': dflg = true; anyflag = true; break; case 'e': eflg = true; anyflag = true; break; case 'i': if ( (getlong (optarg, &inact) == 0) || (inact < -1)) { fprintf (stderr, _("%s: invalid numeric argument '%s'\n"), Prog, optarg); usage (E_BAD_ARG); } iflg = true; anyflag = true; break; case 'k': /* change only if expired, like Linux-PAM passwd -k. */ kflg = true; /* ok for users */ break; case 'l': lflg = true; anyflag = true; break; case 'n': if ( (getlong (optarg, &age_min) == 0) || (age_min < -1)) { fprintf (stderr, _("%s: invalid numeric argument '%s'\n"), Prog, optarg); usage (E_BAD_ARG); } nflg = true; anyflag = true; break; case 'q': qflg = true; /* ok for users */ break; case 'R': if ('/' != optarg[0]) { fprintf (stderr, _("%s: invalid chroot path '%s'\n"), Prog, optarg); exit (E_BAD_ARG); } newroot = optarg; if (access (newroot, F_OK) != 0) { fprintf(stderr, _("%s: chroot directory %s does not exist\n"), Prog, newroot); exit (E_BAD_ARG); } if ( chroot(newroot) != 0 ) { fprintf(stderr, _("%s: unable to chroot to directory %s\n"), Prog, newroot); exit (E_BAD_ARG); } break; case 'r': /* -r repository (files|nis|nisplus) */ /* only "files" supported for now */ if (strcmp (optarg, "files") != 0) { fprintf (stderr, _("%s: repository %s not supported\n"), Prog, optarg); exit (E_BAD_ARG); } break; case 'S': Sflg = true; /* ok for users */ break; case 'u': uflg = true; anyflag = true; break; case 'w': if ( (getlong (optarg, &warn) == 0) || (warn < -1)) { fprintf (stderr, _("%s: invalid numeric argument '%s'\n"), Prog, optarg); usage (E_BAD_ARG); } wflg = true; anyflag = true; break; case 'x': if ( (getlong (optarg, &age_max) == 0) || (age_max < -1)) { fprintf (stderr, _("%s: invalid numeric argument '%s'\n"), Prog, optarg); usage (E_BAD_ARG); } xflg = true; anyflag = true; break; default: usage (E_BAD_ARG); } } } /* * Now I have to get the user name. The name will be gotten from the * command line if possible. Otherwise it is figured out from the * environment. */ pw = get_my_pwent (); if (NULL == pw) { fprintf (stderr, _("%s: Cannot determine your user name.\n"), Prog); SYSLOG ((LOG_WARN, "Cannot determine the user name of the caller (UID %lu)", (unsigned long) getuid ())); exit (E_NOPERM); } myname = xstrdup (pw->pw_name); if (optind < argc) { name = argv[optind]; } else { name = myname; } /* * Make sure that at most one username was specified. */ if (argc > (optind+1)) { usage (E_USAGE); } /* * The -a flag requires -S, no other flags, no username, and * you must be root. --marekm */ if (aflg) { if (anyflag || !Sflg || (optind < argc)) { usage (E_USAGE); } if (!amroot) { fprintf (stderr, _("%s: Permission denied.\n"), Prog); exit (E_NOPERM); } setpwent (); while ( (pw = getpwent ()) != NULL ) { print_status (pw); } endpwent (); exit (E_SUCCESS); } #if 0 /* * Allow certain users (administrators) to change passwords of * certain users. Not implemented yet. --marekm */ if (may_change_passwd (myname, name)) amroot = 1; #endif /* * If any of the flags were given, a user name must be supplied on * the command line. Only an unadorned command line doesn't require * the user's name be given. Also, -x, -n, -w, -i, -e, -d, * -l, -u may appear with each other. -S, -k must appear alone. */ /* * -S now ok for normal users (check status of my own account), and * doesn't require username. --marekm */ if (anyflag && optind >= argc) { usage (E_USAGE); } if ( (Sflg && kflg) || (anyflag && (Sflg || kflg))) { usage (E_USAGE); } if (anyflag && !amroot) { fprintf (stderr, _("%s: Permission denied.\n"), Prog); exit (E_NOPERM); } pw = xgetpwnam (name); if (NULL == pw) { fprintf (stderr, _("%s: user '%s' does not exist\n"), Prog, name); exit (E_NOPERM); } #ifdef WITH_SELINUX /* only do this check when getuid()==0 because it's a pre-condition for changing a password without entering the old one */ if ((is_selinux_enabled() > 0) && (getuid() == 0) && (check_selinux_access (name, pw->pw_uid, PASSWD__PASSWD) != 0)) { security_context_t user_context = NULL; const char *user = "******"; if (getprevcon (&user_context) == 0) { user = user_context; } SYSLOG ((LOG_ALERT, "%s is not authorized to change the password of %s", user, name)); fprintf(stderr, _("%s: %s is not authorized to change the password of %s\n"), Prog, user, name); if (NULL != user_context) { freecon (user_context); } exit (E_NOPERM); } #endif /* WITH_SELINUX */ /* * If the UID of the user does not match the current real UID, * check if I'm root. */ if (!amroot && (pw->pw_uid != getuid ())) { fprintf (stderr, _("%s: You may not view or modify password information for %s.\n"), Prog, name); SYSLOG ((LOG_WARN, "%s: can't view or modify password information for %s", Prog, name)); closelog (); exit (E_NOPERM); } if (Sflg) { print_status (pw); exit (E_SUCCESS); } #ifndef USE_PAM /* * The user name is valid, so let's get the shadow file entry. */ sp = getspnam (name); /* !USE_PAM, no need for xgetspnam */ if (NULL == sp) { sp = pwd_to_spwd (pw); } cp = sp->sp_pwdp; /* * If there are no other flags, just change the password. */ if (!anyflag) { STRFCPY (crypt_passwd, cp); /* * See if the user is permitted to change the password. * Otherwise, go ahead and set a new password. */ check_password (pw, sp); /* * Let the user know whose password is being changed. */ if (!qflg) { printf (_("Changing password for %s\n"), name); } if (new_password (pw)) { fprintf (stderr, _("The password for %s is unchanged.\n"), name); closelog (); exit (E_NOPERM); } do_update_pwd = true; do_update_age = true; } #endif /* !USE_PAM */ /* * Before going any further, raise the ulimit to prevent colliding * into a lowered ulimit, and set the real UID to root to protect * against unexpected signals. Any keyboard signals are set to be * ignored. */ pwd_init (); #ifdef USE_PAM /* * Don't set the real UID for PAM... */ if (!anyflag) { do_pam_passwd (name, qflg, kflg); exit (E_SUCCESS); } #endif /* USE_PAM */ if (setuid (0) != 0) { fputs (_("Cannot change ID to root.\n"), stderr); SYSLOG ((LOG_ERR, "can't setuid(0)")); closelog (); exit (E_NOPERM); } if (spw_file_present ()) { update_shadow (); } else { update_noshadow (); } nscd_flush_cache ("passwd"); nscd_flush_cache ("group"); SYSLOG ((LOG_INFO, "password for '%s' changed by '%s'", name, myname)); closelog (); if (!qflg) { if (!anyflag) { #ifndef USE_PAM printf (_("%s: password changed.\n"), Prog); #endif /* USE_PAM */ } else { printf (_("%s: password expiry information changed.\n"), Prog); } } return E_SUCCESS; }
int passwd_main(int argc UNUSED_PARAM, char **argv) { enum { OPT_algo = (1 << 0), /* -a - password algorithm */ OPT_lock = (1 << 1), /* -l - lock account */ OPT_unlock = (1 << 2), /* -u - unlock account */ OPT_delete = (1 << 3), /* -d - delete password */ OPT_lud = OPT_lock | OPT_unlock | OPT_delete, }; unsigned opt; int rc; const char *opt_a = CONFIG_FEATURE_DEFAULT_PASSWD_ALGO; const char *filename; char *myname; char *name; char *newp; struct passwd *pw; uid_t myuid; struct rlimit rlimit_fsize; char c; #if ENABLE_FEATURE_SHADOWPASSWDS /* Using _r function to avoid pulling in static buffers */ struct spwd spw; char buffer[256]; #endif logmode = LOGMODE_BOTH; openlog(applet_name, 0, LOG_AUTH); opt = getopt32(argv, "a:lud", &opt_a); //argc -= optind; argv += optind; myuid = getuid(); /* -l, -u, -d require root priv and username argument */ if ((opt & OPT_lud) && (myuid != 0 || !argv[0])) bb_show_usage(); /* Will complain and die if username not found */ myname = xstrdup(xuid2uname(myuid)); name = argv[0] ? argv[0] : myname; pw = xgetpwnam(name); if (myuid != 0 && pw->pw_uid != myuid) { /* LOGMODE_BOTH */ bb_error_msg_and_die("%s can't change password for %s", myname, name); } #if ENABLE_FEATURE_SHADOWPASSWDS { /* getspnam_r may return 0 yet set result to NULL. * At least glibc 2.4 does this. Be extra paranoid here. */ struct spwd *result = NULL; errno = 0; if (getspnam_r(pw->pw_name, &spw, buffer, sizeof(buffer), &result) != 0 || !result /* no error, but no record found either */ || strcmp(result->sp_namp, pw->pw_name) != 0 /* paranoia */ ) { if (errno != ENOENT) { /* LOGMODE_BOTH */ bb_perror_msg("no record of %s in %s, using %s", name, bb_path_shadow_file, bb_path_passwd_file); } /* else: /etc/shadow does not exist, * apparently we are on a shadow-less system, * no surprise there */ } else { pw->pw_passwd = result->sp_pwdp; } } #endif /* Decide what the new password will be */ newp = NULL; c = pw->pw_passwd[0] - '!'; if (!(opt & OPT_lud)) { if (myuid != 0 && !c) { /* passwd starts with '!' */ /* LOGMODE_BOTH */ bb_error_msg_and_die("can't change " "locked password for %s", name); } printf("Changing password for %s\n", name); newp = new_password(pw, myuid, opt_a); if (!newp) { logmode = LOGMODE_STDIO; bb_error_msg_and_die("password for %s is unchanged", name); } } else if (opt & OPT_lock) { if (!c) goto skip; /* passwd starts with '!' */ newp = xasprintf("!%s", pw->pw_passwd); } else if (opt & OPT_unlock) { if (c) goto skip; /* not '!' */ /* pw->pw_passwd points to static storage, * strdup'ing to avoid nasty surprizes */ newp = xstrdup(&pw->pw_passwd[1]); } else if (opt & OPT_delete) { newp = (char*)""; } rlimit_fsize.rlim_cur = rlimit_fsize.rlim_max = 512L * 30000; setrlimit(RLIMIT_FSIZE, &rlimit_fsize); bb_signals(0 + (1 << SIGHUP) + (1 << SIGINT) + (1 << SIGQUIT) , SIG_IGN); umask(077); xsetuid(0); #if ENABLE_FEATURE_SHADOWPASSWDS filename = bb_path_shadow_file; rc = update_passwd(bb_path_shadow_file, name, newp, NULL); if (rc > 0) /* password in /etc/shadow was updated */ newp = (char*) "x"; if (rc >= 0) /* 0 = /etc/shadow missing (not an error), >0 = passwd changed in /etc/shadow */ #endif { filename = bb_path_passwd_file; rc = update_passwd(bb_path_passwd_file, name, newp, NULL); } /* LOGMODE_BOTH */ if (rc < 0) bb_error_msg_and_die("can't update password file %s", filename); bb_error_msg("password for %s changed by %s", name, myname); /*if (ENABLE_FEATURE_CLEAN_UP) free(newp); - can't, it may be non-malloced */ skip: if (!newp) { bb_error_msg_and_die("password for %s is already %slocked", name, (opt & OPT_unlock) ? "un" : ""); } if (ENABLE_FEATURE_CLEAN_UP) free(myname); return 0; }
int deluser_main(int argc, char **argv) { /* User or group name */ char *name; /* Username (non-NULL only in "delgroup USER GROUP" case) */ char *member; /* Name of passwd or group file */ const char *pfile; /* Name of shadow or gshadow file */ const char *sfile; /* Are we deluser or delgroup? */ int do_deluser = (ENABLE_DELUSER && (!ENABLE_DELGROUP || applet_name[3] == 'u')); if (geteuid() != 0) bb_error_msg_and_die(bb_msg_perm_denied_are_you_root); name = argv[1]; member = NULL; switch (argc) { case 3: if (!ENABLE_FEATURE_DEL_USER_FROM_GROUP || do_deluser) break; /* It's "delgroup USER GROUP" */ member = name; name = argv[2]; /* Fallthrough */ case 2: if (do_deluser) { /* "deluser USER" */ xgetpwnam(name); /* bail out if USER is wrong */ pfile = bb_path_passwd_file; if (ENABLE_FEATURE_SHADOWPASSWDS) sfile = bb_path_shadow_file; } else { struct group *gr; do_delgroup: /* "delgroup GROUP" or "delgroup USER GROUP" */ if (do_deluser < 0) { /* delgroup after deluser? */ gr = getgrnam(name); if (!gr) return EXIT_SUCCESS; } else { gr = xgetgrnam(name); /* bail out if GROUP is wrong */ } if (!member) { /* "delgroup GROUP" */ struct passwd *pw; struct passwd pwent; /* Check if the group is in use */ #define passwd_buf bb_common_bufsiz1 while (!getpwent_r(&pwent, passwd_buf, sizeof(passwd_buf), &pw)) { if (pwent.pw_gid == gr->gr_gid) bb_error_msg_and_die("'%s' still has '%s' as their primary group!", pwent.pw_name, name); } //endpwent(); } pfile = bb_path_group_file; if (ENABLE_FEATURE_SHADOWPASSWDS) sfile = bb_path_gshadow_file; } /* Modify pfile, then sfile */ do { if (update_passwd(pfile, name, NULL, member) == -1) return EXIT_FAILURE; if (ENABLE_FEATURE_SHADOWPASSWDS) { pfile = sfile; sfile = NULL; } } while (ENABLE_FEATURE_SHADOWPASSWDS && pfile); if (ENABLE_DELGROUP && do_deluser > 0) { /* "deluser USER" also should try to delete * same-named group. IOW: do "delgroup USER" */ // On debian deluser is a perl script that calls userdel. // From man userdel: // If USERGROUPS_ENAB is defined to yes in /etc/login.defs, userdel will // delete the group with the same name as the user. do_deluser = -1; goto do_delgroup; } return EXIT_SUCCESS; } /* Reached only if number of command line args is wrong */ bb_show_usage(); }
int su_main(int argc UNUSED_PARAM, char **argv) { unsigned flags; char *opt_shell = NULL; char *opt_command = NULL; const char *opt_username = "******"; struct passwd *pw; uid_t cur_uid = getuid(); const char *tty; #if ENABLE_FEATURE_UTMP char user_buf[64]; #endif const char *old_user; flags = getopt32(argv, "mplc:s:", &opt_command, &opt_shell); //argc -= optind; argv += optind; if (argv[0] && LONE_DASH(argv[0])) { flags |= SU_OPT_l; argv++; } /* get user if specified */ if (argv[0]) { opt_username = argv[0]; argv++; } if (ENABLE_FEATURE_SU_SYSLOG) { /* The utmp entry (via getlogin) is probably the best way to * identify the user, especially if someone su's from a su-shell. * But getlogin can fail -- usually due to lack of utmp entry. * in this case resort to getpwuid. */ #if ENABLE_FEATURE_UTMP old_user = user_buf; if (getlogin_r(user_buf, sizeof(user_buf)) != 0) #endif { pw = getpwuid(cur_uid); old_user = pw ? xstrdup(pw->pw_name) : ""; } tty = xmalloc_ttyname(2); if (!tty) { tty = "none"; } openlog(applet_name, 0, LOG_AUTH); } pw = xgetpwnam(opt_username); if (cur_uid == 0 || correct_password(pw)) { if (ENABLE_FEATURE_SU_SYSLOG) syslog(LOG_NOTICE, "%c %s %s:%s", '+', tty, old_user, opt_username); } else { if (ENABLE_FEATURE_SU_SYSLOG) syslog(LOG_NOTICE, "%c %s %s:%s", '-', tty, old_user, opt_username); bb_error_msg_and_die("incorrect password"); } if (ENABLE_FEATURE_CLEAN_UP && ENABLE_FEATURE_SU_SYSLOG) { closelog(); } if (!opt_shell && (flags & SU_OPT_mp)) { /* -s SHELL is not given, but "preserve env" opt is */ opt_shell = getenv("SHELL"); } #if ENABLE_FEATURE_SU_CHECKS_SHELLS if (opt_shell && cur_uid != 0 && pw->pw_shell && restricted_shell(pw->pw_shell)) { /* The user being su'd to has a nonstandard shell, and so is * probably a uucp account or has restricted access. Don't * compromise the account by allowing access with a standard * shell. */ bb_error_msg("using restricted shell"); opt_shell = NULL; /* ignore -s PROG */ } /* else: user can run whatever he wants via "su -s PROG USER". * This is safe since PROG is run under user's uid/gid. */ #endif if (!opt_shell) opt_shell = pw->pw_shell; change_identity(pw); setup_environment(opt_shell, ((flags & SU_OPT_l) / SU_OPT_l * SETUP_ENV_CLEARENV) + (!(flags & SU_OPT_mp) * SETUP_ENV_CHANGEENV), pw); IF_SELINUX(set_current_security_context(NULL);)