diff options
author | markm <markm@FreeBSD.org> | 2001-08-26 17:41:13 +0000 |
---|---|---|
committer | markm <markm@FreeBSD.org> | 2001-08-26 17:41:13 +0000 |
commit | 67fcc4111ae36921c649ec85c8cab3bbf50fd738 (patch) | |
tree | 3cf7d6b063084a826d4901572e4ab8447645201d /lib/libpam/modules/pam_unix/pam_unix.c | |
parent | e52902c064d70be6f3fbee06988b2b991e62bd23 (diff) | |
download | FreeBSD-src-67fcc4111ae36921c649ec85c8cab3bbf50fd738.zip FreeBSD-src-67fcc4111ae36921c649ec85c8cab3bbf50fd738.tar.gz |
Big module makeover; improve logging, standardise variable names,
introduce ability to change passwords for both "usual" Unix methods
and NIS.
Diffstat (limited to 'lib/libpam/modules/pam_unix/pam_unix.c')
-rw-r--r-- | lib/libpam/modules/pam_unix/pam_unix.c | 479 |
1 files changed, 460 insertions, 19 deletions
diff --git a/lib/libpam/modules/pam_unix/pam_unix.c b/lib/libpam/modules/pam_unix/pam_unix.c index 61c462c..f63acc0 100644 --- a/lib/libpam/modules/pam_unix/pam_unix.c +++ b/lib/libpam/modules/pam_unix/pam_unix.c @@ -28,6 +28,12 @@ #include <sys/types.h> #include <sys/time.h> +#ifdef YP +#include <rpc/rpc.h> +#include <rpcsvc/yp_prot.h> +#include <rpcsvc/ypclnt.h> +#include <rpcsvc/yppasswd.h> +#endif #include <login_cap.h> #include <pwd.h> #include <stdlib.h> @@ -35,42 +41,72 @@ #include <stdio.h> #include <unistd.h> +#include <pw_copy.h> +#include <pw_util.h> + +#ifdef YP +#include <pw_yp.h> +#include "yppasswd_private.h" +#endif + #define PAM_SM_AUTH #define PAM_SM_ACCOUNT +#define PAM_SM_SESSION +#define PAM_SM_PASSWORD + #include <security/pam_modules.h> #include "pam_mod_misc.h" -#define PASSWORD_PROMPT "Password:" -#define DEFAULT_WARN (2L * 7L * 86400L) /* Two weeks */ +#define USER_PROMPT "Username: " +#define PASSWORD_PROMPT "Password: " +#define PASSWORD_PROMPT_EXPIRED "\nPassword expired\nOld Password: " +#define NEW_PASSWORD_PROMPT_1 "New Password: " +#define NEW_PASSWORD_PROMPT_2 "New Password (again): " +#define PASSWORD_HASH "md5" +#define DEFAULT_WARN (2L * 7L * 86400L) /* Two weeks */ +#define MAX_TRIES 3 -enum { PAM_OPT_AUTH_AS_SELF=PAM_OPT_STD_MAX, PAM_OPT_NULLOK }; +enum { PAM_OPT_AUTH_AS_SELF=PAM_OPT_STD_MAX, PAM_OPT_NULLOK, PAM_OPT_LOCAL_PASS, PAM_OPT_NIS_PASS }; static struct opttab other_options[] = { { "auth_as_self", PAM_OPT_AUTH_AS_SELF }, { "nullok", PAM_OPT_NULLOK }, + { "local_pass", PAM_OPT_LOCAL_PASS }, + { "nis_pass", PAM_OPT_NIS_PASS }, { NULL, 0 } }; +#ifdef YP +int pam_use_yp = 0; +int yp_errno = YP_TRUE; +#endif + +char *tempname = NULL; +static int local_passwd(const char *user, const char *pass); +#ifdef YP +static int yp_passwd(const char *user, const char *pass); +#endif + /* * authentication management */ - PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) { + login_cap_t *lc; struct options options; struct passwd *pwd; int retval; - const char *password, *user; - char *encrypted; + const char *pass, *user; + char *encrypted, *password_prompt; pam_std_option(&options, other_options, argc, argv); PAM_LOG("Options processed"); if (pam_test_option(&options, PAM_OPT_AUTH_AS_SELF, NULL)) - pwd = getpwuid(getuid()); + pwd = getpwnam(getlogin()); else { retval = pam_get_user(pamh, &user, NULL); if (retval != PAM_SUCCESS) @@ -80,6 +116,12 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) PAM_LOG("Got user: %s", user); + lc = login_getclass(NULL); + password_prompt = login_getcapstr(lc, "passwd_prompt", + PASSWORD_PROMPT, PASSWORD_PROMPT); + login_close(lc); + lc = NULL; + if (pwd != NULL) { PAM_LOG("Doing real authentication"); @@ -94,18 +136,18 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) PAM_RETURN(PAM_SUCCESS); } else { - retval = pam_get_pass(pamh, &password, PASSWORD_PROMPT, + retval = pam_get_pass(pamh, &pass, password_prompt, &options); if (retval != PAM_SUCCESS) PAM_RETURN(retval); PAM_LOG("Got password"); } - encrypted = crypt(password, pwd->pw_passwd); - if (password[0] == '\0' && pwd->pw_passwd[0] != '\0') + encrypted = crypt(pass, pwd->pw_passwd); + if (pass[0] == '\0' && pwd->pw_passwd[0] != '\0') encrypted = ":"; - PAM_LOG("Encrypted passwords are: %s & %s", encrypted, - pwd->pw_passwd); + PAM_LOG("Encrypted password 1 is: %s", encrypted); + PAM_LOG("Encrypted password 2 is: %s", pwd->pw_passwd); retval = strcmp(encrypted, pwd->pw_passwd) == 0 ? PAM_SUCCESS : PAM_AUTH_ERR; @@ -118,12 +160,12 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) * User unknown. * Encrypt a dummy password so as to not give away too much. */ - retval = pam_get_pass(pamh, &password, PASSWORD_PROMPT, + retval = pam_get_pass(pamh, &pass, password_prompt, &options); if (retval != PAM_SUCCESS) PAM_RETURN(retval); PAM_LOG("Got password"); - crypt(password, "xx"); + crypt(pass, "xx"); retval = PAM_AUTH_ERR; } @@ -131,22 +173,29 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) * The PAM infrastructure will obliterate the cleartext * password before returning to the application. */ + if (retval != PAM_SUCCESS) + PAM_VERBOSE_ERROR("UNIX authentication refused"); + PAM_RETURN(retval); } PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) { - return PAM_SUCCESS; + struct options options; + + pam_std_option(&options, other_options, argc, argv); + + PAM_LOG("Options processed"); + + PAM_RETURN(PAM_SUCCESS); } /* * account management - * - * check pw_change and pw_expire fields */ -PAM_EXTERN -int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv) +PAM_EXTERN int +pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv) { struct options options; struct passwd *pw; @@ -214,4 +263,396 @@ int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv) PAM_RETURN(retval); } +/* + * session management + * + * logging only + */ +PAM_EXTERN int +pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv) +{ + struct options options; + + pam_std_option(&options, other_options, argc, argv); + + PAM_LOG("Options processed"); + + PAM_RETURN(PAM_SUCCESS); +} + +PAM_EXTERN int +pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv) +{ + struct options options; + + pam_std_option(&options, other_options, argc, argv); + + PAM_LOG("Options processed"); + + PAM_RETURN(PAM_SUCCESS); +} + +/* + * password management + * + * standard Unix and NIS password changing + */ +PAM_EXTERN int +pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv) +{ + struct options options; + struct passwd *pwd; + int retval, retry, res, got; + const char *user, *pass; + char *new_pass, *new_pass_, *encrypted; + + pam_std_option(&options, other_options, argc, argv); + + PAM_LOG("Options processed"); + + if (pam_test_option(&options, PAM_OPT_AUTH_AS_SELF, NULL)) + pwd = getpwnam(getlogin()); + else { + retval = pam_get_user(pamh, &user, NULL); + if (retval != PAM_SUCCESS) + PAM_RETURN(retval); + pwd = getpwnam(user); + } + + PAM_LOG("Got user: %s", user); + + if (flags & PAM_PRELIM_CHECK) { + + PAM_LOG("PRELIM round; checking user password"); + + if (pwd->pw_passwd[0] == '\0' + && pam_test_option(&options, PAM_OPT_NULLOK, NULL)) { + /* + * No password case. XXX Are we giving too much away + * by not prompting for a password? + */ + PAM_LOG("No password, and null password OK"); + PAM_RETURN(PAM_SUCCESS); + } + else { + retval = pam_get_pass(pamh, &pass, + PASSWORD_PROMPT_EXPIRED, &options); + if (retval != PAM_SUCCESS) + PAM_RETURN(retval); + PAM_LOG("Got password: %s", pass); + } + encrypted = crypt(pass, pwd->pw_passwd); + if (pass[0] == '\0' && pwd->pw_passwd[0] != '\0') + encrypted = ":"; + + PAM_LOG("Encrypted password 1 is: %s", encrypted); + PAM_LOG("Encrypted password 2 is: %s", pwd->pw_passwd); + + if (strcmp(encrypted, pwd->pw_passwd) != 0) + PAM_RETURN(PAM_AUTH_ERR); + + retval = pam_set_item(pamh, PAM_OLDAUTHTOK, (const void *)pass); + pass = NULL; + if (retval != PAM_SUCCESS) + PAM_RETURN(retval); + + PAM_LOG("Stashed old password"); + + retval = pam_set_item(pamh, PAM_AUTHTOK, (const void *)pass); + if (retval != PAM_SUCCESS) + PAM_RETURN(retval); + + PAM_LOG("Voided old password"); + + PAM_RETURN(PAM_SUCCESS); + } + else if (flags & PAM_UPDATE_AUTHTOK) { + PAM_LOG("UPDATE round; checking user password"); + + retval = pam_get_item(pamh, PAM_OLDAUTHTOK, + (const void **)&pass); + if (retval != PAM_SUCCESS) + PAM_RETURN(retval); + + PAM_LOG("Got old password: %s", pass); + + got = 0; + retry = 0; + while (retry++ < MAX_TRIES) { + new_pass = NULL; + retval = pam_prompt(pamh, PAM_PROMPT_ECHO_OFF, + NEW_PASSWORD_PROMPT_1, &new_pass); + + if (new_pass == NULL) + new_pass = ""; + + if (retval == PAM_SUCCESS) { + new_pass_ = NULL; + retval = pam_prompt(pamh, PAM_PROMPT_ECHO_OFF, + NEW_PASSWORD_PROMPT_2, &new_pass_); + + if (new_pass_ == NULL) + new_pass_ = ""; + + if (retval == PAM_SUCCESS) { + if (strcmp(new_pass, new_pass_) == 0) { + got = 1; + break; + } + else + PAM_VERBOSE_ERROR("Password mismatch"); + } + } + } + + if (!got) { + PAM_VERBOSE_ERROR("Unable to get valid password"); + PAM_RETURN(PAM_PERM_DENIED); + } + + PAM_LOG("Got new password: %s", new_pass); + +#ifdef YP + /* If NIS is set in the passwd database, use it */ + res = use_yp((char *)user, 0, 0); + if (res == USER_YP_ONLY) { + if (!pam_test_option(&options, PAM_OPT_LOCAL_PASS, + NULL)) + retval = yp_passwd(user, new_pass); + else { + /* Reject 'local' flag if NIS is on and the user + * is not local + */ + retval = PAM_PERM_DENIED; + PAM_LOG("Unknown local user: %s", user); + } + } + else if (res == USER_LOCAL_ONLY) { + if (!pam_test_option(&options, PAM_OPT_NIS_PASS, NULL)) + retval = local_passwd(user, new_pass); + else { + /* Reject 'nis' flag if user is only local */ + retval = PAM_PERM_DENIED; + PAM_LOG("Unknown NIS user: %s", user); + } + } + else if (res == USER_YP_AND_LOCAL) { + if (pam_test_option(&options, PAM_OPT_NIS_PASS, NULL)) + retval = yp_passwd(user, new_pass); + else + retval = local_passwd(user, new_pass); + } + else + retval = PAM_ABORT; /* Bad juju */ +#else + retval = local_passwd(user, new_pass); +#endif + + /* XXX wipe the mem as well */ + pass = NULL; + new_pass = NULL; + } + else { + /* Very bad juju */ + retval = PAM_ABORT; + PAM_LOG("Illegal 'flags'"); + } + + PAM_RETURN(retval); +} + +/* Mostly stolen from passwd(1)'s local_passwd.c - markm */ + +static unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */ + "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +static void +to64(char *s, long v, int n) +{ + while (--n >= 0) { + *s++ = itoa64[v&0x3f]; + v >>= 6; + } +} + +static int +local_passwd(const char *user, const char *pass) +{ + login_cap_t * lc; + struct passwd *pwd; + struct timeval tv; + int pfd, tfd; + char *crypt_type, salt[32]; + + pwd = getpwnam(user); + if (pwd == NULL) + return(PAM_ABORT); /* Really bad things */ + +#ifdef YP + pwd = (struct passwd *)&local_password; +#endif + pw_init(); + + pwd->pw_change = 0; + lc = login_getclass(NULL); + crypt_type = login_getcapstr(lc, "passwd_format", + PASSWORD_HASH, PASSWORD_HASH); + if (login_setcryptfmt(lc, crypt_type, NULL) == NULL) + syslog(LOG_ERR, "cannot set password cipher"); + login_close(lc); + /* Salt suitable for anything */ + srandomdev(); + gettimeofday(&tv, 0); + to64(&salt[0], random(), 3); + to64(&salt[3], tv.tv_usec, 3); + to64(&salt[6], tv.tv_sec, 2); + to64(&salt[8], random(), 5); + to64(&salt[13], random(), 5); + to64(&salt[17], random(), 5); + to64(&salt[22], random(), 5); + salt[27] = '\0'; + + pwd->pw_passwd = crypt(pass, salt); + + pfd = pw_lock(); + tfd = pw_tmp(); + pw_copy(pfd, tfd, pwd); + + if (!pw_mkdb((char *)user)) + pw_error((char *)NULL, 0, 1); + + return PAM_SUCCESS; +} + +#ifdef YP +/* Stolen from src/usr.bin/passwd/yp_passwd.c, carrying copyrights of: + * Copyright (c) 1992/3 Theo de Raadt <deraadt@fsa.ca> + * Copyright (c) 1994 Olaf Kirch <okir@monad.swb.de> + * Copyright (c) 1995 Bill Paul <wpaul@ctr.columbia.edu> + */ +int +yp_passwd(const char *user, const char *pass) +{ + struct master_yppasswd master_yppasswd; + struct passwd *pwd; + struct rpc_err err; + struct timeval tv; + struct yppasswd yppasswd; + CLIENT *clnt; + login_cap_t *lc; + int *status; + uid_t uid; + char *master, *sockname = YP_SOCKNAME, salt[32]; + + _use_yp = 1; + + uid = getuid(); + + master = get_yp_master(1); + if (master == NULL) + return PAM_ABORT; /* Major disaster */ + + /* + * It is presumed that by the time we get here, use_yp() + * has been called and that we have verified that the user + * actually exists. This being the case, the yp_password + * stucture has already been filled in for us. + */ + + /* Use the correct password */ + pwd = (struct passwd *)&yp_password; + + pwd->pw_change = 0; + + /* Initialize password information */ + if (suser_override) { + master_yppasswd.newpw.pw_passwd = strdup(pwd->pw_passwd); + master_yppasswd.newpw.pw_name = strdup(pwd->pw_name); + master_yppasswd.newpw.pw_uid = pwd->pw_uid; + master_yppasswd.newpw.pw_gid = pwd->pw_gid; + master_yppasswd.newpw.pw_expire = pwd->pw_expire; + master_yppasswd.newpw.pw_change = pwd->pw_change; + master_yppasswd.newpw.pw_fields = pwd->pw_fields; + master_yppasswd.newpw.pw_gecos = strdup(pwd->pw_gecos); + master_yppasswd.newpw.pw_dir = strdup(pwd->pw_dir); + master_yppasswd.newpw.pw_shell = strdup(pwd->pw_shell); + master_yppasswd.newpw.pw_class = pwd->pw_class != NULL ? + strdup(pwd->pw_class) : ""; + master_yppasswd.oldpass = ""; + master_yppasswd.domain = yp_domain; + } else { + yppasswd.newpw.pw_passwd = strdup(pwd->pw_passwd); + yppasswd.newpw.pw_name = strdup(pwd->pw_name); + yppasswd.newpw.pw_uid = pwd->pw_uid; + yppasswd.newpw.pw_gid = pwd->pw_gid; + yppasswd.newpw.pw_gecos = strdup(pwd->pw_gecos); + yppasswd.newpw.pw_dir = strdup(pwd->pw_dir); + yppasswd.newpw.pw_shell = strdup(pwd->pw_shell); + yppasswd.oldpass = ""; + } + + if (login_setcryptfmt(lc, "md5", NULL) == NULL) + syslog(LOG_ERR, "cannot set password cipher"); + login_close(lc); + /* Salt suitable for anything */ + srandomdev(); + gettimeofday(&tv, 0); + to64(&salt[0], random(), 3); + to64(&salt[3], tv.tv_usec, 3); + to64(&salt[6], tv.tv_sec, 2); + to64(&salt[8], random(), 5); + to64(&salt[13], random(), 5); + to64(&salt[17], random(), 5); + to64(&salt[22], random(), 5); + salt[27] = '\0'; + + if (suser_override) + master_yppasswd.newpw.pw_passwd = crypt(pass, salt); + else + yppasswd.newpw.pw_passwd = crypt(pass, salt); + + if (suser_override) { + if ((clnt = clnt_create(sockname, MASTER_YPPASSWDPROG, + MASTER_YPPASSWDVERS, "unix")) == NULL) { + syslog(LOG_ERR, + "Cannot contact rpc.yppasswdd on host %s: %s", + master, clnt_spcreateerror("")); + return PAM_ABORT; + } + } + else { + if ((clnt = clnt_create(master, YPPASSWDPROG, + YPPASSWDVERS, "udp")) == NULL) { + syslog(LOG_ERR, + "Cannot contact rpc.yppasswdd on host %s: %s", + master, clnt_spcreateerror("")); + return PAM_ABORT; + } + } + /* + * The yppasswd.x file said `unix authentication required', + * so I added it. This is the only reason it is in here. + * My yppasswdd doesn't use it, but maybe some others out there + * do. --okir + */ + clnt->cl_auth = authunix_create_default(); + + if (suser_override) + status = yppasswdproc_update_master_1(&master_yppasswd, clnt); + else + status = yppasswdproc_update_1(&yppasswd, clnt); + + clnt_geterr(clnt, &err); + + auth_destroy(clnt->cl_auth); + clnt_destroy(clnt); + + if (err.re_status != RPC_SUCCESS || status == NULL || *status) + return PAM_ABORT; + + return (err.re_status || status == NULL || *status) + ? PAM_ABORT : PAM_SUCCESS; +} +#endif /* YP */ + PAM_MODULE_ENTRY("pam_unix"); |