static int knet_vty_pam_auth_user(struct knet_vty *vty, const char *user) { pam_handle_t *pamh=NULL; struct pam_conv conv; int err; int retry = 1; conv.conv = knet_pam_misc_conv; conv.appdata_ptr = (void *)vty; retry_auth: err = pam_start("kronosnet", user, &conv, &pamh); if (err != PAM_SUCCESS) { errno = EINVAL; log_error("PAM fatal error: %s", pam_strerror(pamh, err)); knet_vty_write(vty, "PAM fatal error: %s", pam_strerror(pamh, err)); goto out_fatal; } err = pam_authenticate(pamh, 0); if (err != PAM_SUCCESS) { if (vty->got_epipe) { errno = EPIPE; goto out_fatal; } else { errno = EINVAL; goto out_clean; } } if (knet_vty_get_pam_user(vty, pamh) != PAM_SUCCESS) { log_error("PAM: unable to get PAM_USER: %s", pam_strerror(pamh, err)); knet_vty_write(vty, "PAM: unable to get PAM_USER: %s", pam_strerror(pamh, err)); goto out_clean; } err = pam_acct_mgmt(pamh, 0); if (err != PAM_SUCCESS) { log_info("User: %s failed to authenticate on vty(%d) attempt %d", vty->username, vty->conn_num, retry); goto out_clean; } out_clean: if (pamh) { pam_end(pamh, err); pamh = NULL; } if ((err != PAM_SUCCESS) && (retry < AUTH_MAX_RETRY)) { retry++; goto retry_auth; } out_fatal: if (pamh) { pam_end(pamh, err); pamh = NULL; } knet_vty_write(vty, "\n"); return err; }
int authenticate_user(const char *user, const char *passwd) { #ifndef HAVE_PAM gboolean pass = TRUE; #else int rc = 0; gboolean pass = FALSE; const void *p_user = NULL; struct pam_conv p_conv; struct pam_handle *pam_h = NULL; static const char *pam_name = NULL; if (pam_name == NULL) { pam_name = getenv("CIB_pam_service"); } if (pam_name == NULL) { pam_name = "login"; } p_conv.conv = construct_pam_passwd; p_conv.appdata_ptr = strdup(passwd); rc = pam_start(pam_name, user, &p_conv, &pam_h); if (rc != PAM_SUCCESS) { crm_err("Could not initialize PAM: %s (%d)", pam_strerror(pam_h, rc), rc); goto bail; } rc = pam_authenticate(pam_h, 0); if (rc != PAM_SUCCESS) { crm_err("Authentication failed for %s: %s (%d)", user, pam_strerror(pam_h, rc), rc); goto bail; } /* Make sure we authenticated the user we wanted to authenticate. * Since we also run as non-root, it might be worth pre-checking * the user has the same EID as us, since that the only user we * can authenticate. */ rc = pam_get_item(pam_h, PAM_USER, &p_user); if (rc != PAM_SUCCESS) { crm_err("Internal PAM error: %s (%d)", pam_strerror(pam_h, rc), rc); goto bail; } else if (p_user == NULL) { crm_err("Unknown user authenticated."); goto bail; } else if (safe_str_neq(p_user, user)) { crm_err("User mismatch: %s vs. %s.", (const char *)p_user, (const char *)user); goto bail; } rc = pam_acct_mgmt(pam_h, 0); if (rc != PAM_SUCCESS) { crm_err("Access denied: %s (%d)", pam_strerror(pam_h, rc), rc); goto bail; } pass = TRUE; bail: pam_end(pam_h, rc); #endif return pass; }
static void create_watching_parent (void) { pid_t child; sigset_t ourset; struct sigaction oldact[3]; int status = 0; int retval; retval = pam_open_session (pamh, 0); if (is_pam_failure(retval)) { cleanup_pam (retval); errx (EXIT_FAILURE, _("cannot open session: %s"), pam_strerror (pamh, retval)); } else _pam_session_opened = 1; memset(oldact, 0, sizeof(oldact)); child = fork (); if (child == (pid_t) -1) { cleanup_pam (PAM_ABORT); err (EXIT_FAILURE, _("cannot create child process")); } /* the child proceeds to run the shell */ if (child == 0) return; /* In the parent watch the child. */ /* su without pam support does not have a helper that keeps sitting on any directory so let's go to /. */ if (chdir ("/") != 0) warn (_("cannot change directory to %s"), "/"); sigfillset (&ourset); if (sigprocmask (SIG_BLOCK, &ourset, NULL)) { warn (_("cannot block signals")); caught_signal = true; } if (!caught_signal) { struct sigaction action; action.sa_handler = su_catch_sig; sigemptyset (&action.sa_mask); action.sa_flags = 0; sigemptyset (&ourset); if (!same_session) { if (sigaddset(&ourset, SIGINT) || sigaddset(&ourset, SIGQUIT)) { warn (_("cannot set signal handler")); caught_signal = true; } } if (!caught_signal && (sigaddset(&ourset, SIGTERM) || sigaddset(&ourset, SIGALRM) || sigaction(SIGTERM, &action, &oldact[0]) || sigprocmask(SIG_UNBLOCK, &ourset, NULL))) { warn (_("cannot set signal handler")); caught_signal = true; } if (!caught_signal && !same_session && (sigaction(SIGINT, &action, &oldact[1]) || sigaction(SIGQUIT, &action, &oldact[2]))) { warn (_("cannot set signal handler")); caught_signal = true; } } if (!caught_signal) { pid_t pid; for (;;) { pid = waitpid (child, &status, WUNTRACED); if (pid != (pid_t)-1 && WIFSTOPPED (status)) { kill (getpid (), SIGSTOP); /* once we get here, we must have resumed */ kill (pid, SIGCONT); } else break; } if (pid != (pid_t)-1) { if (WIFSIGNALED (status)) { fprintf (stderr, "%s%s\n", strsignal (WTERMSIG (status)), WCOREDUMP (status) ? _(" (core dumped)") : ""); status = WTERMSIG (status) + 128; } else status = WEXITSTATUS (status); } else if (caught_signal) status = caught_signal + 128; else status = 1; } else status = 1; if (caught_signal) { fprintf (stderr, _("\nSession terminated, killing shell...")); kill (child, SIGTERM); } cleanup_pam (PAM_SUCCESS); if (caught_signal) { sleep (2); kill (child, SIGKILL); fprintf (stderr, _(" ...killed.\n")); /* Let's terminate itself with the received signal. * * It seems that shells use WIFSIGNALED() rather than our exit status * value to detect situations when is necessary to cleanup (reset) * terminal settings (kzak -- Jun 2013). */ switch (caught_signal) { case SIGTERM: sigaction(SIGTERM, &oldact[0], NULL); break; case SIGINT: sigaction(SIGINT, &oldact[1], NULL); break; case SIGQUIT: sigaction(SIGQUIT, &oldact[2], NULL); break; default: /* just in case that signal stuff initialization failed and * caught_signal = true */ caught_signal = SIGKILL; break; } kill(getpid(), caught_signal); } exit (status); }
static void loginpam_auth(struct login_context *cxt) { int rc, failcount = 0, show_unknown, retries; const char *hostname = cxt->hostname ? cxt->hostname : cxt->tty_name ? cxt->tty_name : "<unknown>"; pam_handle_t *pamh = cxt->pamh; /* if we didn't get a user on the command line, set it to NULL */ loginpam_get_username(pamh, &cxt->username); show_unknown = getlogindefs_bool("LOG_UNKFAIL_ENAB", 0); retries = getlogindefs_num("LOGIN_RETRIES", LOGIN_MAX_TRIES); /* * There may be better ways to deal with some of these conditions, but * at least this way I don't think we'll be giving away information... * * Perhaps someday we can trust that all PAM modules will pay attention * to failure count and get rid of LOGIN_MAX_TRIES? */ rc = pam_authenticate(pamh, 0); while ((++failcount < retries) && ((rc == PAM_AUTH_ERR) || (rc == PAM_USER_UNKNOWN) || (rc == PAM_CRED_INSUFFICIENT) || (rc == PAM_AUTHINFO_UNAVAIL))) { if (rc == PAM_USER_UNKNOWN && !show_unknown) /* * logging unknown usernames may be a security issue if * an user enter her password instead of her login name */ cxt->username = NULL; else loginpam_get_username(pamh, &cxt->username); syslog(LOG_NOTICE, _("FAILED LOGIN %d FROM %s FOR %s, %s"), failcount, hostname, cxt->username ? cxt->username : "******", pam_strerror(pamh, rc)); log_btmp(cxt); log_audit(cxt, 0); fprintf(stderr, _("Login incorrect\n\n")); pam_set_item(pamh, PAM_USER, NULL); rc = pam_authenticate(pamh, 0); } if (is_pam_failure(rc)) { if (rc == PAM_USER_UNKNOWN && !show_unknown) cxt->username = NULL; else loginpam_get_username(pamh, &cxt->username); if (rc == PAM_MAXTRIES) syslog(LOG_NOTICE, _("TOO MANY LOGIN TRIES (%d) FROM %s FOR %s, %s"), failcount, hostname, cxt->username ? cxt->username : "******", pam_strerror(pamh, rc)); else syslog(LOG_NOTICE, _("FAILED LOGIN SESSION FROM %s FOR %s, %s"), hostname, cxt->username ? cxt->username : "******", pam_strerror(pamh, rc)); log_btmp(cxt); log_audit(cxt, 0); fprintf(stderr, _("\nLogin incorrect\n")); pam_end(pamh, rc); sleepexit(EXIT_SUCCESS); } }
/* * Authentication thread. */ static void * sshpam_thread(void *ctxtp) { struct pam_ctxt *ctxt = ctxtp; Buffer buffer; struct pam_conv sshpam_conv; int flags = (options.permit_empty_passwd == 0 ? PAM_DISALLOW_NULL_AUTHTOK : 0); #ifndef UNSUPPORTED_POSIX_THREADS_HACK extern char **environ; char **env_from_pam; u_int i; const char *pam_user; const char **ptr_pam_user = &pam_user; char *tz = getenv("TZ"); sshpam_err = pam_get_item(sshpam_handle, PAM_USER, (sshpam_const void **)ptr_pam_user); if (sshpam_err != PAM_SUCCESS) goto auth_fail; environ[0] = NULL; if (tz != NULL) if (setenv("TZ", tz, 1) == -1) error("PAM: could not set TZ environment: %s", strerror(errno)); if (sshpam_authctxt != NULL) { setproctitle("%s [pam]", sshpam_authctxt->valid ? pam_user : "******"); } #endif sshpam_conv.conv = sshpam_thread_conv; sshpam_conv.appdata_ptr = ctxt; if (sshpam_authctxt == NULL) fatal("%s: PAM authctxt not initialized", __func__); buffer_init(&buffer); sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, (const void *)&sshpam_conv); if (sshpam_err != PAM_SUCCESS) goto auth_fail; sshpam_err = pam_authenticate(sshpam_handle, flags); if (sshpam_err == PAM_MAXTRIES) sshpam_set_maxtries_reached(1); if (sshpam_err != PAM_SUCCESS) goto auth_fail; if (compat20) { if (!do_pam_account()) { sshpam_err = PAM_ACCT_EXPIRED; goto auth_fail; } if (sshpam_authctxt->force_pwchange) { sshpam_err = pam_chauthtok(sshpam_handle, PAM_CHANGE_EXPIRED_AUTHTOK); if (sshpam_err != PAM_SUCCESS) goto auth_fail; sshpam_password_change_required(0); } } buffer_put_cstring(&buffer, "OK"); #ifndef UNSUPPORTED_POSIX_THREADS_HACK /* Export variables set by do_pam_account */ buffer_put_int(&buffer, sshpam_account_status); buffer_put_int(&buffer, sshpam_authctxt->force_pwchange); /* Export any environment strings set in child */ for(i = 0; environ[i] != NULL; i++) ; /* Count */ buffer_put_int(&buffer, i); for(i = 0; environ[i] != NULL; i++) buffer_put_cstring(&buffer, environ[i]); /* Export any environment strings set by PAM in child */ env_from_pam = pam_getenvlist(sshpam_handle); for(i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++) ; /* Count */ buffer_put_int(&buffer, i); for(i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++) buffer_put_cstring(&buffer, env_from_pam[i]); #endif /* UNSUPPORTED_POSIX_THREADS_HACK */ /* XXX - can't do much about an error here */ ssh_msg_send(ctxt->pam_csock, sshpam_err, &buffer); buffer_free(&buffer); pthread_exit(NULL); auth_fail: buffer_put_cstring(&buffer, pam_strerror(sshpam_handle, sshpam_err)); /* XXX - can't do much about an error here */ if (sshpam_err == PAM_ACCT_EXPIRED) ssh_msg_send(ctxt->pam_csock, PAM_ACCT_EXPIRED, &buffer); else if (sshpam_maxtries_reached) ssh_msg_send(ctxt->pam_csock, PAM_MAXTRIES, &buffer); else ssh_msg_send(ctxt->pam_csock, PAM_AUTH_ERR, &buffer); buffer_free(&buffer); pthread_exit(NULL); return (NULL); /* Avoid warning for non-pthread case */ }
PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) { int ctrl; struct module_options options; memset(&options, 0, sizeof(options)); options.retry_times = CO_RETRY_TIMES; ctrl = _pam_parse(pamh, &options, argc, argv); if (ctrl < 0) return PAM_BUF_ERR; if (flags & PAM_PRELIM_CHECK) { /* Check for passwd dictionary * We cannot do that, since the original path is compiled * into the cracklib library and we don't know it. */ return PAM_SUCCESS; } else if (flags & PAM_UPDATE_AUTHTOK) { int retval; const void *oldtoken; const char *user; int tries; retval = pam_get_user(pamh, &user, NULL); if (retval != PAM_SUCCESS || user == NULL) { if (ctrl & PAM_DEBUG_ARG) pam_syslog(pamh, LOG_ERR, "Can not get username"); return PAM_AUTHTOK_ERR; } retval = pam_get_item(pamh, PAM_OLDAUTHTOK, &oldtoken); if (retval != PAM_SUCCESS) { if (ctrl & PAM_DEBUG_ARG) pam_syslog(pamh, LOG_ERR, "Can not get old passwd"); oldtoken = NULL; } tries = 0; while (tries < options.retry_times) { void *auxerror; const char *newtoken = NULL; tries++; /* Planned modus operandi: * Get a passwd. * Verify it against libpwquality. * If okay get it a second time. * Check to be the same with the first one. * set PAM_AUTHTOK and return */ retval = pam_get_authtok_noverify(pamh, &newtoken, NULL); if (retval != PAM_SUCCESS) { pam_syslog(pamh, LOG_ERR, "pam_get_authtok_noverify returned error: %s", pam_strerror(pamh, retval)); continue; } else if (newtoken == NULL) { /* user aborted password change, quit */ return PAM_AUTHTOK_ERR; } /* now test this passwd against libpwquality */ retval = pwquality_check(options.pwq, newtoken, oldtoken, user, &auxerror); if (retval < 0) { const char *msg; char buf[PWQ_MAX_ERROR_MESSAGE_LEN]; msg = pwquality_strerror(buf, sizeof(buf), retval, auxerror); if (ctrl & PAM_DEBUG_ARG) pam_syslog(pamh, LOG_DEBUG, "bad password: %s", msg); pam_error(pamh, _("BAD PASSWORD: %s"), msg); if (getuid() || options.enforce_for_root || (flags & PAM_CHANGE_EXPIRED_AUTHTOK)) { pam_set_item(pamh, PAM_AUTHTOK, NULL); retval = PAM_AUTHTOK_ERR; continue; } } else { if (ctrl & PAM_DEBUG_ARG) pam_syslog(pamh, LOG_DEBUG, "password score: %d", retval); } retval = pam_get_authtok_verify(pamh, &newtoken, NULL); if (retval != PAM_SUCCESS) { pam_syslog(pamh, LOG_ERR, "pam_get_authtok_verify returned error: %s", pam_strerror(pamh, retval)); pam_set_item(pamh, PAM_AUTHTOK, NULL); continue; } else if (newtoken == NULL) { /* user aborted password change, quit */ return PAM_AUTHTOK_ERR; } return PAM_SUCCESS; } pam_set_item (pamh, PAM_AUTHTOK, NULL); /* if we have only one try, we can use the real reason, * else say that there were too many tries. */ if (options.retry_times > 1) return PAM_MAXTRIES; else return retval; } else { if (ctrl & PAM_DEBUG_ARG) pam_syslog(pamh, LOG_NOTICE, "UNKNOWN flags setting %02X",flags); } return PAM_SERVICE_ERR; }
/* * Check authentication against PAM. */ static int CheckPAMAuth(Port *port, char *user, char *password) { int retval; pam_handle_t *pamh = NULL; /* * Apparently, Solaris 2.6 is broken, and needs ugly static variable * workaround */ pam_passwd = password; /* * Set the application data portion of the conversation struct This is * later used inside the PAM conversation to pass the password to the * authentication module. */ pam_passw_conv.appdata_ptr = (char *) password; /* from password above, * not allocated */ /* Optionally, one can set the service name in pg_hba.conf */ if (port->auth_arg && port->auth_arg[0] != '\0') retval = pam_start(port->auth_arg, "pgsql@", &pam_passw_conv, &pamh); else retval = pam_start(PGSQL_PAM_SERVICE, "pgsql@", &pam_passw_conv, &pamh); if (retval != PAM_SUCCESS) { ereport(LOG, (errmsg("could not create PAM authenticator: %s", pam_strerror(pamh, retval)))); pam_passwd = NULL; /* Unset pam_passwd */ return STATUS_ERROR; } retval = pam_set_item(pamh, PAM_USER, user); if (retval != PAM_SUCCESS) { ereport(LOG, (errmsg("pam_set_item(PAM_USER) failed: %s", pam_strerror(pamh, retval)))); pam_passwd = NULL; /* Unset pam_passwd */ return STATUS_ERROR; } retval = pam_set_item(pamh, PAM_CONV, &pam_passw_conv); if (retval != PAM_SUCCESS) { ereport(LOG, (errmsg("pam_set_item(PAM_CONV) failed: %s", pam_strerror(pamh, retval)))); pam_passwd = NULL; /* Unset pam_passwd */ return STATUS_ERROR; } retval = pam_authenticate(pamh, 0); if (retval != PAM_SUCCESS) { ereport(LOG, (errmsg("pam_authenticate failed: %s", pam_strerror(pamh, retval)))); pam_passwd = NULL; /* Unset pam_passwd */ return STATUS_ERROR; } retval = pam_acct_mgmt(pamh, 0); if (retval != PAM_SUCCESS) { ereport(LOG, (errmsg("pam_acct_mgmt failed: %s", pam_strerror(pamh, retval)))); pam_passwd = NULL; /* Unset pam_passwd */ return STATUS_ERROR; } retval = pam_end(pamh, retval); if (retval != PAM_SUCCESS) { ereport(LOG, (errmsg("could not release PAM authenticator: %s", pam_strerror(pamh, retval)))); } pam_passwd = NULL; /* Unset pam_passwd */ return (retval == PAM_SUCCESS ? STATUS_OK : STATUS_ERROR); }
int userAuth(const char *uname,char *password) { int ofd; pam_handle_t *pamh; int pam_status; int ret; char t_uname[256]; struct passwd *p1; strcpy(t_uname,uname); printf("******userAuth.c******************************\n"); printf("UserName:%s and password:%s\n",uname,password); printf("**********************************************\n"); /* quick fix password is stored in flat file and deleted after performing the action */ if ( (ofd=open("/tmp/passfile",O_WRONLY|O_CREAT,S_IRWXU)) < 0 ) { perror("open "); exit(0); } write(ofd,password,strlen(password)); close(ofd); dup2(0,10); close(0); if ( (ofd=open("/tmp/passfile",O_RDONLY)) < 0 ) { perror("reading password file "); printf("uid of this process...:%d\n",getuid()); exit(0); } /* PAM AUTHENTICATION MODULE */ pam_status = pam_start("lsb_login",NULL, &conv, &pamh); pam_status = pam_set_item(pamh,PAM_USER,uname); if (pam_status == PAM_SUCCESS) pam_status = pam_authenticate(pamh, PAM_SILENT); printf("pam_authenticate call done...\n"); if (pam_status == PAM_SUCCESS) { pam_get_item(pamh, PAM_USER, (const void **)&uname); fprintf(stdout, "Greetings %s\n", uname); ret = 1; } else { printf("%s\n", pam_strerror(pamh, pam_status)); ret = 0; } pam_end(pamh, pam_status); /*Finally remove the temp pass file */ unlink("/tmp/passfile"); close(ofd); dup2(10,0); if ( ret == 0) //authentication failed return -1; else { //authentication success /* get the userid for this user and return this val */ printf("Getting userid...\n"); if ( (p1=getpwnam(t_uname)) != NULL ) { printf("Userid for %s is :%d\n",t_uname,p1->pw_uid); return(p1->pw_uid); } } return -1; }
/* * main - userdel command */ int main (int argc, char **argv) { int errors = 0; /* Error in the removal of the home directory */ #ifdef ACCT_TOOLS_SETUID #ifdef USE_PAM pam_handle_t *pamh = NULL; int retval; #endif /* USE_PAM */ #endif /* ACCT_TOOLS_SETUID */ /* * Get my name so that I can use it to report errors. */ Prog = Basename (argv[0]); (void) setlocale (LC_ALL, ""); (void) bindtextdomain (PACKAGE, LOCALEDIR); (void) textdomain (PACKAGE); process_root_flag ("-R", argc, argv); OPENLOG ("userdel"); #ifdef WITH_AUDIT audit_help_open (); #endif /* WITH_AUDIT */ { /* * Parse the command line options. */ int c; static struct option long_options[] = { {"force", no_argument, NULL, 'f'}, {"help", no_argument, NULL, 'h'}, {"remove", no_argument, NULL, 'r'}, {"root", required_argument, NULL, 'R'}, #ifdef WITH_SELINUX {"selinux-user", no_argument, NULL, 'Z'}, #endif /* WITH_SELINUX */ {NULL, 0, NULL, '\0'} }; while ((c = getopt_long (argc, argv, #ifdef WITH_SELINUX "fhrR:Z", #else /* !WITH_SELINUX */ "fhrR:", #endif /* !WITH_SELINUX */ long_options, NULL)) != -1) { switch (c) { case 'f': /* force remove even if not owned by user */ fflg = true; break; case 'h': usage (E_SUCCESS); break; case 'r': /* remove home dir and mailbox */ rflg = true; break; case 'R': /* no-op, handled in process_root_flag () */ break; #ifdef WITH_SELINUX case 'Z': if (is_selinux_enabled () > 0) { Zflg = true; } else { fprintf (stderr, _("%s: -Z requires SELinux enabled kernel\n"), Prog); exit (E_BAD_ARG); } break; #endif /* WITH_SELINUX */ default: usage (E_USAGE); } } } if ((optind + 1) != argc) { usage (E_USAGE); } #ifdef ACCT_TOOLS_SETUID #ifdef USE_PAM { struct passwd *pampw; pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */ if (pampw == NULL) { fprintf (stderr, _("%s: Cannot determine your user name.\n"), Prog); exit (E_PW_UPDATE); } retval = pam_start ("userdel", pampw->pw_name, &conv, &pamh); } if (PAM_SUCCESS == retval) { retval = pam_authenticate (pamh, 0); } if (PAM_SUCCESS == retval) { retval = pam_acct_mgmt (pamh, 0); } if (PAM_SUCCESS != retval) { fprintf (stderr, _("%s: PAM: %s\n"), Prog, pam_strerror (pamh, retval)); SYSLOG((LOG_ERR, "%s", pam_strerror (pamh, retval))); if (NULL != pamh) { (void) pam_end (pamh, retval); } exit (E_PW_UPDATE); } (void) pam_end (pamh, retval); #endif /* USE_PAM */ #endif /* ACCT_TOOLS_SETUID */ is_shadow_pwd = spw_file_present (); #ifdef SHADOWGRP is_shadow_grp = sgr_file_present (); #endif /* SHADOWGRP */ #ifdef ENABLE_SUBIDS is_sub_uid = sub_uid_file_present (); is_sub_gid = sub_gid_file_present (); #endif /* ENABLE_SUBIDS */ /* * Start with a quick check to see if the user exists. */ user_name = argv[argc - 1]; { struct passwd *pwd; pwd = getpwnam (user_name); /* local, no need for xgetpwnam */ if (NULL == pwd) { fprintf (stderr, _("%s: user '%s' does not exist\n"), Prog, user_name); #ifdef WITH_AUDIT audit_logger (AUDIT_DEL_USER, Prog, "deleting user not found", user_name, AUDIT_NO_ID, SHADOW_AUDIT_FAILURE); #endif /* WITH_AUDIT */ exit (E_NOTFOUND); } user_id = pwd->pw_uid; user_gid = pwd->pw_gid; user_home = xstrdup (pwd->pw_dir); } #ifdef WITH_TCB if (shadowtcb_set_user (user_name) == SHADOWTCB_FAILURE) { exit (E_NOTFOUND); } #endif /* WITH_TCB */ #ifdef USE_NIS /* * Now make sure it isn't an NIS user. */ if (__ispwNIS ()) { char *nis_domain; char *nis_master; fprintf (stderr, _("%s: user %s is a NIS user\n"), Prog, user_name); if ( !yp_get_default_domain (&nis_domain) && !yp_master (nis_domain, "passwd.byname", &nis_master)) { fprintf (stderr, _("%s: %s is the NIS master\n"), Prog, nis_master); } exit (E_NOTFOUND); } #endif /* USE_NIS */ /* * Check to make certain the user isn't logged in. * Note: This is a best effort basis. The user may log in between, * a cron job may be started on her behalf, etc. */ if (user_busy (user_name, user_id) != 0) { if (!fflg) { #ifdef WITH_AUDIT audit_logger (AUDIT_DEL_USER, Prog, "deleting user logged in", user_name, AUDIT_NO_ID, SHADOW_AUDIT_FAILURE); #endif /* WITH_AUDIT */ exit (E_USER_BUSY); } } /* * Do the hard stuff - open the files, create the user entries, * create the home directory, then close and update the files. */ open_files (); update_user (); update_groups (); if (rflg) { errors += remove_mailbox (); } if (rflg) { int home_owned = is_owner (user_id, user_home); if (-1 == home_owned) { fprintf (stderr, _("%s: %s home directory (%s) not found\n"), Prog, user_name, user_home); rflg = 0; } else if ((0 == home_owned) && !fflg) { fprintf (stderr, _("%s: %s not owned by %s, not removing\n"), Prog, user_home, user_name); rflg = 0; errors++; /* continue */ } } #ifdef EXTRA_CHECK_HOME_DIR /* This may be slow, the above should be good enough. */ if (rflg && !fflg) { struct passwd *pwd; /* * For safety, refuse to remove the home directory if it * would result in removing some other user's home * directory. Still not perfect so be careful, but should * prevent accidents if someone has /home or / as home * directory... --marekm */ setpwent (); while ((pwd = getpwent ())) { if (strcmp (pwd->pw_name, user_name) == 0) { continue; } if (path_prefix (user_home, pwd->pw_dir)) { fprintf (stderr, _("%s: not removing directory %s (would remove home of user %s)\n"), Prog, user_home, pwd->pw_name); rflg = false; errors++; /* continue */ break; } } endpwent (); } #endif /* EXTRA_CHECK_HOME_DIR */ if (rflg) { if (remove_tree (user_home, true) != 0) { fprintf (stderr, _("%s: error removing directory %s\n"), Prog, user_home); errors++; /* continue */ } #ifdef WITH_AUDIT else { audit_logger (AUDIT_DEL_USER, Prog, "deleting home directory", user_name, (unsigned int) user_id, SHADOW_AUDIT_SUCCESS); } #endif /* WITH_AUDIT */ } #ifdef WITH_AUDIT if (0 != errors) { audit_logger (AUDIT_DEL_USER, Prog, "deleting home directory", user_name, AUDIT_NO_ID, SHADOW_AUDIT_FAILURE); } #endif /* WITH_AUDIT */ #ifdef WITH_SELINUX if (Zflg) { if (del_seuser (user_name) != 0) { fprintf (stderr, _("%s: warning: the user name %s to SELinux user mapping removal failed.\n"), Prog, user_name); #ifdef WITH_AUDIT audit_logger (AUDIT_ADD_USER, Prog, "removing SELinux user mapping", user_name, (unsigned int) user_id, SHADOW_AUDIT_FAILURE); #endif /* WITH_AUDIT */ fail_exit (E_SE_UPDATE); } } #endif /* WITH_SELINUX */ /* * Cancel any crontabs or at jobs. Have to do this before we remove * the entry from /etc/passwd. */ user_cancel (user_name); close_files (); #ifdef WITH_TCB errors += remove_tcbdir (user_name, user_id); #endif /* WITH_TCB */ nscd_flush_cache ("passwd"); nscd_flush_cache ("group"); return ((0 != errors) ? E_HOMEDIR : E_SUCCESS); }
static void run_file(const char *filename, uid_t uid, gid_t gid) { /* Run a file by spawning off a process which redirects I/O, * spawns a subshell, then waits for it to complete and sends * mail to the user. */ pid_t pid; int fd_out, fd_in; int queue; char mailbuf[LOGNAMESIZE + 1], fmt[49]; char *mailname = NULL; FILE *stream; int send_mail = 0; struct stat buf, lbuf; off_t size; struct passwd *pentry; int fflags; uid_t nuid; gid_t ngid; #ifdef PAM pam_handle_t *pamh = NULL; int pam_err; struct pam_conv pamc = { .conv = openpam_nullconv, .appdata_ptr = NULL }; #endif PRIV_START if (chmod(filename, S_IRUSR) != 0) { perr("cannot change file permissions"); } PRIV_END pid = fork(); if (pid == -1) perr("cannot fork"); else if (pid != 0) return; /* Let's see who we mail to. Hopefully, we can read it from * the command file; if not, send it to the owner, or, failing that, * to root. */ pentry = getpwuid(uid); if (pentry == NULL) perrx("Userid %lu not found - aborting job %s", (unsigned long) uid, filename); #ifdef PAM PRIV_START pam_err = pam_start(atrun, pentry->pw_name, &pamc, &pamh); if (pam_err != PAM_SUCCESS) perrx("cannot start PAM: %s", pam_strerror(pamh, pam_err)); pam_err = pam_acct_mgmt(pamh, PAM_SILENT); /* Expired password shouldn't prevent the job from running. */ if (pam_err != PAM_SUCCESS && pam_err != PAM_NEW_AUTHTOK_REQD) perrx("Account %s (userid %lu) unavailable for job %s: %s", pentry->pw_name, (unsigned long)uid, filename, pam_strerror(pamh, pam_err)); pam_end(pamh, pam_err); PRIV_END #endif /* PAM */ PRIV_START stream=fopen(filename, "r"); PRIV_END if (stream == NULL) perr("cannot open input file"); if ((fd_in = dup(fileno(stream))) <0) perr("error duplicating input file descriptor"); if (fstat(fd_in, &buf) == -1) perr("error in fstat of input file descriptor"); if (lstat(filename, &lbuf) == -1) perr("error in fstat of input file"); if (S_ISLNK(lbuf.st_mode)) perrx("Symbolic link encountered in job %s - aborting", filename); if ((lbuf.st_dev != buf.st_dev) || (lbuf.st_ino != buf.st_ino) || (lbuf.st_uid != buf.st_uid) || (lbuf.st_gid != buf.st_gid) || (lbuf.st_size!=buf.st_size)) perrx("Somebody changed files from under us for job %s - aborting", filename); if (buf.st_nlink > 1) perrx("Somebody is trying to run a linked script for job %s", filename); if ((fflags = fcntl(fd_in, F_GETFD)) <0) perr("error in fcntl"); fcntl(fd_in, F_SETFD, fflags & ~FD_CLOEXEC); snprintf(fmt, sizeof(fmt), "#!/bin/sh\n# atrun uid=%%ld gid=%%ld\n# mail %%%ds %%d", LOGNAMESIZE); if (fscanf(stream, fmt, &nuid, &ngid, mailbuf, &send_mail) != 4) perrx("File %s is in wrong format - aborting", filename); if (mailbuf[0] == '-') perrx("Illegal mail name %s in %s", mailbuf, filename); mailname = mailbuf; if (nuid != uid) perrx("Job %s - userid %u does not match file uid %u", filename, nuid, uid); if (ngid != gid) perrx("Job %s - groupid %u does not match file gid %u", filename, ngid, gid); fclose(stream); if (chdir(ATSPOOL_DIR) < 0) perr("cannot chdir to %s", ATSPOOL_DIR); /* Create a file to hold the output of the job we are about to run. * Write the mail header. */ if((fd_out=open(filename, O_WRONLY | O_CREAT | O_EXCL, S_IWUSR | S_IRUSR)) < 0) perr("cannot create output file"); write_string(fd_out, "Subject: Output from your job "); write_string(fd_out, filename); write_string(fd_out, "\n\n"); fstat(fd_out, &buf); size = buf.st_size; close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); pid = fork(); if (pid < 0) perr("error in fork"); else if (pid == 0) { char *nul = NULL; char **nenvp = &nul; /* Set up things for the child; we want standard input from the input file, * and standard output and error sent to our output file. */ if (lseek(fd_in, (off_t) 0, SEEK_SET) < 0) perr("error in lseek"); if (dup(fd_in) != STDIN_FILENO) perr("error in I/O redirection"); if (dup(fd_out) != STDOUT_FILENO) perr("error in I/O redirection"); if (dup(fd_out) != STDERR_FILENO) perr("error in I/O redirection"); close(fd_in); close(fd_out); if (chdir(ATJOB_DIR) < 0) perr("cannot chdir to %s", ATJOB_DIR); queue = *filename; PRIV_START nice(tolower(queue) - 'a'); #ifdef LOGIN_CAP /* * For simplicity and safety, set all aspects of the user context * except for a selected subset: Don't set priority, which was * set based on the queue file name according to the tradition. * Don't bother to set environment, including path vars, either * because it will be discarded anyway. Although the job file * should set umask, preset it here just in case. */ if (setusercontext(NULL, pentry, uid, LOGIN_SETALL & ~(LOGIN_SETPRIORITY | LOGIN_SETPATH | LOGIN_SETENV)) != 0) exit(EXIT_FAILURE); /* setusercontext() logged the error */ #else /* LOGIN_CAP */ if (initgroups(pentry->pw_name,pentry->pw_gid)) perr("cannot init group access list"); if (setgid(gid) < 0 || setegid(pentry->pw_gid) < 0) perr("cannot change group"); if (setlogin(pentry->pw_name)) perr("cannot set login name"); if (setuid(uid) < 0 || seteuid(uid) < 0) perr("cannot set user id"); #endif /* LOGIN_CAP */ if (chdir(pentry->pw_dir)) chdir("/"); if(execle("/bin/sh","sh",NULL, nenvp) != 0) perr("exec failed for /bin/sh"); PRIV_END }
PAM_EXTERN int pam_sm_authenticate(pam_handle_t * pamh, int flags, int argc, const char **argv) { const char *user = NULL, *passwd = NULL; struct passwd *pwd; int rval, status; pid_t pid; // For checking mount paths: (mount from + target) char path[PATH_MAX]; char targetpath[PATH_MAX]; char encfs_options[USERNAME_MAX]; char fuse_options[USERNAME_MAX]; char *targetpath_store; strcpy(default_encfs_options, ""); strcpy(default_fuse_options, ""); // For execing: char *arg[USERNAME_MAX]; int arg_pos = 0; int i; int inpipe[2], outpipe[2]; rval = pam_get_user(pamh, &user, NULL); if ((rval != PAM_SUCCESS) || (!user)) { _pam_log(LOG_ERR, "can't get username: %s", pam_strerror(pamh, rval)); return PAM_AUTH_ERR; } rval = pam_get_item(pamh, PAM_AUTHTOK, (const void **) (void *) &passwd); if (rval != PAM_SUCCESS) { _pam_log(LOG_ERR, "Could not retrieve user's password"); return PAM_AUTH_ERR; } if (!passwd) { rval = _set_auth_tok(pamh, flags, argc, argv); if (rval != PAM_SUCCESS) { return rval; } rval = pam_get_item(pamh, PAM_AUTHTOK, (const void **) (void *) &passwd); if (rval != PAM_SUCCESS || passwd == NULL) { _pam_log(LOG_ERR, "Could not retrieve user's password"); return PAM_AUTH_ERR; } } if ((pwd = getpwnam(user)) == NULL) { _pam_log(LOG_ERR, "Could not getpwnam"); return PAM_AUTH_ERR; } // Read configfile if (!readconfig (pwd, pamh, pwd->pw_name, path, targetpath, encfs_options, fuse_options)) { // DEBUG _pam_log(LOG_ERR,"No entry for user found in log"); return PAM_IGNORE; } //DEBUG _pam_log(LOG_ERR,"Username : %s, Encpath : %s, Targetmount : %s",pwd->pw_name,path,targetpath); //Store targetpath targetpath_store = strdup(targetpath); if ((i = pam_set_data(pamh, "encfs_targetpath", targetpath_store, targetpath_cleanup)) != PAM_SUCCESS) { _pam_log(LOG_ERR, "Storing targetpath FAIL"); free(targetpath_store); return i; } // Check if we're mounted already. if (checkmnt(targetpath)) { //DEBUG _pam_log(LOG_ERR,"Already mounted"); return PAM_IGNORE; } /* _pam_log(LOG_ERR,"Config output for %s:",user); _pam_log(LOG_ERR," path : %s",path); _pam_log(LOG_ERR," targetpath : %s",targetpath); _pam_log(LOG_ERR," encfs : %s %s",default_encfs_options,encfs_options); _pam_log(LOG_ERR," fuse : %s %s",default_fuse_options,fuse_options); */ arg_pos += buildCmd(arg, arg_pos, "encfs"); arg_pos += buildCmd(arg, arg_pos, "-S"); arg_pos += buildCmd(arg, arg_pos, default_encfs_options); arg_pos += buildCmd(arg, arg_pos, encfs_options); arg_pos += buildCmd(arg, arg_pos, path); arg_pos += buildCmd(arg, arg_pos, targetpath); if (strlen(default_fuse_options) > 0 && strlen(fuse_options) > 0) strcat(fuse_options, ","); strcat(fuse_options,default_fuse_options); if (strlen(fuse_options) > 0) { arg_pos += buildCmd(arg, arg_pos, "--"); arg_pos += buildCmd(arg, arg_pos, "-o"); arg_pos += buildCmd(arg, arg_pos, fuse_options); } arg[arg_pos] = NULL; /* printf("Arguments : "); for (i = 0; i < arg_pos+1;i++) { _pam_log(LOG_ERR,"Data : %s",arg[i]); } _pam_log(LOG_ERR,"Number of arguments : %d",arg_pos); */ /* arg[0] = cmd; arg[1] = params; // arg[2] = params2; arg[2] = params3; arg[3] = path; arg[4] = targetpath; arg[5] = fuseparams; arg[6] = fuseparams2; arg[7] = NULL; */ if (pipe(inpipe) || pipe(outpipe)) { _pam_log(LOG_ERR, "Failed to create pipe"); return PAM_IGNORE; } // Execute switch (pid = fork()) { case -1: _pam_log(LOG_ERR, "Fork failed"); return PAM_SERVICE_ERR; case 0: if (drop_permissions == 1) if ((initgroups(pwd->pw_name, pwd->pw_gid) == -1) || (setgid(pwd->pw_gid) == -1) || (setuid(pwd->pw_uid) == -1)) { _pam_log(LOG_ERR, "Dropping permissions failed"); return PAM_SERVICE_ERR; } close(outpipe[WRITE_END]); dup2(outpipe[READ_END], fileno(stdin)); close(outpipe[READ_END]); close(inpipe[READ_END]); dup2(inpipe[WRITE_END], fileno(stdout)); close(inpipe[WRITE_END]); // For some reason the current directory has to be set to targetpath (or path?) before exec'ing encfs through gdm chdir(targetpath); execvp("encfs", arg); char errstr[128]; snprintf(errstr, 127, "%d - %s", errno, strerror(errno)); _pam_log(LOG_ERR, "Exec failed - %s", errstr); exit(127); } int len; close(inpipe[WRITE_END]); close(outpipe[READ_END]); if (waitpid(pid, &status, WNOHANG) == 0) { len = write(outpipe[WRITE_END], passwd, (size_t) strlen(passwd)); if ((len != (size_t) strlen(passwd)) || (write(outpipe[WRITE_END], "\n", 1) != 1)) _pam_log(LOG_ERR, "Did not send password to pipe (%d sent)", len); close(outpipe[WRITE_END]); } if (waitpid_timeout(pid, &status, 0)) { _pam_log(LOG_ERR, "Timed out waiting for encfs, killing\n"); kill(pid, SIGKILL); } int exitstatus = WEXITSTATUS(status); char buff[512]; len = read(inpipe[READ_END], &buff, 511); close(inpipe[READ_END]); buff[len] = 0; if (!checkmnt(targetpath) && (len > 0 || exitstatus > 0)) { _pam_log(LOG_ERR, "exitcode : %d, errorstring : %s", exitstatus, buff); return PAM_AUTH_ERR; } else { return PAM_IGNORE; } return PAM_AUTH_ERR; }
static int _pam_dispatch_aux(pam_handle_t *pamh, int flags, struct handler *h, _pam_boolean resumed, int use_cached_chain) { int depth, impression, status, skip_depth; IF_NO_PAMH("_pam_dispatch_aux", pamh, PAM_SYSTEM_ERR); if (h == NULL) { const char *service=NULL; (void) pam_get_item(pamh, PAM_SERVICE, (const void **)&service); _pam_system_log(LOG_ERR, "no modules loaded for `%s' service", service ? service:"<unknown>" ); service = NULL; return PAM_MUST_FAIL_CODE; } /* if we are recalling this module stack because a former call did not complete, we restore the state of play from pamh. */ if (resumed) { skip_depth = pamh->former.depth; status = pamh->former.status; impression = pamh->former.impression; /* forget all that */ pamh->former.impression = _PAM_UNDEF; pamh->former.status = PAM_MUST_FAIL_CODE; pamh->former.depth = 0; } else { skip_depth = 0; impression = _PAM_UNDEF; status = PAM_MUST_FAIL_CODE; } /* Loop through module logic stack */ for (depth=0 ; h != NULL ; h = h->next, ++depth) { int retval, cached_retval, action; /* skip leading modules if they have already returned */ if (depth < skip_depth) { continue; } /* attempt to call the module */ if (h->func == NULL) { D(("module function is not defined, indicating failure")); retval = PAM_MODULE_UNKNOWN; } else { D(("passing control to module...")); retval = h->func(pamh, flags, h->argc, h->argv); D(("module returned: %s", pam_strerror(pamh, retval))); if (h->must_fail) { D(("module poorly listed in PAM config; forcing failure")); retval = PAM_MUST_FAIL_CODE; } } /* * PAM_INCOMPLETE return is special. It indicates that the * module wants to wait for the application before continuing. * In order to return this, the module will have saved its * state so it can resume from an equivalent position when it * is called next time. (This was added as of 0.65) */ if (retval == PAM_INCOMPLETE) { pamh->former.impression = impression; pamh->former.status = status; pamh->former.depth = depth; D(("module %d returned PAM_INCOMPLETE", depth)); return retval; } if (use_cached_chain) { /* a former stack execution has frozen the chain */ cached_retval = *(h->cached_retval_p); } else { /* this stack execution is defining the frozen chain */ cached_retval = h->cached_retval = retval; } /* verify that the return value is a valid one */ if ((cached_retval < PAM_SUCCESS) || (cached_retval >= _PAM_RETURN_VALUES)) { retval = PAM_MUST_FAIL_CODE; action = _PAM_ACTION_BAD; } else { /* We treat the current retval with some respect. It may (for example, in the case of setcred) have a value that needs to be propagated to the user. We want to use the cached_retval to determine the modules to be executed in the stacked chain, but we want to treat each non-ignored module in the cached chain as now being 'required'. We only need to treat the, _PAM_ACTION_IGNORE, _PAM_ACTION_IS_JUMP and _PAM_ACTION_RESET actions specially. */ action = h->actions[cached_retval]; } D((stderr, "use_cached_chain=%d action=%d cached_retval=%d retval=%d\n", use_cached_chain, action, cached_retval, retval)); /* decide what to do */ switch (action) { case _PAM_ACTION_RESET: /* if (use_cached_chain) { XXX - we need to consider the use_cached_chain case do we want to trash accumulated info here..? } */ impression = _PAM_UNDEF; status = PAM_MUST_FAIL_CODE; break; case _PAM_ACTION_OK: case _PAM_ACTION_DONE: /* XXX - should we maintain cached_status and status in the case of use_cached_chain? The same with BAD&DIE below */ if ( impression == _PAM_UNDEF || (impression == _PAM_POSITIVE && status == PAM_SUCCESS) ) { impression = _PAM_POSITIVE; status = retval; } if ( impression == _PAM_POSITIVE && action == _PAM_ACTION_DONE ) { goto decision_made; } break; case _PAM_ACTION_BAD: case _PAM_ACTION_DIE: #ifdef PAM_FAIL_NOW_ON if ( cached_retval == PAM_ABORT ) { impression = _PAM_NEGATIVE; status = PAM_PERM_DENIED; goto decision_made; } #endif /* PAM_FAIL_NOW_ON */ if ( impression != _PAM_NEGATIVE ) { impression = _PAM_NEGATIVE; status = retval; } if ( action == _PAM_ACTION_DIE ) { goto decision_made; } break; case _PAM_ACTION_IGNORE: /* if (use_cached_chain) { XXX - when evaluating a cached chain, do we still want to ignore the module's return value? } */ break; /* if we get here, we expect action is a positive number -- this is what the ...JUMP macro checks. */ default: if ( _PAM_ACTION_IS_JUMP(action) ) { /* If we are evaluating a cached chain, we treat this module as required (aka _PAM_ACTION_OK) as well as executing the jump. */ if (use_cached_chain) { if (impression == _PAM_UNDEF || (impression == _PAM_POSITIVE && status == PAM_SUCCESS) ) { impression = _PAM_POSITIVE; status = retval; } } /* this means that we need to skip #action stacked modules */ do { h = h->next; } while ( --action > 0 && h != NULL ); /* note if we try to skip too many modules action is still non-zero and we snag the next if. */ } /* this case is a syntax error: we can't succeed */ if (action) { D(("action syntax error")); impression = _PAM_NEGATIVE; status = PAM_MUST_FAIL_CODE; } } } decision_made: /* by getting here we have made a decision */ /* Sanity check */ if ( status == PAM_SUCCESS && impression != _PAM_POSITIVE ) { D(("caught on sanity check -- this is probably a config error!")); status = PAM_MUST_FAIL_CODE; } /* We have made a decision about the modules executed */ return status; }
PAM_EXTERN int pam_sm_authenticate (pam_handle_t * pamh, int flags, int argc, const char **argv) { int retval, rc; const char *user = NULL; const char *password = NULL; char otp[MAX_TOKEN_ID_LEN + TOKEN_OTP_LEN + 1] = { 0 }; char otp_id[MAX_TOKEN_ID_LEN + 1] = { 0 }; int password_len = 0; int skip_bytes = 0; int valid_token = 0; struct pam_conv *conv; struct pam_message *pmsg[1], msg[1]; struct pam_response *resp; int nargs = 1; ykclient_t *ykc = NULL; struct cfg cfg_st; struct cfg *cfg = &cfg_st; /* for DBG macro */ parse_cfg (flags, argc, argv, cfg); retval = pam_get_user (pamh, &user, NULL); if (retval != PAM_SUCCESS) { DBG (("get user returned error: %s", pam_strerror (pamh, retval))); goto done; } DBG (("get user returned: %s", user)); if (cfg->mode == CHRESP) { return do_challenge_response(pamh, cfg, user); } if (cfg->try_first_pass || cfg->use_first_pass) { retval = pam_get_item (pamh, PAM_AUTHTOK, (const void **) &password); if (retval != PAM_SUCCESS) { DBG (("get password returned error: %s", pam_strerror (pamh, retval))); goto done; } DBG (("get password returned: %s", password)); } if (cfg->use_first_pass && password == NULL) { DBG (("use_first_pass set and no password, giving up")); retval = PAM_AUTH_ERR; goto done; } rc = ykclient_init (&ykc); if (rc != YKCLIENT_OK) { DBG (("ykclient_init() failed (%d): %s", rc, ykclient_strerror (rc))); retval = PAM_AUTHINFO_UNAVAIL; goto done; } rc = ykclient_set_client_b64 (ykc, cfg->client_id, cfg->client_key); if (rc != YKCLIENT_OK) { DBG (("ykclient_set_client_b64() failed (%d): %s", rc, ykclient_strerror (rc))); retval = PAM_AUTHINFO_UNAVAIL; goto done; } if (cfg->capath) ykclient_set_ca_path (ykc, cfg->capath); if (cfg->url) ykclient_set_url_template (ykc, cfg->url); if (password == NULL) { retval = pam_get_item (pamh, PAM_CONV, (const void **) &conv); if (retval != PAM_SUCCESS) { DBG (("get conv returned error: %s", pam_strerror (pamh, retval))); goto done; } pmsg[0] = &msg[0]; { const char *query_template = "Yubikey for `%s': "; size_t len = strlen (query_template) + strlen (user); size_t wrote; msg[0].msg = malloc (len); if (!msg[0].msg) { retval = PAM_BUF_ERR; goto done; } wrote = snprintf ((char *) msg[0].msg, len, query_template, user); if (wrote < 0 || wrote >= len) { retval = PAM_BUF_ERR; goto done; } } msg[0].msg_style = cfg->verbose_otp ? PAM_PROMPT_ECHO_ON : PAM_PROMPT_ECHO_OFF; resp = NULL; retval = conv->conv (nargs, (const struct pam_message **) pmsg, &resp, conv->appdata_ptr); free ((char *) msg[0].msg); if (retval != PAM_SUCCESS) { DBG (("conv returned error: %s", pam_strerror (pamh, retval))); goto done; } if (resp->resp == NULL) { DBG (("conv returned NULL passwd?")); goto done; } DBG (("conv returned %i bytes", strlen(resp->resp))); password = resp->resp; } password_len = strlen (password); if (password_len < (cfg->token_id_length + TOKEN_OTP_LEN)) { DBG (("OTP too short to be considered : %i < %i", password_len, (cfg->token_id_length + TOKEN_OTP_LEN))); retval = PAM_AUTH_ERR; goto done; } /* In case the input was systempassword+YubiKeyOTP, we want to skip over "systempassword" when copying the token_id and OTP to separate buffers */ skip_bytes = password_len - (cfg->token_id_length + TOKEN_OTP_LEN); DBG (("Skipping first %i bytes. Length is %i, token_id set to %i and token OTP always %i.", skip_bytes, password_len, cfg->token_id_length, TOKEN_OTP_LEN)); /* Copy full YubiKey output (public ID + OTP) into otp */ strncpy (otp, password + skip_bytes, sizeof (otp) - 1); /* Copy only public ID into otp_id. Destination buffer is zeroed. */ strncpy (otp_id, password + skip_bytes, cfg->token_id_length); DBG (("OTP: %s ID: %s ", otp, otp_id)); /* user entered their system password followed by generated OTP? */ if (password_len > TOKEN_OTP_LEN + cfg->token_id_length) { char *onlypasswd = strdup (password); onlypasswd[password_len - (TOKEN_OTP_LEN + cfg->token_id_length)] = '\0'; DBG (("Extracted a probable system password entered before the OTP - " "setting item PAM_AUTHTOK")); retval = pam_set_item (pamh, PAM_AUTHTOK, onlypasswd); free (onlypasswd); if (retval != PAM_SUCCESS) { DBG (("set_item returned error: %s", pam_strerror (pamh, retval))); goto done; } } else password = NULL; rc = ykclient_request (ykc, otp); DBG (("ykclient return value (%d): %s", rc, ykclient_strerror (rc))); switch (rc) { case YKCLIENT_OK: break; case YKCLIENT_BAD_OTP: case YKCLIENT_REPLAYED_OTP: retval = PAM_AUTH_ERR; goto done; default: retval = PAM_AUTHINFO_UNAVAIL; goto done; } /* authorize the user with supplied token id */ if (cfg->ldapserver != NULL || cfg->ldap_uri != NULL) valid_token = authorize_user_token_ldap (cfg, user, otp_id); else valid_token = authorize_user_token (cfg, user, otp_id); if (valid_token == 0) { DBG (("Yubikey not authorized to login as user")); retval = PAM_AUTHINFO_UNAVAIL; goto done; } retval = PAM_SUCCESS; done: if (ykc) ykclient_done (&ykc); if (cfg->alwaysok && retval != PAM_SUCCESS) { DBG (("alwaysok needed (otherwise return with %d)", retval)); retval = PAM_SUCCESS; } DBG (("done. [%s]", pam_strerror (pamh, retval))); pam_set_data (pamh, "yubico_setcred_return", (void*) (intptr_t) retval, NULL); return retval; }
static CockpitCreds * parse_auth_results (SessionLoginData *sl, GHashTable *headers, GError **error) { CockpitCreds *creds = NULL; GByteArray *buffer; GError *json_error = NULL; const gchar *pam_user; JsonObject *results; gint64 code = -1; buffer = cockpit_pipe_get_buffer (sl->auth_pipe); g_debug ("cockpit-session says: %.*s", (int)buffer->len, (const gchar *)buffer->data); results = cockpit_json_parse_object ((const gchar *)buffer->data, buffer->len, &json_error); if (g_error_matches (json_error, JSON_PARSER_ERROR, JSON_PARSER_ERROR_INVALID_DATA)) { g_message ("got non-utf8 user name from cockpit-session"); g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Login user name is not UTF8 encoded"); g_error_free (json_error); } else if (!results) { g_warning ("couldn't parse session auth output: %s", json_error ? json_error->message : NULL); g_error_free (json_error); g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Invalid data from session process: no results"); } else if (!cockpit_json_get_int (results, "result-code", -1, &code) || code < 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Invalid data from session process: bad PAM result"); } else if (code == PAM_SUCCESS) { if (!cockpit_json_get_string (results, "user", NULL, &pam_user) || !pam_user) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Invalid data from session process: missing user"); } else { g_debug ("user authenticated as %s", pam_user); creds = create_creds_for_authenticated (pam_user, sl, results); } } else if (code == PAM_AUTH_ERR || code == PAM_USER_UNKNOWN) { g_debug ("authentication failed: %d", (int)code); g_set_error (error, COCKPIT_ERROR, COCKPIT_ERROR_AUTHENTICATION_FAILED, "Authentication failed"); } else if (code == PAM_PERM_DENIED) { g_debug ("permission denied: %d", (int)code); g_set_error (error, COCKPIT_ERROR, COCKPIT_ERROR_PERMISSION_DENIED, "Permission denied"); } else { g_debug ("pam error: %d", (int)code); g_set_error (error, COCKPIT_ERROR, COCKPIT_ERROR_FAILED, "%s", pam_strerror (NULL, code)); } build_gssapi_output_header (headers, results); if (results) json_object_unref (results); return creds; }
int pam_authenticate(pam_handle_t *pamh, int flags) { int retval; D(("pam_authenticate called")); IF_NO_PAMH("pam_authenticate", pamh, PAM_SYSTEM_ERR); if (__PAM_FROM_MODULE(pamh)) { D(("called from module!?")); return PAM_SYSTEM_ERR; } if (pamh->former.choice == PAM_NOT_STACKED) { _pam_sanitize(pamh); _pam_start_timer(pamh); /* we try to make the time for a failure independent of the time it takes to fail */ } retval = _pam_dispatch(pamh, flags, PAM_AUTHENTICATE); if (retval != PAM_INCOMPLETE) { _pam_sanitize(pamh); _pam_await_timer(pamh, retval); /* if unsuccessful then wait now */ D(("pam_authenticate exit")); } else { D(("will resume when ready")); } #ifdef PRELUDE prelude_send_alert(pamh, retval); #endif #if HAVE_LIBAUDIT retval = _pam_auditlog(pamh, PAM_AUTHENTICATE, retval, flags); #endif #ifdef CONFIG_PROP_STATSD_STATSD if (retval != PAM_SUCCESS) { char usr[MAX_PAM_STATS_USR_SIZE]; char buf[MAX_PAM_STATS_BUF_SIZE]; struct pam_data *data; char *u = NULL; /* The pam_sg module has stored module data so we * can tell whether this is a valid user. If not * we log stats under "unknown". The proper mechanism * for accessing module data bars access from within * application code so we are going around it. This is * a kludge, but the best one possible for now. */ data = pamh->data; while (data) { if ((!strcmp((data->name)+3, "null")) || (!strcmp((data->name)+3, pamh->user))) { u = (char *)(data->data); break; } data = data->next; } /* Don't log stats if the module info is unavailable * or the PAM system itself failed during auth */ if ((u != NULL) && strcmp(u, "PAM_SYSTEM_ERR")) { u = (!strcmp(u, "PAM_USER_UNKNOWN")) ? "unknown":pamh->user; usr[MAX_PAM_STATS_USR_SIZE-1]='\0'; strncpy(usr,u,MAX_PAM_STATS_USR_SIZE-1); /* OK, start logging stats */ memset(buf,'\0',MAX_PAM_STATS_BUF_SIZE); snprintf(buf, MAX_PAM_STATS_BUF_SIZE-1, "statsd -a incr pam_failed_%s %s \\;" " push pam_last_failure_%s %s \"%s\" 0 \\;" " incr pam_users %s \\; incr pam_services %s", usr,pamh->service_name, usr,pamh->service_name, pam_strerror(pamh, retval), usr, pamh->service_name); if (system(buf) == -1) { pam_syslog(pamh, LOG_INFO, "%s - failed", buf); } } } #endif return retval; }
static int open_session (pam_handle_t *pamh) { struct passwd *buf = NULL; const char *name; int res; name = NULL; pwd = NULL; res = pam_get_item (pamh, PAM_USER, (const void **)&name); if (res != PAM_SUCCESS) { warnx ("couldn't load user from pam"); return res; } /* Yes, buf "leaks" */ buf = malloc (sizeof (struct passwd) + 8192); if (buf == NULL) res = ENOMEM; else res = getpwnam_r (name, buf, (char *)(buf + 1), 8192, &pwd); if (pwd == NULL) { warnx ("couldn't load user info for: %s: %s", name, res == 0 ? "not found" : strerror (res)); return PAM_SYSTEM_ERR; } /* * If we're already running as the right user, and have authenticated * then skip starting a new session. This is used when testing, or * running as your own user. */ want_session = !(geteuid () != 0 && geteuid () == pwd->pw_uid && getuid () == pwd->pw_uid && getegid () == pwd->pw_gid && getgid () == pwd->pw_gid); if (want_session) { debug ("checking access for %s", name); res = pam_acct_mgmt (pamh, 0); if (res != PAM_SUCCESS) { warnx ("user account access failed: %s: %s", name, pam_strerror (pamh, res)); /* We change PAM_AUTH_ERR to PAM_PERM_DENIED so that we can * distinguish between failures here and in * * pam_authenticate. */ if (res == PAM_AUTH_ERR) res = PAM_PERM_DENIED; return res; } debug ("opening pam session for %s", name); pam_putenv (pamh, "XDG_SESSION_CLASS=user"); res = pam_setcred (pamh, PAM_ESTABLISH_CRED); if (res != PAM_SUCCESS) { warnx ("establishing credentials failed: %s: %s", name, pam_strerror (pamh, res)); return res; } res = pam_open_session (pamh, 0); if (res != PAM_SUCCESS) { warnx ("couldn't open session: %s: %s", name, pam_strerror (pamh, res)); return res; } res = pam_setcred (pamh, PAM_REINITIALIZE_CRED); if (res != PAM_SUCCESS) { warnx ("reinitializing credentials failed: %s: %s", name, pam_strerror (pamh, res)); return res; } } return PAM_SUCCESS; }
static void pam_dp_process_reply(DBusPendingCall *pending, void *ptr) { DBusError dbus_error; DBusMessage* msg; int ret; int type; struct pam_auth_req *preq = NULL; struct pam_auth_dp_req *pdp_req; pdp_req = talloc_get_type(ptr, struct pam_auth_dp_req); preq = pdp_req->preq; talloc_free(pdp_req); dbus_error_init(&dbus_error); msg = dbus_pending_call_steal_reply(pending); /* Check if the client still exists. If not, simply free all the resources * and quit */ if (preq == NULL) { DEBUG(SSSDBG_MINOR_FAILURE, "Client already disconnected\n"); dbus_pending_call_unref(pending); dbus_message_unref(msg); return; } /* Sanity-check of message validity */ if (msg == NULL) { DEBUG(SSSDBG_FATAL_FAILURE, "Severe error. A reply callback was called but no reply was" "received and no timeout occurred\n"); preq->pd->pam_status = PAM_SYSTEM_ERR; goto done; } type = dbus_message_get_type(msg); switch (type) { case DBUS_MESSAGE_TYPE_METHOD_RETURN: ret = dp_unpack_pam_response(msg, preq->pd, &dbus_error); if (!ret) { DEBUG(SSSDBG_FATAL_FAILURE, "Failed to parse reply.\n"); preq->pd->pam_status = PAM_SYSTEM_ERR; goto done; } DEBUG(SSSDBG_FUNC_DATA, "received: [%d (%s)][%s]\n", preq->pd->pam_status, pam_strerror(NULL, preq->pd->pam_status), preq->pd->domain); break; case DBUS_MESSAGE_TYPE_ERROR: DEBUG(SSSDBG_FATAL_FAILURE, "Reply error.\n"); preq->pd->pam_status = PAM_SYSTEM_ERR; break; default: DEBUG(SSSDBG_FATAL_FAILURE, "Default... what now?.\n"); preq->pd->pam_status = PAM_SYSTEM_ERR; } done: dbus_pending_call_unref(pending); dbus_message_unref(msg); preq->callback(preq); }
static pam_handle_t * perform_basic (void) { struct pam_conv conv = { pam_conv_func, }; pam_handle_t *pamh; char *input = NULL; char *password; int res; debug ("reading password from cockpit-ws"); /* The input should be a user:password */ input = read_fd_until_eof (AUTH_FD, "password", NULL); password = strchr (input, ':'); if (password == NULL || strchr (password + 1, '\n')) { debug ("bad basic auth input"); write_auth_begin (PAM_AUTH_ERR); write_auth_end (); exit (5); } *password = '******'; password++; conv.appdata_ptr = &input; res = pam_start ("cockpit", input, &conv, &pamh); if (res != PAM_SUCCESS) errx (EX, "couldn't start pam: %s", pam_strerror (NULL, res)); /* Move the password into place for use during auth */ memmove (input, password, strlen (password) + 1); if (pam_set_item (pamh, PAM_RHOST, rhost) != PAM_SUCCESS) errx (EX, "couldn't setup pam"); debug ("authenticating"); res = pam_authenticate (pamh, 0); if (res == PAM_SUCCESS) res = open_session (pamh); write_auth_begin (res); if (res == PAM_SUCCESS && pwd) { write_auth_string ("user", pwd->pw_name); if (pwd->pw_gecos) write_auth_string ("full-name", pwd->pw_gecos); } write_auth_end (); if (res != PAM_SUCCESS) exit (5); if (input) { memset (input, 0, strlen (input)); free (input); } return pamh; }
PAM_EXTERN int pam_sm_authenticate (pam_handle_t * pamh, int flags, int argc, const char **argv) { int retval, rc; const char *user = NULL; const char *password = NULL; char otp[MAX_OTP_LEN + 1]; int password_len = 0; struct pam_conv *conv; struct pam_message *pmsg[1], msg[1]; struct pam_response *resp; int nargs = 1; struct cfg cfg; char *query_prompt = NULL; char *onlypasswd = strdup (""); /* empty passwords never match */ parse_cfg (flags, argc, argv, &cfg); retval = pam_get_user (pamh, &user, NULL); if (retval != PAM_SUCCESS) { DBG (("get user returned error: %s", pam_strerror (pamh, retval))); goto done; } DBG (("get user returned: %s", user)); if (cfg.try_first_pass || cfg.use_first_pass) { retval = pam_get_item (pamh, PAM_AUTHTOK, (const void **) &password); if (retval != PAM_SUCCESS) { DBG (("get password returned error: %s", pam_strerror (pamh, retval))); goto done; } DBG (("get password returned: %s", password)); } if (cfg.use_first_pass && password == NULL) { DBG (("use_first_pass set and no password, giving up")); retval = PAM_AUTH_ERR; goto done; } rc = oath_init (); if (rc != OATH_OK) { DBG (("oath_init() failed (%d)", rc)); retval = PAM_AUTHINFO_UNAVAIL; goto done; } if (password == NULL) { retval = pam_get_item (pamh, PAM_CONV, (const void **) &conv); if (retval != PAM_SUCCESS) { DBG (("get conv returned error: %s", pam_strerror (pamh, retval))); goto done; } pmsg[0] = &msg[0]; { const char *query_template = "One-time password (OATH) for `%s': "; size_t len = strlen (query_template) + strlen (user); size_t wrote; query_prompt = malloc (len); if (!query_prompt) { retval = PAM_BUF_ERR; goto done; } wrote = snprintf (query_prompt, len, query_template, user); if (wrote < 0 || wrote >= len) { retval = PAM_BUF_ERR; goto done; } msg[0].msg = query_prompt; } msg[0].msg_style = PAM_PROMPT_ECHO_OFF; resp = NULL; retval = conv->conv (nargs, (const struct pam_message **) pmsg, &resp, conv->appdata_ptr); free (query_prompt); query_prompt = NULL; if (retval != PAM_SUCCESS) { DBG (("conv returned error: %s", pam_strerror (pamh, retval))); goto done; } DBG (("conv returned: %s", resp->resp)); password = resp->resp; } if (password) password_len = strlen (password); else { DBG (("Could not read password")); retval = PAM_AUTH_ERR; goto done; } if (password_len < MIN_OTP_LEN) { DBG (("OTP too short: %s", password)); retval = PAM_AUTH_ERR; goto done; } else if (cfg.digits != 0 && password_len < cfg.digits) { DBG (("OTP shorter than digits=%d: %s", cfg.digits, password)); retval = PAM_AUTH_ERR; goto done; } else if (cfg.digits == 0 && password_len > MAX_OTP_LEN) { DBG (("OTP too long (and no digits=): %s", password)); retval = PAM_AUTH_ERR; goto done; } else if (cfg.digits != 0 && password_len > cfg.digits) { free (onlypasswd); onlypasswd = strdup (password); /* user entered their system password followed by generated OTP? */ onlypasswd[password_len - cfg.digits] = '\0'; DBG (("Password: %s ", onlypasswd)); memcpy (otp, password + password_len - cfg.digits, cfg.digits); otp[cfg.digits] = '\0'; retval = pam_set_item (pamh, PAM_AUTHTOK, onlypasswd); if (retval != PAM_SUCCESS) { DBG (("set_item returned error: %s", pam_strerror (pamh, retval))); goto done; } } else { strcpy (otp, password); password = NULL; } DBG (("OTP: %s", otp ? otp : "(null)")); { time_t last_otp; rc = oath_authenticate_usersfile (cfg.usersfile, user, otp, cfg.window, onlypasswd, &last_otp); DBG (("authenticate rc %d (%s: %s) last otp %s", rc, oath_strerror_name (rc) ? oath_strerror_name (rc) : "UNKNOWN", oath_strerror (rc), ctime (&last_otp))); } if (rc != OATH_OK) { DBG (("One-time password not authorized to login as user '%s'", user)); retval = PAM_AUTH_ERR; goto done; } retval = PAM_SUCCESS; done: oath_done (); free (query_prompt); free (onlypasswd); if (cfg.alwaysok && retval != PAM_SUCCESS) { DBG (("alwaysok needed (otherwise return with %d)", retval)); retval = PAM_SUCCESS; } DBG (("done. [%s]", pam_strerror (pamh, retval))); return retval; }
static pam_handle_t * perform_gssapi (void) { struct pam_conv conv = { pam_conv_func, }; OM_uint32 major, minor; gss_cred_id_t client = GSS_C_NO_CREDENTIAL; gss_buffer_desc input = GSS_C_EMPTY_BUFFER; gss_buffer_desc output = GSS_C_EMPTY_BUFFER; gss_buffer_desc local = GSS_C_EMPTY_BUFFER; gss_buffer_desc display = GSS_C_EMPTY_BUFFER; gss_name_t name = GSS_C_NO_NAME; gss_ctx_id_t context = GSS_C_NO_CONTEXT; gss_OID mech_type = GSS_C_NO_OID; pam_handle_t *pamh = NULL; OM_uint32 flags = 0; char *str = NULL; OM_uint32 caps = 0; int res; res = PAM_AUTH_ERR; /* We shouldn't be writing to kerberos caches here */ setenv ("KRB5CCNAME", "FILE:/dev/null", 1); debug ("reading kerberos auth from cockpit-ws"); input.value = read_fd_until_eof (AUTH_FD, "gssapi data", &input.length); major = gss_accept_sec_context (&minor, &context, GSS_C_NO_CREDENTIAL, &input, GSS_C_NO_CHANNEL_BINDINGS, &name, &mech_type, &output, &flags, &caps, &client); if (GSS_ERROR (major)) { warnx ("gssapi auth failed: %s", gssapi_strerror (major, minor)); goto out; } /* * In general gssapi mechanisms can require multiple challenge response * iterations keeping &context between each, however Kerberos doesn't * require this, so we don't care :O * * If we ever want this to work with something other than Kerberos, then * we'll have to have some sorta session that holds the context. */ if (major & GSS_S_CONTINUE_NEEDED) goto out; major = gss_localname (&minor, name, mech_type, &local); if (major == GSS_S_COMPLETE) { minor = 0; str = dup_string (local.value, local.length); debug ("mapped gssapi name to local user '%s'", str); if (getpwnam (str)) { res = pam_start ("cockpit", str, &conv, &pamh); } else { /* If the local user doesn't exist, pretend gss_localname() failed */ free (str); str = NULL; major = GSS_S_FAILURE; minor = KRB5_NO_LOCALNAME; } } if (major != GSS_S_COMPLETE) { if (minor == (OM_uint32)KRB5_NO_LOCALNAME || minor == (OM_uint32)KRB5_LNAME_NOTRANS) { major = gss_display_name (&minor, name, &display, NULL); if (GSS_ERROR (major)) { warnx ("couldn't get gssapi display name: %s", gssapi_strerror (major, minor)); goto out; } str = dup_string (display.value, display.length); debug ("no local user mapping for gssapi name '%s'", str); res = pam_start ("cockpit", str, &conv, &pamh); } else { warnx ("couldn't map gssapi name to local user: %s", gssapi_strerror (major, minor)); goto out; } } if (res != PAM_SUCCESS) errx (EX, "couldn't start pam: %s", pam_strerror (NULL, res)); if (pam_set_item (pamh, PAM_RHOST, rhost) != PAM_SUCCESS) errx (EX, "couldn't setup pam"); res = open_session (pamh); out: write_auth_begin (res); if (pwd) { write_auth_string ("user", pwd->pw_name); if (pwd->pw_gecos) write_auth_string ("full-name", pwd->pw_gecos); } if (output.value) write_auth_hex ("gssapi-output", output.value, output.length); if (output.value) gss_release_buffer (&minor, &output); output.value = NULL; output.length = 0; if (caps & GSS_C_DELEG_FLAG && client != GSS_C_NO_CREDENTIAL) { #ifdef HAVE_GSS_IMPORT_CRED major = gss_export_cred (&minor, client, &output); if (GSS_ERROR (major)) warnx ("couldn't export gssapi credentials: %s", gssapi_strerror (major, minor)); else if (output.value) write_auth_hex ("gssapi-creds", output.value, output.length); #else /* cockpit-ws will complain for us, if they're ever used */ write_auth_hex ("gssapi-creds", (void *)"", 0); #endif } write_auth_end (); if (display.value) gss_release_buffer (&minor, &display); if (output.value) gss_release_buffer (&minor, &output); if (local.value) gss_release_buffer (&minor, &local); if (client != GSS_C_NO_CREDENTIAL) gss_release_cred (&minor, &client); if (name != GSS_C_NO_NAME) gss_release_name (&minor, &name); if (context != GSS_C_NO_CONTEXT) gss_delete_sec_context (&minor, &context, GSS_C_NO_BUFFER); free (input.value); free (str); unsetenv ("KRB5CCNAME"); if (res != PAM_SUCCESS) exit (5); return pamh; }
/* * main - groupmod command * */ int main (int argc, char **argv) { #ifdef ACCT_TOOLS_SETUID #ifdef USE_PAM pam_handle_t *pamh = NULL; int retval; #endif /* USE_PAM */ #endif /* ACCT_TOOLS_SETUID */ /* * Get my name so that I can use it to report errors. */ Prog = Basename (argv[0]); (void) setlocale (LC_ALL, ""); (void) bindtextdomain (PACKAGE, LOCALEDIR); (void) textdomain (PACKAGE); process_root_flag ("-R", argc, argv); OPENLOG ("groupmod"); #ifdef WITH_AUDIT audit_help_open (); #endif if (atexit (do_cleanups) != 0) { fprintf (stderr, _("%s: Cannot setup cleanup service.\n"), Prog); exit (1); } process_flags (argc, argv); #ifdef ACCT_TOOLS_SETUID #ifdef USE_PAM { struct passwd *pampw; pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */ if (NULL == pampw) { fprintf (stderr, _("%s: Cannot determine your user name.\n"), Prog); exit (1); } retval = pam_start ("groupmod", pampw->pw_name, &conv, &pamh); } if (PAM_SUCCESS == retval) { retval = pam_authenticate (pamh, 0); } if (PAM_SUCCESS == retval) { retval = pam_acct_mgmt (pamh, 0); } if (PAM_SUCCESS != retval) { fprintf (stderr, _("%s: PAM: %s\n"), Prog, pam_strerror (pamh, retval)); SYSLOG((LOG_ERR, "%s", pam_strerror (pamh, retval))); if (NULL != pamh) { (void) pam_end (pamh, retval); } exit (1); } (void) pam_end (pamh, retval); #endif /* USE_PAM */ #endif /* ACCT_TOOLS_SETUID */ #ifdef SHADOWGRP is_shadow_grp = sgr_file_present (); #endif { struct group *grp; /* * Start with a quick check to see if the group exists. */ grp = getgrnam (group_name); /* local, no need for xgetgrnam */ if (NULL == grp) { fprintf (stderr, _("%s: group '%s' does not exist\n"), Prog, group_name); exit (E_NOTFOUND); } else { group_id = grp->gr_gid; } } #ifdef USE_NIS /* * Now make sure it isn't an NIS group. */ if (__isgrNIS ()) { char *nis_domain; char *nis_master; fprintf (stderr, _("%s: group %s is a NIS group\n"), Prog, group_name); if (!yp_get_default_domain (&nis_domain) && !yp_master (nis_domain, "group.byname", &nis_master)) { fprintf (stderr, _("%s: %s is the NIS master\n"), Prog, nis_master); } exit (E_NOTFOUND); } #endif if (gflg) { check_new_gid (); } if (nflg) { check_new_name (); } lock_files (); /* * Now if the group is not changed, it's our fault. * Make sure failures will be reported. */ prepare_failure_reports (); /* * Do the hard stuff - open the files, create the group entries, * then close and update the files. */ open_files (); grp_update (); close_files (); nscd_flush_cache ("group"); return E_SUCCESS; }
int main (int argc, char **argv) { pam_handle_t *pamh = NULL; const char *auth; char **env; int status; int flags; int res; int i; if (isatty (0)) errx (2, "this command is not meant to be run from the console"); if (argc != 3) errx (2, "invalid arguments to cockpit-session"); /* Cleanup the umask */ umask (077); save_environment (); /* When setuid root, make sure our group is also root */ if (geteuid () == 0) { /* Never trust the environment when running setuid() */ if (getuid() != 0) { if (clearenv () != 0) err (1, "couldn't clear environment"); } /* set a minimal environment */ setenv ("PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 1); if (setgid (0) != 0 || setuid (0) != 0) err (1, "couldn't switch permissions correctly"); } /* We should never leak our auth fd to other processes */ flags = fcntl (AUTH_FD, F_GETFD); if (flags < 0 || fcntl (AUTH_FD, F_SETFD, flags | FD_CLOEXEC)) err (1, "couldn't set auth fd flags"); auth = argv[1]; rhost = argv[2]; signal (SIGALRM, SIG_DFL); signal (SIGQUIT, SIG_DFL); signal (SIGTSTP, SIG_IGN); signal (SIGHUP, SIG_IGN); signal (SIGPIPE, SIG_IGN); if (strcmp (auth, "basic") == 0) pamh = perform_basic (); else if (strcmp (auth, "negotiate") == 0) pamh = perform_gssapi (); else errx (2, "unrecognized authentication method: %s", auth); for (i = 0; env_saved[i] != NULL; i++) pam_putenv (pamh, env_saved[i]); env = pam_getenvlist (pamh); if (env == NULL) errx (EX, "get pam environment failed"); if (want_session) { assert (pwd != NULL); if (initgroups (pwd->pw_name, pwd->pw_gid) < 0) err (EX, "%s: can't init groups", pwd->pw_name); signal (SIGTERM, pass_to_child); signal (SIGINT, pass_to_child); signal (SIGQUIT, pass_to_child); utmp_log (1); status = fork_session (env); utmp_log (0); signal (SIGTERM, SIG_DFL); signal (SIGINT, SIG_DFL); signal (SIGQUIT, SIG_DFL); res = pam_setcred (pamh, PAM_DELETE_CRED); if (res != PAM_SUCCESS) err (EX, "%s: couldn't delete creds: %s", pwd->pw_name, pam_strerror (pamh, res)); res = pam_close_session (pamh, 0); if (res != PAM_SUCCESS) err (EX, "%s: couldn't close session: %s", pwd->pw_name, pam_strerror (pamh, res)); } else { status = session (env); } pam_end (pamh, PAM_SUCCESS); if (WIFEXITED(status)) exit (WEXITSTATUS(status)); else if (WIFSIGNALED(status)) raise (WTERMSIG(status)); else exit (127); }
int main (int argc, char *argv[]) { int rc; const char *user_to_auth; char *cookie = NULL; struct pam_conv pam_conversation; pam_handle_t *pam_h; const void *authed_user; rc = 0; pam_h = NULL; /* clear the entire environment to avoid attacks using with libraries honoring environment variables */ if (_polkit_clearenv () != 0) goto error; /* set a minimal environment */ setenv ("PATH", "/usr/sbin:/usr/bin:/sbin:/bin", 1); /* check that we are setuid root */ if (geteuid () != 0) { gchar *s; fprintf (stderr, "polkit-agent-helper-1: needs to be setuid root\n"); /* Special-case a very common error triggered in jhbuild setups */ s = g_strdup_printf ("Incorrect permissions on %s (needs to be setuid root)", argv[0]); send_to_helper ("PAM_ERROR_MSG", s); g_free (s); goto error; } openlog ("polkit-agent-helper-1", LOG_CONS | LOG_PID, LOG_AUTHPRIV); /* check for correct invocation */ if (!(argc == 2 || argc == 3)) { syslog (LOG_NOTICE, "inappropriate use of helper, wrong number of arguments [uid=%d]", getuid ()); fprintf (stderr, "polkit-agent-helper-1: wrong number of arguments. This incident has been logged.\n"); goto error; } user_to_auth = argv[1]; cookie = read_cookie (argc, argv); if (!cookie) goto error; if (getuid () != 0) { /* check we're running with a non-tty stdin */ if (isatty (STDIN_FILENO) != 0) { syslog (LOG_NOTICE, "inappropriate use of helper, stdin is a tty [uid=%d]", getuid ()); fprintf (stderr, "polkit-agent-helper-1: inappropriate use of helper, stdin is a tty. This incident has been logged.\n"); goto error; } } #ifdef PAH_DEBUG fprintf (stderr, "polkit-agent-helper-1: user to auth is '%s'.\n", user_to_auth); #endif /* PAH_DEBUG */ pam_conversation.conv = conversation_function; pam_conversation.appdata_ptr = NULL; /* start the pam stack */ rc = pam_start ("polkit-1", user_to_auth, &pam_conversation, &pam_h); if (rc != PAM_SUCCESS) { fprintf (stderr, "polkit-agent-helper-1: pam_start failed: %s\n", pam_strerror (pam_h, rc)); goto error; } /* set the requesting user */ rc = pam_set_item (pam_h, PAM_RUSER, user_to_auth); if (rc != PAM_SUCCESS) { fprintf (stderr, "polkit-agent-helper-1: pam_set_item failed: %s\n", pam_strerror (pam_h, rc)); goto error; } /* is user really user? */ rc = pam_authenticate (pam_h, 0); if (rc != PAM_SUCCESS) { const char *err; err = pam_strerror (pam_h, rc); fprintf (stderr, "polkit-agent-helper-1: pam_authenticate failed: %s\n", err); goto error; } /* permitted access? */ rc = pam_acct_mgmt (pam_h, 0); if (rc != PAM_SUCCESS) { const char *err; err = pam_strerror (pam_h, rc); fprintf (stderr, "polkit-agent-helper-1: pam_acct_mgmt failed: %s\n", err); goto error; } /* did we auth the right user? */ rc = pam_get_item (pam_h, PAM_USER, &authed_user); if (rc != PAM_SUCCESS) { const char *err; err = pam_strerror (pam_h, rc); fprintf (stderr, "polkit-agent-helper-1: pam_get_item failed: %s\n", err); goto error; } if (strcmp (authed_user, user_to_auth) != 0) { fprintf (stderr, "polkit-agent-helper-1: Tried to auth user '%s' but we got auth for user '%s' instead", user_to_auth, (const char *) authed_user); goto error; } #ifdef PAH_DEBUG fprintf (stderr, "polkit-agent-helper-1: successfully authenticated user '%s'.\n", user_to_auth); #endif /* PAH_DEBUG */ pam_end (pam_h, rc); pam_h = NULL; #ifdef PAH_DEBUG fprintf (stderr, "polkit-agent-helper-1: sending D-Bus message to PolicyKit daemon\n"); #endif /* PAH_DEBUG */ /* now send a D-Bus message to the PolicyKit daemon that * includes a) the cookie; and b) the user we authenticated */ if (!send_dbus_message (cookie, user_to_auth)) { #ifdef PAH_DEBUG fprintf (stderr, "polkit-agent-helper-1: error sending D-Bus message to PolicyKit daemon\n"); #endif /* PAH_DEBUG */ goto error; } free (cookie); #ifdef PAH_DEBUG fprintf (stderr, "polkit-agent-helper-1: successfully sent D-Bus message to PolicyKit daemon\n"); #endif /* PAH_DEBUG */ fprintf (stdout, "SUCCESS\n"); flush_and_wait(); return 0; error: free (cookie); if (pam_h != NULL) pam_end (pam_h, rc); fprintf (stdout, "FAILURE\n"); flush_and_wait(); return 1; }
static void setup_child (int inp[2], int outp[2], int errp[2], pam_handle_t *ph, struct passwd *pwd) { const char* display; int i, ret; #ifdef VALGRIND char *args[] = { VALGRIND, VALGRIND_ARG, MATE_KEYRING_DAEMON, "--daemonize", "--login", NULL}; #else char *args[] = { MATE_KEYRING_DAEMON, "--daemonize", "--login", NULL}; #endif assert (pwd); assert (pwd->pw_dir); /* Fix up our end of the pipes */ if (dup2 (inp[READ_END], STDIN) < 0 || dup2 (outp[WRITE_END], STDOUT) < 0 || dup2 (errp[WRITE_END], STDERR) < 0) { syslog (GKR_LOG_ERR, "gkr-pam: couldn't setup pipes: %s", strerror (errno)); exit (EXIT_FAILURE); } /* Try valiantly to close unnecessary file descriptors */ for (i = STDERR; i < 64; ++i) close (i); /* Close unnecessary file descriptors */ close (inp[READ_END]); close (inp[WRITE_END]); close (outp[READ_END]); close (outp[WRITE_END]); close (errp[READ_END]); close (errp[WRITE_END]); /* We may be running effective as another user, revert that */ seteuid (getuid ()); setegid (getgid ()); /* Setup process credentials */ if (setgid (pwd->pw_gid) < 0 || setuid (pwd->pw_uid) < 0 || setegid (pwd->pw_gid) < 0 || seteuid (pwd->pw_uid) < 0) { syslog (GKR_LOG_ERR, "gkr-pam: couldn't setup credentials: %s", strerror (errno)); exit (EXIT_FAILURE); } /* Setup environment variables */ ret = setup_pam_env (ph, "HOME", pwd->pw_dir); if (ret == PAM_SUCCESS && !pam_getenv (ph, "DISPLAY")) { display = getenv ("DISPLAY"); if (display) ret = setup_pam_env (ph, "DISPLAY", display); } /* Make sure that worked */ if (ret != PAM_SUCCESS) { syslog (GKR_LOG_ERR, "gkr-pam: couldn't setup environment: %s", pam_strerror (ph, ret)); exit (EXIT_FAILURE); } /* Now actually execute the process */ execve (args[0], args, pam_getenvlist (ph)); syslog (GKR_LOG_ERR, "gkr-pam: couldn't run mate-keyring-daemon: %s", strerror (errno)); exit (EXIT_FAILURE); }
static int sshpam_query(void *ctx, char **name, char **info, u_int *num, char ***prompts, u_int **echo_on) { struct ssh *ssh = active_state; /* XXX */ Buffer buffer; struct pam_ctxt *ctxt = ctx; size_t plen; u_char type; char *msg; size_t len, mlen; debug3("PAM: %s entering", __func__); buffer_init(&buffer); *name = xstrdup(""); *info = xstrdup(""); *prompts = xmalloc(sizeof(char *)); **prompts = NULL; plen = 0; *echo_on = xmalloc(sizeof(u_int)); while (ssh_msg_recv(ctxt->pam_psock, &buffer) == 0) { type = buffer_get_char(&buffer); msg = buffer_get_string(&buffer, NULL); mlen = strlen(msg); switch (type) { case PAM_PROMPT_ECHO_ON: case PAM_PROMPT_ECHO_OFF: *num = 1; len = plen + mlen + 1; **prompts = xreallocarray(**prompts, 1, len); strlcpy(**prompts + plen, msg, len - plen); plen += mlen; **echo_on = (type == PAM_PROMPT_ECHO_ON); free(msg); return (0); case PAM_ERROR_MSG: case PAM_TEXT_INFO: /* accumulate messages */ len = plen + mlen + 2; **prompts = xreallocarray(**prompts, 1, len); strlcpy(**prompts + plen, msg, len - plen); plen += mlen; strlcat(**prompts + plen, "\n", len - plen); plen++; free(msg); break; case PAM_ACCT_EXPIRED: case PAM_MAXTRIES: if (type == PAM_ACCT_EXPIRED) sshpam_account_status = 0; if (type == PAM_MAXTRIES) sshpam_set_maxtries_reached(1); /* FALLTHROUGH */ case PAM_AUTH_ERR: debug3("PAM: %s", pam_strerror(sshpam_handle, type)); if (**prompts != NULL && strlen(**prompts) != 0) { *info = **prompts; **prompts = NULL; *num = 0; **echo_on = 0; ctxt->pam_done = -1; free(msg); return 0; } /* FALLTHROUGH */ case PAM_SUCCESS: if (**prompts != NULL) { /* drain any accumulated messages */ debug("PAM: %s", **prompts); buffer_append(&loginmsg, **prompts, strlen(**prompts)); free(**prompts); **prompts = NULL; } if (type == PAM_SUCCESS) { if (!sshpam_authctxt->valid || (sshpam_authctxt->pw->pw_uid == 0 && options.permit_root_login != PERMIT_YES)) fatal("Internal error: PAM auth " "succeeded when it should have " "failed"); import_environments(&buffer); *num = 0; **echo_on = 0; ctxt->pam_done = 1; free(msg); return (0); } error("PAM: %s for %s%.100s from %.100s", msg, sshpam_authctxt->valid ? "" : "illegal user ", sshpam_authctxt->user, auth_get_canonical_hostname(ssh, options.use_dns)); /* FALLTHROUGH */ default: *num = 0; **echo_on = 0; free(msg); ctxt->pam_done = -1; return (-1); } } return (-1); }
PAM_EXTERN int pam_sm_authenticate (pam_handle_t *ph, int unused, int argc, const char **argv) { struct passwd *pwd; const char *user, *password; const char *control; int started_daemon; uint args; int ret; args = parse_args (ph, argc, argv); if (args & ARG_IGNORE_SERVICE) return PAM_SUCCESS; /* Figure out and/or prompt for the user name */ ret = pam_get_user (ph, &user, NULL); if (ret != PAM_SUCCESS) { syslog (GKR_LOG_ERR, "gkr-pam: couldn't get the user name: %s", pam_strerror (ph, ret)); return PAM_SERVICE_ERR; } pwd = getpwnam (user); if (!pwd) { syslog (GKR_LOG_ERR, "gkr-pam: error looking up user information"); return PAM_SERVICE_ERR; } /* Look up the password */ ret = pam_get_item (ph, PAM_AUTHTOK, (const void**)&password); if (ret != PAM_SUCCESS || password == NULL) { if (ret == PAM_SUCCESS) syslog (GKR_LOG_WARN, "gkr-pam: no password is available for user"); else syslog (GKR_LOG_WARN, "gkr-pam: no password is available for user: %s", pam_strerror (ph, ret)); return PAM_SUCCESS; } started_daemon = 0; /* Should we start the daemon? */ if (args & ARG_AUTO_START) { ret = start_daemon_if_necessary (ph, pwd, password, &started_daemon); if (ret != PAM_SUCCESS) return ret; } control = get_any_env (ph, ENV_CONTROL); /* If mate keyring is running, then unlock now */ if (control) { /* If we started the daemon, its already unlocked, since we passed the password */ if (!started_daemon) { ret = unlock_keyring (ph, pwd, password); if (ret != PAM_SUCCESS) return ret; } /* Otherwise start later in open session, store password */ } else { if (pam_set_data (ph, "gkr_system_authtok", strdup (password), cleanup_free_password) != PAM_SUCCESS) { syslog (GKR_LOG_ERR, "gkr-pam: error storing authtok"); return PAM_AUTHTOK_RECOVER_ERR; } } return PAM_SUCCESS; }
struct passwd * pam_modutil_getpwuid(pam_handle_t *pamh, uid_t uid) { #ifdef HAVE_GETPWUID_R void *buffer=NULL; size_t length = PWD_INITIAL_LENGTH; do { int status; void *new_buffer; struct passwd *result = NULL; new_buffer = realloc(buffer, sizeof(struct passwd) + length); if (new_buffer == NULL) { D(("out of memory")); /* no memory for the user - so delete the memory */ if (buffer) { free(buffer); } return NULL; } buffer = new_buffer; /* make the re-entrant call to get the pwd structure */ errno = 0; status = getpwuid_r(uid, buffer, sizeof(struct passwd) + (char *) buffer, length, &result); if (!status && (result == buffer)) { char *data_name; const void *ignore; int i; data_name = malloc(strlen("_pammodutil_getpwuid") + 1 + longlen((long) uid) + 1 + intlen(INT_MAX) + 1); if ((pamh != NULL) && (data_name == NULL)) { D(("was unable to register the data item [%s]", pam_strerror(pamh, status))); free(buffer); return NULL; } if (pamh != NULL) { for (i = 0; i < INT_MAX; i++) { sprintf(data_name, "_pammodutil_getpwuid_%ld_%d", (long) uid, i); status = PAM_NO_MODULE_DATA; if (pam_get_data(pamh, data_name, &ignore) != PAM_SUCCESS) { status = pam_set_data(pamh, data_name, result, pam_modutil_cleanup); } if (status == PAM_SUCCESS) { break; } } } else { status = PAM_SUCCESS; } free(data_name); if (status == PAM_SUCCESS) { D(("success")); return result; } D(("was unable to register the data item [%s]", pam_strerror(pamh, status))); free(buffer); return NULL; } else if (errno != ERANGE && errno != EINTR) { /* no sense in repeating the call */ break; } length <<= PWD_LENGTH_SHIFT; } while (length < PWD_ABSURD_PWD_LENGTH); D(("pwd structure took %u bytes or so of memory", length+sizeof(struct passwd))); free(buffer); return NULL; #else /* ie. ifndef HAVE_GETPWUID_R */ /* * Sorry, there does not appear to be a reentrant version of * getpwuid(). So, we use the standard libc function. */ return getpwuid(uid); #endif /* def HAVE_GETPWUID_R */ }
static int sftppam_driver_authenticate(sftp_kbdint_driver_t *driver, const char *user) { int res; pr_signals_block(); PRIVS_ROOT res = pam_authenticate(sftppam_pamh, 0); if (res != PAM_SUCCESS) { switch (res) { case PAM_USER_UNKNOWN: sftppam_auth_code = PR_AUTH_NOPWD; break; default: sftppam_auth_code = PR_AUTH_BADPWD; } (void) pr_log_writefile(sftp_logfd, MOD_SFTP_PAM_VERSION, "PAM authentication error (%d) for user '%s': %s", res, user, pam_strerror(sftppam_pamh, res)); (void) pr_log_pri(PR_LOG_NOTICE, MOD_SFTP_PAM_VERSION ": PAM authentication error (%d) for user '%s': %s", res, user, pam_strerror(sftppam_pamh, res)); PRIVS_RELINQUISH pr_signals_unblock(); errno = EPERM; return -1; } res = pam_acct_mgmt(sftppam_pamh, 0); if (res != PAM_SUCCESS) { switch (res) { #ifdef PAM_AUTHTOKEN_REQD case PAM_AUTHTOKEN_REQD: pr_trace_msg(trace_channel, 8, "PAM account mgmt error: PAM_AUTHTOKEN_REQD"); break; #endif case PAM_ACCT_EXPIRED: pr_trace_msg(trace_channel, 8, "PAM account mgmt error: PAM_ACCT_EXPIRED"); sftppam_auth_code = PR_AUTH_DISABLEDPWD; break; #ifdef PAM_ACCT_DISABLED case PAM_ACCT_DISABLED: pr_trace_msg(trace_channel, 8, "PAM account mgmt error: PAM_ACCT_DISABLED"); sftppam_auth_code = PR_AUTH_DISABLEDPWD; break; #endif case PAM_USER_UNKNOWN: pr_trace_msg(trace_channel, 8, "PAM account mgmt error: PAM_USER_UNKNOWN"); sftppam_auth_code = PR_AUTH_NOPWD; break; default: sftppam_auth_code = PR_AUTH_BADPWD; break; } pr_trace_msg(trace_channel, 1, "PAM account mgmt error (%d) for user '%s': %s", res, user, pam_strerror(sftppam_pamh, res)); PRIVS_RELINQUISH pr_signals_unblock(); errno = EPERM; return -1; } res = pam_open_session(sftppam_pamh, 0); if (res != PAM_SUCCESS) { sftppam_auth_code = PR_AUTH_DISABLEDPWD; pr_trace_msg(trace_channel, 1, "PAM session error (%d) for user '%s': %s", res, user, pam_strerror(sftppam_pamh, res)); PRIVS_RELINQUISH pr_signals_unblock(); errno = EPERM; return -1; } #ifdef PAM_CRED_ESTABLISH res = pam_setcred(sftppam_pamh, PAM_CRED_ESTABLISH); #else res = pam_setcred(sftppam_pamh, PAM_ESTABLISH_CRED); #endif /* !PAM_CRED_ESTABLISH */ if (res != PAM_SUCCESS) { switch (res) { case PAM_CRED_EXPIRED: pr_trace_msg(trace_channel, 8, "PAM credentials error: PAM_CRED_EXPIRED"); sftppam_auth_code = PR_AUTH_AGEPWD; break; case PAM_USER_UNKNOWN: pr_trace_msg(trace_channel, 8, "PAM credentials error: PAM_USER_UNKNOWN"); sftppam_auth_code = PR_AUTH_NOPWD; break; default: sftppam_auth_code = PR_AUTH_BADPWD; break; } pr_trace_msg(trace_channel, 1, "PAM credentials error (%d) for user '%s': %s", res, user, pam_strerror(sftppam_pamh, res)); PRIVS_RELINQUISH pr_signals_unblock(); errno = EPERM; return -1; } /* XXX Not sure why these platforms have different treatment...? */ #if defined(SOLARIS2) || defined(HPUX10) || defined(HPUX11) res = pam_close_session(sftppam_pamh, 0); if (sftppam_pamh) { pam_end(sftppam_pamh, res); sftppam_pamh = NULL; } #endif PRIVS_RELINQUISH pr_signals_unblock(); return 0; }
static void authenticate (const struct passwd *pw) { const struct passwd *lpw = NULL; const char *cp, *srvname = NULL; int retval; switch (su_mode) { case SU_MODE: srvname = simulate_login ? PAM_SRVNAME_SU_L : PAM_SRVNAME_SU; break; case RUNUSER_MODE: srvname = simulate_login ? PAM_SRVNAME_RUNUSER_L : PAM_SRVNAME_RUNUSER; break; default: abort(); break; } retval = pam_start (srvname, pw->pw_name, &conv, &pamh); if (is_pam_failure(retval)) goto done; if (isatty (0) && (cp = ttyname (0)) != NULL) { const char *tty; if (strncmp (cp, "/dev/", 5) == 0) tty = cp + 5; else tty = cp; retval = pam_set_item (pamh, PAM_TTY, tty); if (is_pam_failure(retval)) goto done; } lpw = current_getpwuid (); if (lpw && lpw->pw_name) { retval = pam_set_item (pamh, PAM_RUSER, (const void *) lpw->pw_name); if (is_pam_failure(retval)) goto done; } if (su_mode == RUNUSER_MODE) { /* * This is the only difference between runuser(1) and su(1). The command * runuser(1) does not required authentication, because user is root. */ if (restricted) errx(EXIT_FAILURE, _("may not be used by non-root users")); return; } retval = pam_authenticate (pamh, 0); if (is_pam_failure(retval)) goto done; retval = pam_acct_mgmt (pamh, 0); if (retval == PAM_NEW_AUTHTOK_REQD) { /* Password has expired. Offer option to change it. */ retval = pam_chauthtok (pamh, PAM_CHANGE_EXPIRED_AUTHTOK); } done: log_syslog(pw, !is_pam_failure(retval)); if (is_pam_failure(retval)) { const char *msg; log_btmp(pw); msg = pam_strerror(pamh, retval); pam_end(pamh, retval); sleep (getlogindefs_num ("FAIL_DELAY", 1)); errx (EXIT_FAILURE, "%s", msg?msg:_("incorrect password")); } }
int start_stop_daemon(int argc, char **argv) { int devnull_fd = -1; #ifdef TIOCNOTTY int tty_fd = -1; #endif #ifdef HAVE_PAM pam_handle_t *pamh = NULL; int pamr; const char *const *pamenv = NULL; #endif int opt; bool start = false; bool stop = false; bool oknodo = false; bool test = false; char *exec = NULL; char *startas = NULL; char *name = NULL; char *pidfile = NULL; char *retry = NULL; int sig = -1; int nicelevel = 0, ionicec = -1, ioniced = 0; bool background = false; bool makepidfile = false; bool interpreted = false; bool progress = false; uid_t uid = 0; gid_t gid = 0; char *home = NULL; int tid = 0; char *redirect_stderr = NULL; char *redirect_stdout = NULL; int stdout_fd; int stderr_fd; pid_t pid, spid; int i; char *svcname = getenv("RC_SVCNAME"); RC_STRINGLIST *env_list; RC_STRING *env; char *tmp, *newpath, *np; char *p; char *token; char exec_file[PATH_MAX]; struct passwd *pw; struct group *gr; char line[130]; FILE *fp; size_t len; mode_t numask = 022; char **margv; unsigned int start_wait = 0; TAILQ_INIT(&schedule); #ifdef DEBUG_MEMORY atexit(cleanup); #endif signal_setup(SIGINT, handle_signal); signal_setup(SIGQUIT, handle_signal); signal_setup(SIGTERM, handle_signal); if ((tmp = getenv("SSD_NICELEVEL"))) if (sscanf(tmp, "%d", &nicelevel) != 1) eerror("%s: invalid nice level `%s' (SSD_NICELEVEL)", applet, tmp); /* Get our user name and initial dir */ p = getenv("USER"); home = getenv("HOME"); if (home == NULL || p == NULL) { pw = getpwuid(getuid()); if (pw != NULL) { if (p == NULL) setenv("USER", pw->pw_name, 1); if (home == NULL) { setenv("HOME", pw->pw_dir, 1); home = pw->pw_dir; } } } while ((opt = getopt_long(argc, argv, getoptstring, longopts, (int *) 0)) != -1) switch (opt) { case 'I': /* --ionice */ if (sscanf(optarg, "%d:%d", &ionicec, &ioniced) == 0) eerrorx("%s: invalid ionice `%s'", applet, optarg); if (ionicec == 0) ioniced = 0; else if (ionicec == 3) ioniced = 7; ionicec <<= 13; /* class shift */ break; case 'K': /* --stop */ stop = true; break; case 'N': /* --nice */ if (sscanf(optarg, "%d", &nicelevel) != 1) eerrorx("%s: invalid nice level `%s'", applet, optarg); break; case 'P': /* --progress */ progress = true; break; case 'R': /* --retry <schedule>|<timeout> */ retry = optarg; break; case 'S': /* --start */ start = true; break; case 'b': /* --background */ background = true; break; case 'c': /* --chuid <username>|<uid> */ /* DEPRECATED */ ewarn("WARNING: -c/--chuid is deprecated and will be removed in the future, please use -u/--user instead"); case 'u': /* --user <username>|<uid> */ { p = optarg; tmp = strsep(&p, ":"); changeuser = xstrdup(tmp); if (sscanf(tmp, "%d", &tid) != 1) pw = getpwnam(tmp); else pw = getpwuid((uid_t)tid); if (pw == NULL) eerrorx("%s: user `%s' not found", applet, tmp); uid = pw->pw_uid; home = pw->pw_dir; unsetenv("HOME"); if (pw->pw_dir) setenv("HOME", pw->pw_dir, 1); unsetenv("USER"); if (pw->pw_name) setenv("USER", pw->pw_name, 1); if (gid == 0) gid = pw->pw_gid; if (p) { tmp = strsep (&p, ":"); if (sscanf(tmp, "%d", &tid) != 1) gr = getgrnam(tmp); else gr = getgrgid((gid_t) tid); if (gr == NULL) eerrorx("%s: group `%s'" " not found", applet, tmp); gid = gr->gr_gid; } } break; case 'd': /* --chdir /new/dir */ ch_dir = optarg; break; case 'e': /* --env */ putenv(optarg); break; case 'g': /* --group <group>|<gid> */ if (sscanf(optarg, "%d", &tid) != 1) gr = getgrnam(optarg); else gr = getgrgid((gid_t)tid); if (gr == NULL) eerrorx("%s: group `%s' not found", applet, optarg); gid = gr->gr_gid; break; case 'i': /* --interpreted */ interpreted = true; break; case 'k': if (parse_mode(&numask, optarg)) eerrorx("%s: invalid mode `%s'", applet, optarg); break; case 'm': /* --make-pidfile */ makepidfile = true; break; case 'n': /* --name <process-name> */ name = optarg; break; case 'o': /* --oknodo */ /* DEPRECATED */ ewarn("WARNING: -o/--oknodo is deprecated and will be removed in the future"); oknodo = true; break; case 'p': /* --pidfile <pid-file> */ pidfile = optarg; break; case 's': /* --signal <signal> */ sig = parse_signal(optarg); break; case 't': /* --test */ test = true; break; case 'r': /* --chroot /new/root */ ch_root = optarg; break; case 'a': /* --startas <name> */ /* DEPRECATED */ ewarn("WARNING: -a/--startas is deprecated and will be removed in the future, please use -x/--exec or -n/--name instead"); startas = optarg; break; case 'w': if (sscanf(optarg, "%d", &start_wait) != 1) eerrorx("%s: `%s' not a number", applet, optarg); break; case 'x': /* --exec <executable> */ exec = optarg; break; case '1': /* --stdout /path/to/stdout.lgfile */ redirect_stdout = optarg; break; case '2': /* --stderr /path/to/stderr.logfile */ redirect_stderr = optarg; break; case_RC_COMMON_GETOPT } endpwent(); argc -= optind; argv += optind; /* Allow start-stop-daemon --signal HUP --exec /usr/sbin/dnsmasq * instead of forcing --stop --oknodo as well */ if (!start && !stop && sig != SIGINT && sig != SIGTERM && sig != SIGQUIT && sig != SIGKILL) oknodo = true; if (!exec) exec = startas; else if (!name) name = startas; if (!exec) { exec = *argv; if (!exec) exec = name; if (name && start) *argv = name; } else if (name) *--argv = name; else if (exec) *--argv = exec; if (stop || sig != -1) { if (sig == -1) sig = SIGTERM; if (!*argv && !pidfile && !name && !uid) eerrorx("%s: --stop needs --exec, --pidfile," " --name or --user", applet); if (background) eerrorx("%s: --background is only relevant with" " --start", applet); if (makepidfile) eerrorx("%s: --make-pidfile is only relevant with" " --start", applet); if (redirect_stdout || redirect_stderr) eerrorx("%s: --stdout and --stderr are only relevant" " with --start", applet); } else { if (!exec) eerrorx("%s: nothing to start", applet); if (makepidfile && !pidfile) eerrorx("%s: --make-pidfile is only relevant with" " --pidfile", applet); if ((redirect_stdout || redirect_stderr) && !background) eerrorx("%s: --stdout and --stderr are only relevant" " with --background", applet); } /* Expand ~ */ if (ch_dir && *ch_dir == '~') ch_dir = expand_home(home, ch_dir); if (ch_root && *ch_root == '~') ch_root = expand_home(home, ch_root); if (exec) { if (*exec == '~') exec = expand_home(home, exec); /* Validate that the binary exists if we are starting */ if (*exec == '/' || *exec == '.') { /* Full or relative path */ if (ch_root) snprintf(exec_file, sizeof(exec_file), "%s/%s", ch_root, exec); else snprintf(exec_file, sizeof(exec_file), "%s", exec); } else { /* Something in $PATH */ p = tmp = xstrdup(getenv("PATH")); *exec_file = '\0'; while ((token = strsep(&p, ":"))) { if (ch_root) snprintf(exec_file, sizeof(exec_file), "%s/%s/%s", ch_root, token, exec); else snprintf(exec_file, sizeof(exec_file), "%s/%s", token, exec); if (exists(exec_file)) break; *exec_file = '\0'; } free(tmp); } } if (start && !exists(exec_file)) { eerror("%s: %s does not exist", applet, *exec_file ? exec_file : exec); exit(EXIT_FAILURE); } /* If we don't have a pidfile we should check if it's interpreted * or not. If it we, we need to pass the interpreter through * to our daemon calls to find it correctly. */ if (interpreted && !pidfile) { fp = fopen(exec_file, "r"); if (fp) { p = fgets(line, sizeof(line), fp); fclose(fp); if (p != NULL && line[0] == '#' && line[1] == '!') { p = line + 2; /* Strip leading spaces */ while (*p == ' ' || *p == '\t') p++; /* Remove the trailing newline */ len = strlen(p) - 1; if (p[len] == '\n') p[len] = '\0'; token = strsep(&p, " "); strncpy(exec_file, token, sizeof(exec_file)); opt = 0; for (nav = argv; *nav; nav++) opt++; nav = xmalloc(sizeof(char *) * (opt + 3)); nav[0] = exec_file; len = 1; if (p) nav[len++] = p; for (i = 0; i < opt; i++) nav[i + len] = argv[i]; nav[i + len] = '\0'; } } } margv = nav ? nav : argv; if (stop || sig != -1) { if (sig == -1) sig = SIGTERM; if (!stop) oknodo = true; if (retry) parse_schedule(retry, sig); else if (test || oknodo) parse_schedule("0", sig); else parse_schedule(NULL, sig); i = run_stop_schedule(exec, (const char *const *)margv, pidfile, uid, test, progress); if (i < 0) /* We failed to stop something */ exit(EXIT_FAILURE); if (test || oknodo) return i > 0 ? EXIT_SUCCESS : EXIT_FAILURE; /* Even if we have not actually killed anything, we should * remove information about it as it may have unexpectedly * crashed out. We should also return success as the end * result would be the same. */ if (pidfile && exists(pidfile)) unlink(pidfile); if (svcname) rc_service_daemon_set(svcname, exec, (const char *const *)argv, pidfile, false); exit(EXIT_SUCCESS); } if (pidfile) pid = get_pid(pidfile); else pid = 0; if (do_stop(exec, (const char * const *)margv, pid, uid, 0, test) > 0) eerrorx("%s: %s is already running", applet, exec); if (test) { if (rc_yesno(getenv("EINFO_QUIET"))) exit (EXIT_SUCCESS); einfon("Would start"); while (argc-- >= 0) printf(" %s", *argv++); printf("\n"); eindent(); if (uid != 0) einfo("as user id %d", uid); if (gid != 0) einfo("as group id %d", gid); if (ch_root) einfo("in root `%s'", ch_root); if (ch_dir) einfo("in dir `%s'", ch_dir); if (nicelevel != 0) einfo("with a priority of %d", nicelevel); if (name) einfo ("with a process name of %s", name); eoutdent(); exit(EXIT_SUCCESS); } ebeginv("Detaching to start `%s'", exec); eindentv(); /* Remove existing pidfile */ if (pidfile) unlink(pidfile); if (background) signal_setup(SIGCHLD, handle_signal); if ((pid = fork()) == -1) eerrorx("%s: fork: %s", applet, strerror(errno)); /* Child process - lets go! */ if (pid == 0) { pid_t mypid = getpid(); umask(numask); #ifdef TIOCNOTTY tty_fd = open("/dev/tty", O_RDWR); #endif devnull_fd = open("/dev/null", O_RDWR); if (nicelevel) { if (setpriority(PRIO_PROCESS, mypid, nicelevel) == -1) eerrorx("%s: setpritory %d: %s", applet, nicelevel, strerror(errno)); } if (ionicec != -1 && ioprio_set(1, mypid, ionicec | ioniced) == -1) eerrorx("%s: ioprio_set %d %d: %s", applet, ionicec, ioniced, strerror(errno)); if (ch_root && chroot(ch_root) < 0) eerrorx("%s: chroot `%s': %s", applet, ch_root, strerror(errno)); if (ch_dir && chdir(ch_dir) < 0) eerrorx("%s: chdir `%s': %s", applet, ch_dir, strerror(errno)); if (makepidfile && pidfile) { fp = fopen(pidfile, "w"); if (! fp) eerrorx("%s: fopen `%s': %s", applet, pidfile, strerror(errno)); fprintf(fp, "%d\n", mypid); fclose(fp); } #ifdef HAVE_PAM if (changeuser != NULL) { pamr = pam_start("start-stop-daemon", changeuser, &conv, &pamh); if (pamr == PAM_SUCCESS) pamr = pam_acct_mgmt(pamh, PAM_SILENT); if (pamr == PAM_SUCCESS) pamr = pam_open_session(pamh, PAM_SILENT); if (pamr != PAM_SUCCESS) eerrorx("%s: pam error: %s", applet, pam_strerror(pamh, pamr)); } #endif if (gid && setgid(gid)) eerrorx("%s: unable to set groupid to %d", applet, gid); if (changeuser && initgroups(changeuser, gid)) eerrorx("%s: initgroups (%s, %d)", applet, changeuser, gid); if (uid && setuid(uid)) eerrorx ("%s: unable to set userid to %d", applet, uid); /* Close any fd's to the passwd database */ endpwent(); #ifdef TIOCNOTTY ioctl(tty_fd, TIOCNOTTY, 0); close(tty_fd); #endif /* Clean the environment of any RC_ variables */ env_list = rc_stringlist_new(); i = 0; while (environ[i]) rc_stringlist_add(env_list, environ[i++]); #ifdef HAVE_PAM if (changeuser != NULL) { pamenv = (const char *const *)pam_getenvlist(pamh); if (pamenv) { while (*pamenv) { /* Don't add strings unless they set a var */ if (strchr(*pamenv, '=')) putenv(xstrdup(*pamenv)); else unsetenv(*pamenv); pamenv++; } } } #endif TAILQ_FOREACH(env, env_list, entries) { if ((strncmp(env->value, "RC_", 3) == 0 && strncmp(env->value, "RC_SERVICE=", 10) != 0 && strncmp(env->value, "RC_SVCNAME=", 10) != 0) || strncmp(env->value, "SSD_NICELEVEL=", 14) == 0) { p = strchr(env->value, '='); *p = '\0'; unsetenv(env->value); continue; } } rc_stringlist_free(env_list); /* For the path, remove the rcscript bin dir from it */ if ((token = getenv("PATH"))) { len = strlen(token); newpath = np = xmalloc(len + 1); while (token && *token) { p = strchr(token, ':'); if (p) { *p++ = '\0'; while (*p == ':') p++; } if (strcmp(token, RC_LIBEXECDIR "/bin") != 0 && strcmp(token, RC_LIBEXECDIR "/sbin") != 0) { len = strlen(token); if (np != newpath) *np++ = ':'; memcpy(np, token, len); np += len; } token = p; } *np = '\0'; unsetenv("PATH"); setenv("PATH", newpath, 1); } stdout_fd = devnull_fd; stderr_fd = devnull_fd; if (redirect_stdout) { if ((stdout_fd = open(redirect_stdout, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR)) == -1) eerrorx("%s: unable to open the logfile" " for stdout `%s': %s", applet, redirect_stdout, strerror(errno)); } if (redirect_stderr) { if ((stderr_fd = open(redirect_stderr, O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR)) == -1) eerrorx("%s: unable to open the logfile" " for stderr `%s': %s", applet, redirect_stderr, strerror(errno)); } /* We don't redirect stdin as some daemons may need it */ if (background || redirect_stdout || rc_yesno(getenv("EINFO_QUIET"))) dup2(stdout_fd, STDOUT_FILENO); if (background || redirect_stderr || rc_yesno(getenv("EINFO_QUIET"))) dup2(stderr_fd, STDERR_FILENO); for (i = getdtablesize() - 1; i >= 3; --i) close(i); setsid(); execvp(exec, argv); #ifdef HAVE_PAM if (changeuser != NULL && pamr == PAM_SUCCESS) pam_close_session(pamh, PAM_SILENT); #endif eerrorx("%s: failed to exec `%s': %s", applet, exec,strerror(errno)); }