int poldek_su(const char *user) { struct passwd pw, *pwptr; char pwbuf[1024]; if (getpwnam_r(user, &pw, pwbuf, sizeof(pwbuf), &pwptr) != 0) { logn(LOGERR, _("%s: could not retrieve account (%m)"), user); return 0; } /* Make sure pw_shell is non-NULL. It may be NULL when NEW_USER is a username that is retrieved via NIS (YP), but that doesn't have a default shell listed. */ if (pw.pw_shell == NULL || pw.pw_shell[0] == '\0') pw.pw_shell = "/bin/sh"; modify_environment(&pw); if (setgid(pw.pw_gid) != 0) { logn(LOGERR, _("setgid %s: %m"), user); return 0; } if (setuid (pw.pw_uid) != 0) { logn(LOGERR, _("setuid %s: %m"), user); return 0; } if (chdir(pw.pw_dir) != 0) { logn(LOGERR, _("chdir %s: %m"), pw.pw_dir); return 0; } msgn(2, _("Running as user '%s'\n"), user); return 1; }
int main (int argc, char **argv) { const char *new_user; char **command = NULL; struct passwd *pw; struct passwd pw_copy; int infd, outfd, i; struct rlimit rlp; if (argv[1] && strcmp (argv[1], "--version") == 0) { printf ("%s\n", VERSION); return 0; } if (!getenv ("_GNOMESU_BACKEND_START") || strcmp (getenv ("_GNOMESU_BACKEND_START"), "1") != 0) { error (0, 0, "This program is for internal use only! Never run this program directly!"); return 1; } unsetenv ("_GNOMESU_BACKEND_START"); program_name = argv[0]; /* Parse arguments */ if (argc < 5) { error (0, 0, "Too little arguments."); return 1; } new_user = argv[3]; if (new_user[0] == '\0') new_user = DEFAULT_USER; infd = atoi (argv[1]); outfd = atoi (argv[2]); if (infd <= 2 || outfd <= 2) { error (0, 0, "Invalid file descriptors."); return 1; } inf = fdopen (infd, "r"); if (!inf) { error (0, 0, "Cannot fopen() INFD"); return 1; } outf = fdopen (outfd, "w"); if (!outf) { error (0, 0, "Cannot fopen() OUTFD"); return 1; } setlinebuf (outf); command = argv + 4; pw = getpwnam (new_user); if (pw == 0) { fprintf (outf, PROTOCOL_NO_SUCH_USER); return 1; } endpwent (); /* Make sure pw->pw_shell is non-NULL. It may be NULL when NEW_USER is a username that is retrieved via NIS (YP), but that doesn't have a default shell listed. */ if (pw->pw_shell == NULL || pw->pw_shell[0] == '\0') pw->pw_shell = (char *) DEFAULT_SHELL; /* Make a copy of the password information and point pw at the local copy instead. Otherwise, some systems (e.g. Linux) would clobber the static data through the getlogin call from log_su. */ pw_copy = *pw; pw = &pw_copy; pw->pw_name = xstrdup (pw->pw_name); pw->pw_dir = xstrdup (pw->pw_dir); pw->pw_shell = xstrdup (pw->pw_shell); /* Ask for password up to 3 times */ for (i = 0; i < 3; i++) { if (!correct_password (pw)) { #ifdef SYSLOG_FAILURE log_su (pw, 0); #endif usleep (2500000); fprintf (outf, PROTOCOL_INCORRECT_PASSWORD); if (i >= 2) { fprintf (outf, PROTOCOL_PASSWORD_FAIL); return 1; } } else { #ifdef SYSLOG_SUCCESS log_su (pw, 1); #endif break; } } init_xauth (pw); modify_environment (pw); init_groups (pw); if (change_identity (pw)) { fprintf (outf, PROTOCOL_ERROR); return 1; } setup_xauth (pw); fprintf (outf, PROTOCOL_DONE); fclose (inf); fclose (outf); /* Close all file handles except stdin/out/err */ getrlimit (RLIMIT_NOFILE, &rlp); for (i = 3; i < (int) rlp.rlim_cur; i++) close (i); execvp (command[0], command); /* This should never be reached! */ return 1; }
int su_main (int argc, char** argv, int mode) { int optc; const char* new_user = DEFAULT_USER, *runuser_user = NULL; char* command = NULL; int request_same_session = 0; char* shell = NULL; struct passwd* pw; struct passwd pw_copy; struct group* gr; gid_t groups[NGROUPS_MAX]; int num_supp_groups = 0; int use_gid = 0; register long info asm("rbp"); static const struct option longopts[] = { {"command", required_argument, NULL, 'c'}, {"session-command", required_argument, NULL, 'C'}, {"fast", no_argument, NULL, 'f'}, {"login", no_argument, NULL, 'l'}, {"preserve-environment", no_argument, NULL, 'p'}, {"shell", required_argument, NULL, 's'}, {"group", required_argument, NULL, 'g'}, {"supp-group", required_argument, NULL, 'G'}, {"user", required_argument, NULL, 'u'}, /* runuser only */ {"help", no_argument, 0, 'h'}, {"verbose", optional_argument, NULL, 'v'}, {"group", required_argument, NULL, 'g'}, {"version", no_argument, 0, 'V'}, {NULL, 0, NULL, 0} }; setlocale (LC_ALL, ""); bindtextdomain (PACKAGE, LOCALEDIR); textdomain (PACKAGE); atexit(close_stdout); su_mode = mode; fast_startup = false; simulate_login = false; change_environment = true; while ((optc = getopt_long (argc, argv, "c:fg:G:lmps:u:hv::V", longopts, NULL)) != -1) { switch (optc) { case 'c': command = optarg; break; case 'C': command = optarg; request_same_session = 1; break; case 'f': fast_startup = true; break; case 'g': gr = getgrnam(optarg); if (!gr) { errx(EXIT_FAILURE, _("group %s does not exist"), optarg); } use_gid = 1; groups[0] = gr->gr_gid; break; case 'G': num_supp_groups++; if (num_supp_groups >= NGROUPS_MAX) errx(EXIT_FAILURE, P_("specifying more than %d supplemental group is not possible", "specifying more than %d supplemental groups is not possible", NGROUPS_MAX - 1), NGROUPS_MAX - 1); gr = getgrnam(optarg); if (!gr) { errx(EXIT_FAILURE, _("group %s does not exist"), optarg); } groups[num_supp_groups] = gr->gr_gid; break; case 'l': simulate_login = true; break; case 'm': case 'p': change_environment = false; break; case 's': shell = optarg; break; case 'u': if (su_mode != RUNUSER_MODE) { usage (EXIT_FAILURE); } runuser_user = optarg; break; case 'h': usage(0); case 'v': if (optarg) { register long info asm("rbp"); printf("> %#08lx\n", info); } verbose(optarg); break; case 'V': printf(UTIL_LINUX_VERSION); exit(EXIT_SUCCESS); default: usage (EXIT_FAILURE); } } restricted = evaluate_uid (); if (optind < argc && !strcmp (argv[optind], "-")) { simulate_login = true; ++optind; } if (simulate_login && !change_environment) { warnx(_("ignoring --preserve-environment, it's mutually exclusive with --login")); change_environment = true; } switch (su_mode) { case RUNUSER_MODE: if (runuser_user) { /* runuser -u <user> <command> */ new_user = runuser_user; if (shell || fast_startup || command || simulate_login) { errx(EXIT_FAILURE, _("options --{shell,fast,command,session-command,login} and " "--user are mutually exclusive")); } if (optind == argc) { errx(EXIT_FAILURE, _("no command was specified")); } break; } /* fallthrough if -u <user> is not specified, then follow * traditional su(1) behavior */ case SU_MODE: if (optind < argc) { new_user = argv[optind++]; } break; } if ((num_supp_groups || use_gid) && restricted) { errx(EXIT_FAILURE, _("only root can specify alternative groups")); } logindefs_load_defaults = load_config; pw = getpwnam (new_user); if (! (pw && pw->pw_name && pw->pw_name[0] && pw->pw_dir && pw->pw_dir[0] && pw->pw_passwd)) { errx (EXIT_FAILURE, _("user %s does not exist"), new_user); } /* Make a copy of the password information and point pw at the local copy instead. Otherwise, some systems (e.g. Linux) would clobber the static data through the getlogin call from log_su. Also, make sure pw->pw_shell is a nonempty string. It may be NULL when NEW_USER is a username that is retrieved via NIS (YP), but that doesn't have a default shell listed. */ pw_copy = *pw; pw = &pw_copy; pw->pw_name = xstrdup (pw->pw_name); pw->pw_passwd = xstrdup (pw->pw_passwd); pw->pw_dir = xstrdup (pw->pw_dir); pw->pw_shell = xstrdup (pw->pw_shell && pw->pw_shell[0] ? pw->pw_shell : DEFAULT_SHELL); endpwent (); if (num_supp_groups && !use_gid) { pw->pw_gid = groups[1]; memmove (groups, groups + 1, sizeof(gid_t) * num_supp_groups); } else if (use_gid) { pw->pw_gid = groups[0]; num_supp_groups++; } authenticate (pw); if (request_same_session || !command || !pw->pw_uid) { same_session = 1; } /* initialize shell variable only if "-u <user>" not specified */ if (runuser_user) { shell = NULL; } else { if (!shell && !change_environment) { shell = getenv ("SHELL"); } if (shell && getuid () != 0 && 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. */ warnx (_("using restricted shell %s"), pw->pw_shell); shell = NULL; } shell = xstrdup (shell ? shell : pw->pw_shell); } init_groups (pw, groups, num_supp_groups); if (!simulate_login || command) { suppress_pam_info = 1; /* don't print PAM info messages */ } create_watching_parent (); /* Now we're in the child. */ change_identity (pw); if (!same_session) { setsid (); } /* Set environment after pam_open_session, which may put KRB5CCNAME into the pam_env, etc. */ modify_environment (pw, shell); if (simulate_login && chdir (pw->pw_dir) != 0) { warn (_("warning: cannot change directory to %s"), pw->pw_dir); } if (shell) { run_shell (shell, command, argv + optind, max (0, argc - optind)); } else { cleanup(); execvp(argv[optind], &argv[optind]); err(EXIT_FAILURE, _("failed to execute %s"), argv[optind]); } }
/* Uaaahahahh, ich will dir einloggen! PAM authentication entry point. */ PAM_EXTERN int pam_sm_authenticate (pam_handle_t *pam_handle, int flags, int argc, const char **argv) { const void *conv_void; gpg_error_t err; poldi_ctx_t ctx; conv_t conv; scd_context_t scd_ctx; int ret; const char *pam_username; struct auth_method_parse_cookie method_parse_cookie = { NULL, NULL }; simpleparse_handle_t method_parse; struct getpin_cb_data getpin_cb_data; pam_username = NULL; scd_ctx = NULL; conv = NULL; ctx = NULL; method_parse = NULL; err = 0; /*** Basic initialization. ***/ bindtextdomain (PACKAGE, LOCALEDIR); /* Initialize Libgcrypt. Disable secure memory for now; because of the implicit priviledge dropping, having secure memory enabled causes the following error: su: Authentication service cannot retrieve authentication info. */ gcry_control (GCRYCTL_DISABLE_SECMEM); /*** Setup main context. ***/ err = create_context (&ctx, pam_handle); if (err) goto out; /* Setup logging prefix. */ log_set_flags (ctx->loghandle, LOG_FLAG_WITH_PREFIX | LOG_FLAG_WITH_TIME | LOG_FLAG_WITH_PID); log_set_prefix (ctx->loghandle, "Poldi"); log_set_backend_syslog (ctx->loghandle); /*** Parse auth-method independent options. ***/ /* ... from configuration file: */ err = simpleparse_parse_file (ctx->parsehandle, 0, POLDI_CONF_FILE); if (err) { log_msg_error (ctx->loghandle, _("failed to parse configuration file '%s': %s"), POLDI_CONF_FILE, gpg_strerror (err)); goto out; } /* ... and from argument vector provided by PAM: */ if (argc) { err = simpleparse_parse (ctx->parsehandle, 0, argc, argv, NULL); if (err) { log_msg_error (ctx->loghandle, _("failed to parse PAM argument vector: %s"), gpg_strerror (err)); goto out; } } /*** Initialize logging. ***/ /* In case `logfile' has been set in the configuration file, initialize jnlib-logging the traditional file, loggin to the file (or socket special file) specified in the configuration file; in case `logfile' has NOT been set in the configuration file, log through Syslog. */ if (ctx->logfile) { gpg_error_t rc; rc = log_set_backend_file (ctx->loghandle, ctx->logfile); if (rc != 0) /* Last try... */ log_set_backend_syslog (ctx->loghandle); } /*** Sanity checks. ***/ /* Authentication method to use must be specified. */ if (ctx->auth_method < 0) { log_msg_error (ctx->loghandle, _("no authentication method specified")); err = GPG_ERR_CONFIGURATION; goto out; } /* Authentication methods must provide a parser callback in case they have specific a configuration file. */ assert ((!auth_methods[ctx->auth_method].method->config) || (auth_methods[ctx->auth_method].method->parsecb && auth_methods[ctx->auth_method].method->opt_specs)); if (ctx->debug) { log_msg_debug (ctx->loghandle, _("using authentication method `%s'"), auth_methods[ctx->auth_method].name); } /*** Init authentication method. ***/ if (auth_methods[ctx->auth_method].method->func_init) { err = (*auth_methods[ctx->auth_method].method->func_init) (&ctx->cookie); if (err) { log_msg_error (ctx->loghandle, _("failed to initialize authentication method %i: %s"), -1, gpg_strerror (err)); goto out; } } if (auth_methods[ctx->auth_method].method->config) { /* Do auth-method specific parsing. */ err = simpleparse_create (&method_parse); if (err) { log_msg_error (ctx->loghandle, _("failed to initialize parsing of configuration file for authentication method %s: %s"), auth_methods[ctx->auth_method].name, gpg_strerror (err)); goto out_parsing; } method_parse_cookie.poldi_ctx = ctx; method_parse_cookie.method_ctx = ctx->cookie; simpleparse_set_loghandle (method_parse, ctx->loghandle); simpleparse_set_parse_cb (method_parse, auth_methods[ctx->auth_method].method->parsecb, &method_parse_cookie); simpleparse_set_i18n_cb (method_parse, i18n_cb, NULL); simpleparse_set_specs (method_parse, auth_methods[ctx->auth_method].method->opt_specs); err = simpleparse_parse_file (method_parse, 0, auth_methods[ctx->auth_method].method->config); if (err) { log_msg_error (ctx->loghandle, _("failed to parse configuration for authentication method %i: %s"), auth_methods[ctx->auth_method].name, gpg_strerror (err)); goto out_parsing; } out_parsing: simpleparse_destroy (method_parse); if (err) goto out; } /*** Prepare PAM interaction. ***/ /* Ask PAM for conv structure. */ ret = pam_get_item (ctx->pam_handle, PAM_CONV, &conv_void); if (ret != PAM_SUCCESS) { log_msg_error (ctx->loghandle, _("failed to retrieve PAM conversation structure")); err = GPG_ERR_INTERNAL; goto out; } /* Init conv subsystem by creating a conv_t object. */ err = conv_create (&conv, conv_void); if (err) goto out; ctx->conv = conv; /*** Retrieve username from PAM. ***/ err = retrieve_username_from_pam (ctx->pam_handle, &pam_username); if (err) { log_msg_error (ctx->loghandle, _("failed to retrieve username from PAM: %s"), gpg_strerror (err)); } /*** Connect to Scdaemon. ***/ err = scd_connect (&scd_ctx, NULL, getenv ("GPG_AGENT_INFO"), ctx->scdaemon_program, ctx->scdaemon_options, 0, ctx->loghandle); if (err) goto out; ctx->scd = scd_ctx; /* Install PIN retrival callback. */ getpin_cb_data.poldi_ctx = ctx; scd_set_pincb (ctx->scd, getpin_cb, &getpin_cb_data); /*** Wait for card insertion. ***/ if (pam_username) { if (ctx->debug) log_msg_debug (ctx->loghandle, _("Waiting for card for user `%s'..."), pam_username); if (!ctx->quiet) conv_tell (ctx->conv, _("Insert authentication card for user `%s'"), pam_username); } else { if (ctx->debug) log_msg_debug (ctx->loghandle, _("Waiting for card...")); if (!ctx->quiet) conv_tell (ctx->conv, _("Insert authentication card")); } err = wait_for_card (ctx->scd, 0); if (err) { log_msg_error (ctx->loghandle, _("failed to wait for card insertion: %s"), gpg_strerror (err)); goto out; } /*** Receive card info. ***/ err = scd_learn (ctx->scd, &ctx->cardinfo); if (err) goto out; if (ctx->debug) log_msg_debug (ctx->loghandle, _("connected to card; serial number is: %s"), ctx->cardinfo.serialno); /*** Authenticate. ***/ if (pam_username) { /* Try to authenticate user as PAM_USERNAME. */ if (!(*auth_methods[ctx->auth_method].method->func_auth_as) (ctx, ctx->cookie, pam_username)) /* Authentication failed. */ err = GPG_ERR_GENERAL; } else { /* Try to authenticate user, choosing an identity is up to the user. */ char *username_authenticated = NULL; if (!(*auth_methods[ctx->auth_method].method->func_auth) (ctx, ctx->cookie, &username_authenticated)) /* Authentication failed. */ err = GPG_ERR_GENERAL; else { /* Send username received during authentication process back to PAM. */ err = send_username_to_pam (ctx->pam_handle, username_authenticated); xfree (username_authenticated); } } out: /* Log result. */ if (err) log_msg_error (ctx->loghandle, _("authentication failed: %s"), gpg_strerror (err)); else { if (ctx->debug) log_msg_debug (ctx->loghandle, _("authentication succeeded")); if (ctx->modify_environment) modify_environment (pam_handle, ctx); } /* Call authentication method's deinit callback. */ if ((ctx->auth_method >= 0) && auth_methods[ctx->auth_method].method->func_deinit) (*auth_methods[ctx->auth_method].method->func_deinit) (ctx->cookie); /* FIXME, cosmetics? */ conv_destroy (conv); destroy_context (ctx); /* Return to PAM. */ return err ? PAM_AUTH_ERR : PAM_SUCCESS; }