diff --git a/lib/libpam/modules/pam_krb5/pam_krb5.c b/lib/libpam/modules/pam_krb5/pam_krb5.c index 705b4521814d..a81ddff8007a 100644 --- a/lib/libpam/modules/pam_krb5/pam_krb5.c +++ b/lib/libpam/modules/pam_krb5/pam_krb5.c @@ -1,995 +1,960 @@ /*- * This pam_krb5 module contains code that is: * Copyright (c) Derrick J. Brashear, 1996. All rights reserved. * Copyright (c) Frank Cusack, 1999-2001. All rights reserved. * Copyright (c) Jacques A. Vidrine, 2000-2001. All rights reserved. * Copyright (c) Nicolas Williams, 2001. All rights reserved. * Copyright (c) Perot Systems Corporation, 2001. All rights reserved. * Copyright (c) Mark R V Murray, 2001. All rights reserved. * Copyright (c) Networks Associates Technology, Inc., 2002. * All rights reserved. * * Portions of this software were developed for the FreeBSD Project by * ThinkSec AS and NAI Labs, the Security Research Division of Network * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 * ("CBOSS"), as part of the DARPA CHATS research program. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notices, and the entire permission notice in its entirety, * including the disclaimer of warranties. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote * products derived from this software without specific prior * written permission. * * ALTERNATIVELY, this product may be distributed under the terms of * the GNU Public License, in which case the provisions of the GPL are * required INSTEAD OF the above restrictions. (This clause is * necessary due to a potential bad interaction between the GPL and * the restrictions contained in a BSD-style copyright.) * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #define PAM_SM_AUTH #define PAM_SM_ACCOUNT #define PAM_SM_PASSWORD #include #include #include +#include #define COMPAT_HEIMDAL /* #define COMPAT_MIT */ static int verify_krb_v5_tgt(krb5_context, krb5_ccache, char *, int); static void cleanup_cache(pam_handle_t *, void *, int); static const char *compat_princ_component(krb5_context, krb5_principal, int); static void compat_free_data_contents(krb5_context, krb5_data *); #define USER_PROMPT "Username: " #define PASSWORD_PROMPT "Password:" #define NEW_PASSWORD_PROMPT "New Password:" -enum { - PAM_OPT_AUTH_AS_SELF = PAM_OPT_STD_MAX, - PAM_OPT_CCACHE, - PAM_OPT_FORWARDABLE, - PAM_OPT_NO_CCACHE, - PAM_OPT_REUSE_CCACHE -}; - -static struct opttab other_options[] = { - { "auth_as_self", PAM_OPT_AUTH_AS_SELF }, - { "ccache", PAM_OPT_CCACHE }, - { "forwardable", PAM_OPT_FORWARDABLE }, - { "no_ccache", PAM_OPT_NO_CCACHE }, - { "reuse_ccache", PAM_OPT_REUSE_CCACHE }, - { NULL, 0 } -}; +#define PAM_OPT_CCACHE "ccache" +#define PAM_OPT_FORWARDABLE "forwardable" +#define PAM_OPT_NO_CCACHE "no_ccache" +#define PAM_OPT_REUSE_CCACHE "reuse_ccache" /* * authentication management */ PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags __unused, - int argc, const char *argv[]) + int argc __unused, const char *argv[] __unused) { krb5_error_code krbret; krb5_context pam_context; krb5_creds creds; krb5_principal princ; krb5_ccache ccache; krb5_get_init_creds_opt opts; - struct options options; struct passwd *pwd; int retval; const char *sourceuser, *user, *pass, *service; char *principal, *princ_name, *ccache_name, luser[32], *srvdup; - pam_std_option(&options, other_options, argc, argv); - - PAM_LOG("Options processed"); - retval = pam_get_user(pamh, &user, USER_PROMPT); if (retval != PAM_SUCCESS) return (retval); PAM_LOG("Got user: %s", user); retval = pam_get_item(pamh, PAM_RUSER, (const void **)&sourceuser); if (retval != PAM_SUCCESS) return (retval); PAM_LOG("Got ruser: %s", sourceuser); service = NULL; pam_get_item(pamh, PAM_SERVICE, (const void **)&service); if (service == NULL) service = "unknown"; PAM_LOG("Got service: %s", service); krbret = krb5_init_context(&pam_context); if (krbret != 0) { PAM_VERBOSE_ERROR("Kerberos 5 error"); return (PAM_SERVICE_ERR); } PAM_LOG("Context initialised"); krb5_get_init_creds_opt_init(&opts); - if (pam_test_option(&options, PAM_OPT_FORWARDABLE, NULL)) + if (openpam_get_option(pamh, PAM_OPT_FORWARDABLE)) krb5_get_init_creds_opt_set_forwardable(&opts, 1); PAM_LOG("Credentials initialised"); krbret = krb5_cc_register(pam_context, &krb5_mcc_ops, FALSE); if (krbret != 0 && krbret != KRB5_CC_TYPE_EXISTS) { PAM_VERBOSE_ERROR("Kerberos 5 error"); retval = PAM_SERVICE_ERR; goto cleanup3; } PAM_LOG("Done krb5_cc_register()"); /* Get principal name */ - if (pam_test_option(&options, PAM_OPT_AUTH_AS_SELF, NULL)) + if (openpam_get_option(pamh, PAM_OPT_AUTH_AS_SELF)) asprintf(&principal, "%s/%s", sourceuser, user); else principal = strdup(user); PAM_LOG("Created principal: %s", principal); krbret = krb5_parse_name(pam_context, principal, &princ); free(principal); if (krbret != 0) { PAM_LOG("Error krb5_parse_name(): %s", krb5_get_err_text(pam_context, krbret)); PAM_VERBOSE_ERROR("Kerberos 5 error"); retval = PAM_SERVICE_ERR; goto cleanup3; } PAM_LOG("Done krb5_parse_name()"); /* Now convert the principal name into something human readable */ princ_name = NULL; krbret = krb5_unparse_name(pam_context, princ, &princ_name); if (krbret != 0) { PAM_LOG("Error krb5_unparse_name(): %s", krb5_get_err_text(pam_context, krbret)); PAM_VERBOSE_ERROR("Kerberos 5 error"); retval = PAM_SERVICE_ERR; goto cleanup2; } PAM_LOG("Got principal: %s", princ_name); /* Get password */ retval = pam_get_authtok(pamh, PAM_AUTHTOK, &pass, PASSWORD_PROMPT); if (retval != PAM_SUCCESS) goto cleanup2; PAM_LOG("Got password"); /* Verify the local user exists (AFTER getting the password) */ if (strchr(user, '@')) { /* get a local account name for this principal */ krbret = krb5_aname_to_localname(pam_context, princ, sizeof(luser), luser); if (krbret != 0) { PAM_VERBOSE_ERROR("Kerberos 5 error"); PAM_LOG("Error krb5_aname_to_localname(): %s", krb5_get_err_text(pam_context, krbret)); retval = PAM_USER_UNKNOWN; goto cleanup2; } retval = pam_set_item(pamh, PAM_USER, luser); if (retval != PAM_SUCCESS) goto cleanup2; retval = pam_get_item(pamh, PAM_USER, (const void **)&user); if (retval != PAM_SUCCESS) goto cleanup2; PAM_LOG("PAM_USER Redone"); } pwd = getpwnam(user); if (pwd == NULL) { retval = PAM_USER_UNKNOWN; goto cleanup2; } PAM_LOG("Done getpwnam()"); /* Get a TGT */ memset(&creds, 0, sizeof(krb5_creds)); krbret = krb5_get_init_creds_password(pam_context, &creds, princ, pass, NULL, pamh, 0, NULL, &opts); if (krbret != 0) { PAM_VERBOSE_ERROR("Kerberos 5 error"); PAM_LOG("Error krb5_get_init_creds_password(): %s", krb5_get_err_text(pam_context, krbret)); retval = PAM_AUTH_ERR; goto cleanup2; } PAM_LOG("Got TGT"); /* Generate a temporary cache */ krbret = krb5_cc_gen_new(pam_context, &krb5_mcc_ops, &ccache); if (krbret != 0) { PAM_VERBOSE_ERROR("Kerberos 5 error"); PAM_LOG("Error krb5_cc_gen_new(): %s", krb5_get_err_text(pam_context, krbret)); retval = PAM_SERVICE_ERR; goto cleanup; } krbret = krb5_cc_initialize(pam_context, ccache, princ); if (krbret != 0) { PAM_VERBOSE_ERROR("Kerberos 5 error"); PAM_LOG("Error krb5_cc_initialize(): %s", krb5_get_err_text(pam_context, krbret)); retval = PAM_SERVICE_ERR; goto cleanup; } krbret = krb5_cc_store_cred(pam_context, ccache, &creds); if (krbret != 0) { PAM_VERBOSE_ERROR("Kerberos 5 error"); PAM_LOG("Error krb5_cc_store_cred(): %s", krb5_get_err_text(pam_context, krbret)); krb5_cc_destroy(pam_context, ccache); retval = PAM_SERVICE_ERR; goto cleanup; } PAM_LOG("Credentials stashed"); /* Verify them */ if ((srvdup = strdup(service)) == NULL) { retval = PAM_BUF_ERR; goto cleanup; } krbret = verify_krb_v5_tgt(pam_context, ccache, srvdup, - pam_test_option(&options, PAM_OPT_FORWARDABLE, NULL)); + openpam_get_option(pamh, PAM_OPT_FORWARDABLE) ? 1 : 0); free(srvdup); if (krbret == -1) { PAM_VERBOSE_ERROR("Kerberos 5 error"); krb5_cc_destroy(pam_context, ccache); retval = PAM_AUTH_ERR; goto cleanup; } PAM_LOG("Credentials stash verified"); retval = pam_get_data(pamh, "ccache", (const void **)&ccache_name); if (retval == PAM_SUCCESS) { krb5_cc_destroy(pam_context, ccache); PAM_VERBOSE_ERROR("Kerberos 5 error"); retval = PAM_AUTH_ERR; goto cleanup; } PAM_LOG("Credentials stash not pre-existing"); asprintf(&ccache_name, "%s:%s", krb5_cc_get_type(pam_context, ccache), krb5_cc_get_name(pam_context, ccache)); if (ccache_name == NULL) { PAM_VERBOSE_ERROR("Kerberos 5 error"); retval = PAM_BUF_ERR; goto cleanup; } retval = pam_set_data(pamh, "ccache", ccache_name, cleanup_cache); if (retval != 0) { krb5_cc_destroy(pam_context, ccache); PAM_VERBOSE_ERROR("Kerberos 5 error"); retval = PAM_SERVICE_ERR; goto cleanup; } PAM_LOG("Credentials stash saved"); cleanup: krb5_free_cred_contents(pam_context, &creds); PAM_LOG("Done cleanup"); cleanup2: krb5_free_principal(pam_context, princ); PAM_LOG("Done cleanup2"); cleanup3: if (princ_name) free(princ_name); krb5_free_context(pam_context); PAM_LOG("Done cleanup3"); if (retval != PAM_SUCCESS) PAM_VERBOSE_ERROR("Kerberos 5 refuses you"); return (retval); } PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int flags, - int argc, const char *argv[]) + int argc __unused, const char *argv[] __unused) { krb5_error_code krbret; krb5_context pam_context; krb5_principal princ; krb5_creds creds; krb5_ccache ccache_temp, ccache_perm; krb5_cc_cursor cursor; - struct options options; struct passwd *pwd = NULL; int retval; - char *user; - char *cache_name, *cache_env_name, *p, *q; + const char *cache_name, *q, *user; + char *cache_name_buf = NULL, *p; uid_t euid; gid_t egid; - pam_std_option(&options, other_options, argc, argv); - - PAM_LOG("Options processed"); - if (flags & PAM_DELETE_CRED) return (PAM_SUCCESS); if (flags & PAM_REFRESH_CRED) return (PAM_SUCCESS); if (flags & PAM_REINITIALIZE_CRED) return (PAM_SUCCESS); if (!(flags & PAM_ESTABLISH_CRED)) return (PAM_SERVICE_ERR); PAM_LOG("Establishing credentials"); /* Get username */ retval = pam_get_item(pamh, PAM_USER, (const void **)&user); if (retval != PAM_SUCCESS) return (retval); PAM_LOG("Got user: %s", user); krbret = krb5_init_context(&pam_context); if (krbret != 0) { PAM_LOG("Error krb5_init_context() failed"); return (PAM_SERVICE_ERR); } PAM_LOG("Context initialised"); euid = geteuid(); /* Usually 0 */ egid = getegid(); PAM_LOG("Got euid, egid: %d %d", euid, egid); /* Retrieve the temporary cache */ retval = pam_get_data(pamh, "ccache", (const void **)&cache_name); if (retval != PAM_SUCCESS) { retval = PAM_CRED_UNAVAIL; goto cleanup3; } krbret = krb5_cc_resolve(pam_context, cache_name, &ccache_temp); if (krbret != 0) { PAM_LOG("Error krb5_cc_resolve(\"%s\"): %s", cache_name, krb5_get_err_text(pam_context, krbret)); retval = PAM_SERVICE_ERR; goto cleanup3; } /* Get the uid. This should exist. */ pwd = getpwnam(user); if (pwd == NULL) { retval = PAM_USER_UNKNOWN; goto cleanup3; } PAM_LOG("Done getpwnam()"); /* Avoid following a symlink as root */ if (setegid(pwd->pw_gid)) { retval = PAM_SERVICE_ERR; goto cleanup3; } if (seteuid(pwd->pw_uid)) { retval = PAM_SERVICE_ERR; goto cleanup3; } PAM_LOG("Done setegid() & seteuid()"); /* Get the cache name */ - cache_name = NULL; - pam_test_option(&options, PAM_OPT_CCACHE, &cache_name); - if (cache_name == NULL) - asprintf(&cache_name, "FILE:/tmp/krb5cc_%d", pwd->pw_uid); + cache_name = openpam_get_option(pamh, PAM_OPT_CCACHE); + if (cache_name == NULL) { + asprintf(&cache_name_buf, "FILE:/tmp/krb5cc_%d", pwd->pw_uid); + cache_name = cache_name_buf; + } p = calloc(PATH_MAX + 16, sizeof(char)); q = cache_name; if (p == NULL) { PAM_LOG("Error malloc(): failure"); retval = PAM_BUF_ERR; goto cleanup3; } cache_name = p; /* convert %u and %p */ while (*q) { if (*q == '%') { q++; if (*q == 'u') { sprintf(p, "%d", pwd->pw_uid); p += strlen(p); } else if (*q == 'p') { sprintf(p, "%d", getpid()); p += strlen(p); } else { /* Not a special token */ *p++ = '%'; q--; } q++; } else { *p++ = *q++; } } PAM_LOG("Got cache_name: %s", cache_name); /* Initialize the new ccache */ krbret = krb5_cc_get_principal(pam_context, ccache_temp, &princ); if (krbret != 0) { PAM_LOG("Error krb5_cc_get_principal(): %s", krb5_get_err_text(pam_context, krbret)); retval = PAM_SERVICE_ERR; goto cleanup3; } krbret = krb5_cc_resolve(pam_context, cache_name, &ccache_perm); if (krbret != 0) { PAM_LOG("Error krb5_cc_resolve(): %s", krb5_get_err_text(pam_context, krbret)); retval = PAM_SERVICE_ERR; goto cleanup2; } krbret = krb5_cc_initialize(pam_context, ccache_perm, princ); if (krbret != 0) { PAM_LOG("Error krb5_cc_initialize(): %s", krb5_get_err_text(pam_context, krbret)); retval = PAM_SERVICE_ERR; goto cleanup2; } PAM_LOG("Cache initialised"); /* Prepare for iteration over creds */ krbret = krb5_cc_start_seq_get(pam_context, ccache_temp, &cursor); if (krbret != 0) { PAM_LOG("Error krb5_cc_start_seq_get(): %s", krb5_get_err_text(pam_context, krbret)); krb5_cc_destroy(pam_context, ccache_perm); retval = PAM_SERVICE_ERR; goto cleanup2; } PAM_LOG("Prepared for iteration"); /* Copy the creds (should be two of them) */ while ((krbret = krb5_cc_next_cred(pam_context, ccache_temp, &cursor, &creds) == 0)) { krbret = krb5_cc_store_cred(pam_context, ccache_perm, &creds); if (krbret != 0) { PAM_LOG("Error krb5_cc_store_cred(): %s", krb5_get_err_text(pam_context, krbret)); krb5_cc_destroy(pam_context, ccache_perm); krb5_free_cred_contents(pam_context, &creds); retval = PAM_SERVICE_ERR; goto cleanup2; } krb5_free_cred_contents(pam_context, &creds); PAM_LOG("Iteration"); } krb5_cc_end_seq_get(pam_context, ccache_temp, &cursor); PAM_LOG("Done iterating"); if (strstr(cache_name, "FILE:") == cache_name) { if (chown(&cache_name[5], pwd->pw_uid, pwd->pw_gid) == -1) { PAM_LOG("Error chown(): %s", strerror(errno)); krb5_cc_destroy(pam_context, ccache_perm); retval = PAM_SERVICE_ERR; goto cleanup2; } PAM_LOG("Done chown()"); if (chmod(&cache_name[5], (S_IRUSR | S_IWUSR)) == -1) { PAM_LOG("Error chmod(): %s", strerror(errno)); krb5_cc_destroy(pam_context, ccache_perm); retval = PAM_SERVICE_ERR; goto cleanup2; } PAM_LOG("Done chmod()"); } krb5_cc_close(pam_context, ccache_perm); PAM_LOG("Cache closed"); - cache_env_name = malloc(strlen(cache_name) + 12); - if (!cache_env_name) { - PAM_LOG("Error malloc(): failure"); - krb5_cc_destroy(pam_context, ccache_perm); - retval = PAM_BUF_ERR; - goto cleanup2; - } - - sprintf(cache_env_name, "KRB5CCNAME=%s", cache_name); - if ((retval = pam_putenv(pamh, cache_env_name)) != 0) { - PAM_LOG("Error pam_putenv(): %s", pam_strerror(pamh, retval)); + retval = pam_setenv(pamh, "KRB5CCNAME", cache_name, 1); + if (retval != PAM_SUCCESS) { + PAM_LOG("Error pam_setenv(): %s", pam_strerror(pamh, retval)); krb5_cc_destroy(pam_context, ccache_perm); retval = PAM_SERVICE_ERR; goto cleanup2; } PAM_LOG("Environment done: KRB5CCNAME=%s", cache_name); cleanup2: krb5_free_principal(pam_context, princ); PAM_LOG("Done cleanup2"); cleanup3: krb5_free_context(pam_context); PAM_LOG("Done cleanup3"); seteuid(euid); setegid(egid); PAM_LOG("Done seteuid() & setegid()"); + if (cache_name_buf != NULL) + free(cache_name_buf); + return (retval); } /* * account management */ PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags __unused, - int argc, const char *argv[]) + int argc __unused, const char *argv[] __unused) { krb5_error_code krbret; krb5_context pam_context; krb5_ccache ccache; krb5_principal princ; - struct options options; int retval; const char *user, *ccache_name; - pam_std_option(&options, other_options, argc, argv); - - PAM_LOG("Options processed"); - retval = pam_get_item(pamh, PAM_USER, (const void **)&user); if (retval != PAM_SUCCESS) return (retval); PAM_LOG("Got user: %s", user); retval = pam_get_data(pamh, "ccache", (const void **)&ccache_name); if (retval != PAM_SUCCESS) return (PAM_SUCCESS); PAM_LOG("Got credentials"); krbret = krb5_init_context(&pam_context); if (krbret != 0) { PAM_LOG("Error krb5_init_context() failed"); return (PAM_PERM_DENIED); } PAM_LOG("Context initialised"); krbret = krb5_cc_resolve(pam_context, ccache_name, &ccache); if (krbret != 0) { PAM_LOG("Error krb5_cc_resolve(\"%s\"): %s", ccache_name, krb5_get_err_text(pam_context, krbret)); krb5_free_context(pam_context); return (PAM_PERM_DENIED); } PAM_LOG("Got ccache %s", ccache_name); krbret = krb5_cc_get_principal(pam_context, ccache, &princ); if (krbret != 0) { PAM_LOG("Error krb5_cc_get_principal(): %s", krb5_get_err_text(pam_context, krbret)); retval = PAM_PERM_DENIED;; goto cleanup; } PAM_LOG("Got principal"); if (krb5_kuserok(pam_context, princ, user)) retval = PAM_SUCCESS; else retval = PAM_PERM_DENIED; krb5_free_principal(pam_context, princ); PAM_LOG("Done kuserok()"); cleanup: krb5_free_context(pam_context); PAM_LOG("Done cleanup"); return (retval); } /* * password management */ PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags, - int argc, const char *argv[]) + int argc __unused, const char *argv[] __unused) { krb5_error_code krbret; krb5_context pam_context; krb5_creds creds; krb5_principal princ; krb5_get_init_creds_opt opts; krb5_data result_code_string, result_string; - struct options options; int result_code, retval; const char *user, *pass; char *princ_name, *passdup; - pam_std_option(&options, other_options, argc, argv); - - PAM_LOG("Options processed"); - if (!(flags & PAM_UPDATE_AUTHTOK)) return (PAM_AUTHTOK_ERR); retval = pam_get_item(pamh, PAM_USER, (const void **)&user); if (retval != PAM_SUCCESS) return (retval); PAM_LOG("Got user: %s", user); krbret = krb5_init_context(&pam_context); if (krbret != 0) { PAM_LOG("Error krb5_init_context() failed"); return (PAM_SERVICE_ERR); } PAM_LOG("Context initialised"); krb5_get_init_creds_opt_init(&opts); PAM_LOG("Credentials options initialised"); /* Get principal name */ krbret = krb5_parse_name(pam_context, user, &princ); if (krbret != 0) { PAM_LOG("Error krb5_parse_name(): %s", krb5_get_err_text(pam_context, krbret)); retval = PAM_USER_UNKNOWN; goto cleanup3; } /* Now convert the principal name into something human readable */ princ_name = NULL; krbret = krb5_unparse_name(pam_context, princ, &princ_name); if (krbret != 0) { PAM_LOG("Error krb5_unparse_name(): %s", krb5_get_err_text(pam_context, krbret)); retval = PAM_SERVICE_ERR; goto cleanup2; } PAM_LOG("Got principal: %s", princ_name); /* Get password */ retval = pam_get_authtok(pamh, PAM_OLDAUTHTOK, &pass, PASSWORD_PROMPT); if (retval != PAM_SUCCESS) goto cleanup2; PAM_LOG("Got password"); memset(&creds, 0, sizeof(krb5_creds)); krbret = krb5_get_init_creds_password(pam_context, &creds, princ, pass, NULL, pamh, 0, "kadmin/changepw", &opts); if (krbret != 0) { PAM_LOG("Error krb5_get_init_creds_password()", krb5_get_err_text(pam_context, krbret)); retval = PAM_AUTH_ERR; goto cleanup2; } PAM_LOG("Credentials established"); /* Now get the new password */ for (;;) { retval = pam_get_authtok(pamh, PAM_AUTHTOK, &pass, NEW_PASSWORD_PROMPT); if (retval != PAM_TRY_AGAIN) break; pam_error(pamh, "Mismatch; try again, EOF to quit."); } if (retval != PAM_SUCCESS) goto cleanup; PAM_LOG("Got new password"); /* Change it */ if ((passdup = strdup(pass)) == NULL) { retval = PAM_BUF_ERR; goto cleanup; } krbret = krb5_change_password(pam_context, &creds, passdup, &result_code, &result_code_string, &result_string); free(passdup); if (krbret != 0) { PAM_LOG("Error krb5_change_password(): %s", krb5_get_err_text(pam_context, krbret)); retval = PAM_AUTHTOK_ERR; goto cleanup; } if (result_code) { PAM_LOG("Error krb5_change_password(): (result_code)"); retval = PAM_AUTHTOK_ERR; goto cleanup; } PAM_LOG("Password changed"); if (result_string.data) free(result_string.data); if (result_code_string.data) free(result_code_string.data); cleanup: krb5_free_cred_contents(pam_context, &creds); PAM_LOG("Done cleanup"); cleanup2: krb5_free_principal(pam_context, princ); PAM_LOG("Done cleanup2"); cleanup3: if (princ_name) free(princ_name); krb5_free_context(pam_context); PAM_LOG("Done cleanup3"); return (retval); } PAM_MODULE_ENTRY("pam_krb5"); /* * This routine with some modification is from the MIT V5B6 appl/bsd/login.c * Modified by Sam Hartman to support PAM services * for Debian. * * Verify the Kerberos ticket-granting ticket just retrieved for the * user. If the Kerberos server doesn't respond, assume the user is * trying to fake us out (since we DID just get a TGT from what is * supposedly our KDC). If the host/ service is unknown (i.e., * the local keytab doesn't have it), and we cannot find another * service we do have, let her in. * * Returns 1 for confirmation, -1 for failure, 0 for uncertainty. */ /* ARGSUSED */ static int verify_krb_v5_tgt(krb5_context context, krb5_ccache ccache, char *pam_service, int debug) { krb5_error_code retval; krb5_principal princ; krb5_keyblock *keyblock; krb5_data packet; krb5_auth_context auth_context; char phost[BUFSIZ]; const char *services[3], **service; packet.data = 0; /* If possible we want to try and verify the ticket we have * received against a keytab. We will try multiple service * principals, including at least the host principal and the PAM * service principal. The host principal is preferred because access * to that key is generally sufficient to compromise root, while the * service key for this PAM service may be less carefully guarded. * It is important to check the keytab first before the KDC so we do * not get spoofed by a fake KDC. */ services[0] = "host"; services[1] = pam_service; services[2] = NULL; keyblock = 0; retval = -1; for (service = &services[0]; *service != NULL; service++) { retval = krb5_sname_to_principal(context, NULL, *service, KRB5_NT_SRV_HST, &princ); if (retval != 0) { if (debug) syslog(LOG_DEBUG, "pam_krb5: verify_krb_v5_tgt(): %s: %s", "krb5_sname_to_principal()", krb5_get_err_text(context, retval)); return -1; } /* Extract the name directly. */ strncpy(phost, compat_princ_component(context, princ, 1), BUFSIZ); phost[BUFSIZ - 1] = '\0'; /* * Do we have service/ keys? * (use default/configured keytab, kvno IGNORE_VNO to get the * first match, and ignore enctype.) */ retval = krb5_kt_read_service_key(context, NULL, princ, 0, 0, &keyblock); if (retval != 0) continue; break; } if (retval != 0) { /* failed to find key */ /* Keytab or service key does not exist */ if (debug) syslog(LOG_DEBUG, "pam_krb5: verify_krb_v5_tgt(): %s: %s", "krb5_kt_read_service_key()", krb5_get_err_text(context, retval)); retval = 0; goto cleanup; } if (keyblock) krb5_free_keyblock(context, keyblock); /* Talk to the kdc and construct the ticket. */ auth_context = NULL; retval = krb5_mk_req(context, &auth_context, 0, *service, phost, NULL, ccache, &packet); if (auth_context) { krb5_auth_con_free(context, auth_context); auth_context = NULL; /* setup for rd_req */ } if (retval) { if (debug) syslog(LOG_DEBUG, "pam_krb5: verify_krb_v5_tgt(): %s: %s", "krb5_mk_req()", krb5_get_err_text(context, retval)); retval = -1; goto cleanup; } /* Try to use the ticket. */ retval = krb5_rd_req(context, &auth_context, &packet, princ, NULL, NULL, NULL); if (retval) { if (debug) syslog(LOG_DEBUG, "pam_krb5: verify_krb_v5_tgt(): %s: %s", "krb5_rd_req()", krb5_get_err_text(context, retval)); retval = -1; } else retval = 1; cleanup: if (packet.data) compat_free_data_contents(context, &packet); krb5_free_principal(context, princ); return retval; } /* Free the memory for cache_name. Called by pam_end() */ /* ARGSUSED */ static void cleanup_cache(pam_handle_t *pamh __unused, void *data, int pam_end_status __unused) { krb5_context pam_context; krb5_ccache ccache; krb5_error_code krbret; if (krb5_init_context(&pam_context)) return; krbret = krb5_cc_resolve(pam_context, data, &ccache); if (krbret == 0) krb5_cc_destroy(pam_context, ccache); krb5_free_context(pam_context); free(data); } #ifdef COMPAT_HEIMDAL #ifdef COMPAT_MIT #error This cannot be MIT and Heimdal compatible! #endif #endif #ifndef COMPAT_HEIMDAL #ifndef COMPAT_MIT #error One of COMPAT_MIT and COMPAT_HEIMDAL must be specified! #endif #endif #ifdef COMPAT_HEIMDAL /* ARGSUSED */ static const char * compat_princ_component(krb5_context context __unused, krb5_principal princ, int n) { return princ->name.name_string.val[n]; } /* ARGSUSED */ static void compat_free_data_contents(krb5_context context __unused, krb5_data * data) { krb5_xfree(data->data); } #endif #ifdef COMPAT_MIT static const char * compat_princ_component(krb5_context context, krb5_principal princ, int n) { return krb5_princ_component(context, princ, n)->data; } static void compat_free_data_contents(krb5_context context, krb5_data * data) { krb5_free_data_contents(context, data); } #endif diff --git a/lib/libpam/modules/pam_opie/pam_opie.c b/lib/libpam/modules/pam_opie/pam_opie.c index c66adf4b04aa..07f6f6e3b2ec 100644 --- a/lib/libpam/modules/pam_opie/pam_opie.c +++ b/lib/libpam/modules/pam_opie/pam_opie.c @@ -1,160 +1,146 @@ /*- * Copyright 2000 James Bloom * All rights reserved. * Based upon code Copyright 1998 Juniper Networks, Inc. * Copyright (c) 2001,2002 Networks Associates Technology, Inc. * All rights reserved. * * Portions of this software were developed for the FreeBSD Project by * ThinkSec AS and NAI Labs, the Security Research Division of Network * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 * ("CBOSS"), as part of the DARPA CHATS research program. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #define PAM_SM_AUTH #include #include #include -enum { - PAM_OPT_AUTH_AS_SELF = PAM_OPT_STD_MAX, - PAM_OPT_NO_FAKE_PROMPTS -}; - -static struct opttab other_options[] = { - { "auth_as_self", PAM_OPT_AUTH_AS_SELF }, - { "no_fake_prompts", PAM_OPT_NO_FAKE_PROMPTS }, - { NULL, 0 } -}; +#define PAM_OPT_NO_FAKE_PROMPTS "no_fake_prompts" PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags __unused, - int argc, const char *argv[]) + int argc __unused, const char *argv[] __unused) { struct opie opie; - struct options options; struct passwd *pwd; int retval, i; const char *(promptstr[]) = { "%s\nPassword: ", "%s\nPassword [echo on]: "}; char challenge[OPIE_CHALLENGE_MAX]; char *user; char *response; int style; - pam_std_option(&options, other_options, argc, argv); - - PAM_LOG("Options processed"); - user = NULL; - if (pam_test_option(&options, PAM_OPT_AUTH_AS_SELF, NULL)) { + if (openpam_get_option(pamh, PAM_OPT_AUTH_AS_SELF)) { if ((pwd = getpwnam(getlogin())) == NULL) return (PAM_AUTH_ERR); user = pwd->pw_name; } else { retval = pam_get_user(pamh, (const char **)&user, NULL); if (retval != PAM_SUCCESS) return (retval); } PAM_LOG("Got user: %s", user); /* * Don't call the OPIE atexit() handler when our program exits, * since the module has been unloaded and we will SEGV. */ opiedisableaeh(); /* * If the no_fake_prompts option was given, and the user * doesn't have an OPIE key, just fail rather than present the * user with a bogus OPIE challenge. */ /* XXX generates a const warning because of incorrect prototype */ if (opiechallenge(&opie, (char *)user, challenge) != 0 && - pam_test_option(&options, PAM_OPT_NO_FAKE_PROMPTS, NULL)) + openpam_get_option(pamh, PAM_OPT_NO_FAKE_PROMPTS)) return (PAM_AUTH_ERR); /* * It doesn't make sense to use a password that has already been * typed in, since we haven't presented the challenge to the user * yet, so clear the stored password. */ pam_set_item(pamh, PAM_AUTHTOK, NULL); style = PAM_PROMPT_ECHO_OFF; for (i = 0; i < 2; i++) { retval = pam_prompt(pamh, style, &response, promptstr[i], challenge); if (retval != PAM_SUCCESS) { opieunlock(); return (retval); } PAM_LOG("Completed challenge %d: %s", i, response); if (response[0] != '\0') break; /* Second time round, echo the password */ style = PAM_PROMPT_ECHO_ON; } pam_set_item(pamh, PAM_AUTHTOK, response); /* * Opieverify is supposed to return -1 only if an error occurs. * But it returns -1 even if the response string isn't in the form * it expects. Thus we can't log an error and can only check for * success or lack thereof. */ retval = opieverify(&opie, response); free(response); return (retval == 0 ? PAM_SUCCESS : PAM_AUTH_ERR); } PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh __unused, int flags __unused, int argc __unused, const char *argv[] __unused) { return (PAM_SUCCESS); } PAM_MODULE_ENTRY("pam_opie"); diff --git a/lib/libpam/modules/pam_radius/pam_radius.c b/lib/libpam/modules/pam_radius/pam_radius.c index bc09d26d8aaf..891ea1d59a52 100644 --- a/lib/libpam/modules/pam_radius/pam_radius.c +++ b/lib/libpam/modules/pam_radius/pam_radius.c @@ -1,346 +1,329 @@ /*- * Copyright 1998 Juniper Networks, Inc. * All rights reserved. * Copyright (c) 2001,2002 Networks Associates Technology, Inc. * All rights reserved. * * Portions of this software were developed for the FreeBSD Project by * ThinkSec AS and NAI Labs, the Security Research Division of Network * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 * ("CBOSS"), as part of the DARPA CHATS research program. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #define PAM_SM_AUTH #include #include #include -enum { - PAM_OPT_CONF = PAM_OPT_STD_MAX, - PAM_OPT_TEMPLATE_USER, - PAM_OPT_NAS_ID -}; - -static struct opttab other_options[] = { - { "conf", PAM_OPT_CONF }, - { "template_user", PAM_OPT_TEMPLATE_USER }, - { "nas_id", PAM_OPT_NAS_ID }, - { NULL, 0 } -}; +#define PAM_OPT_CONF "conf" +#define PAM_OPT_TEMPLATE_USER "template_user" +#define PAM_OPT_NAS_ID "nas_id" #define MAX_CHALLENGE_MSGS 10 #define PASSWORD_PROMPT "RADIUS Password:" static int build_access_request(struct rad_handle *, const char *, const char *, const char *, const void *, size_t); static int do_accept(pam_handle_t *, struct rad_handle *); static int do_challenge(pam_handle_t *, struct rad_handle *, const char *); /* * Construct an access request, but don't send it. Returns 0 on success, * -1 on failure. */ static int build_access_request(struct rad_handle *radh, const char *user, const char *pass, const char *nas_id, const void *state, size_t state_len) { char host[MAXHOSTNAMELEN]; if (rad_create_request(radh, RAD_ACCESS_REQUEST) == -1) { syslog(LOG_CRIT, "rad_create_request: %s", rad_strerror(radh)); return (-1); } if (nas_id == NULL && gethostname(host, sizeof host) != -1) nas_id = host; if ((user != NULL && rad_put_string(radh, RAD_USER_NAME, user) == -1) || (pass != NULL && rad_put_string(radh, RAD_USER_PASSWORD, pass) == -1) || (nas_id != NULL && rad_put_string(radh, RAD_NAS_IDENTIFIER, nas_id) == -1)) { syslog(LOG_CRIT, "rad_put_string: %s", rad_strerror(radh)); return (-1); } if (state != NULL && rad_put_attr(radh, RAD_STATE, state, state_len) == -1) { syslog(LOG_CRIT, "rad_put_attr: %s", rad_strerror(radh)); return (-1); } if (rad_put_int(radh, RAD_SERVICE_TYPE, RAD_AUTHENTICATE_ONLY) == -1) { syslog(LOG_CRIT, "rad_put_int: %s", rad_strerror(radh)); return (-1); } return (0); } static int do_accept(pam_handle_t *pamh, struct rad_handle *radh) { int attrtype; const void *attrval; size_t attrlen; char *s; while ((attrtype = rad_get_attr(radh, &attrval, &attrlen)) > 0) { if (attrtype == RAD_USER_NAME) { s = rad_cvt_string(attrval, attrlen); if (s == NULL) { syslog(LOG_CRIT, "rad_cvt_string: out of memory"); return (-1); } pam_set_item(pamh, PAM_USER, s); free(s); } } if (attrtype == -1) { syslog(LOG_CRIT, "rad_get_attr: %s", rad_strerror(radh)); return (-1); } return (0); } static int do_challenge(pam_handle_t *pamh, struct rad_handle *radh, const char *user) { int retval; int attrtype; const void *attrval; size_t attrlen; const void *state; size_t statelen; struct pam_message msgs[MAX_CHALLENGE_MSGS]; const struct pam_message *msg_ptrs[MAX_CHALLENGE_MSGS]; struct pam_response *resp; int num_msgs; const void *item; const struct pam_conv *conv; state = NULL; statelen = 0; num_msgs = 0; while ((attrtype = rad_get_attr(radh, &attrval, &attrlen)) > 0) { switch (attrtype) { case RAD_STATE: state = attrval; statelen = attrlen; break; case RAD_REPLY_MESSAGE: if (num_msgs >= MAX_CHALLENGE_MSGS) { syslog(LOG_CRIT, "Too many RADIUS challenge messages"); return (PAM_SERVICE_ERR); } msgs[num_msgs].msg = rad_cvt_string(attrval, attrlen); if (msgs[num_msgs].msg == NULL) { syslog(LOG_CRIT, "rad_cvt_string: out of memory"); return (PAM_SERVICE_ERR); } msgs[num_msgs].msg_style = PAM_TEXT_INFO; msg_ptrs[num_msgs] = &msgs[num_msgs]; num_msgs++; break; } } if (attrtype == -1) { syslog(LOG_CRIT, "rad_get_attr: %s", rad_strerror(radh)); return (PAM_SERVICE_ERR); } if (num_msgs == 0) { msgs[num_msgs].msg = strdup("(null RADIUS challenge): "); if (msgs[num_msgs].msg == NULL) { syslog(LOG_CRIT, "Out of memory"); return (PAM_SERVICE_ERR); } msgs[num_msgs].msg_style = PAM_TEXT_INFO; msg_ptrs[num_msgs] = &msgs[num_msgs]; num_msgs++; } msgs[num_msgs-1].msg_style = PAM_PROMPT_ECHO_ON; if ((retval = pam_get_item(pamh, PAM_CONV, &item)) != PAM_SUCCESS) { syslog(LOG_CRIT, "do_challenge: cannot get PAM_CONV"); return (retval); } conv = (const struct pam_conv *)item; if ((retval = conv->conv(num_msgs, msg_ptrs, &resp, conv->appdata_ptr)) != PAM_SUCCESS) return (retval); if (build_access_request(radh, user, resp[num_msgs-1].resp, NULL, state, statelen) == -1) return (PAM_SERVICE_ERR); memset(resp[num_msgs-1].resp, 0, strlen(resp[num_msgs-1].resp)); free(resp[num_msgs-1].resp); free(resp); while (num_msgs > 0) free(msgs[--num_msgs].msg); return (PAM_SUCCESS); } PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags __unused, - int argc, const char *argv[]) + int argc __unused, const char *argv[] __unused) { - struct options options; struct rad_handle *radh; const char *user, *tmpuser, *pass; - char *conf_file, *template_user, *nas_id; + const char *conf_file, *template_user, *nas_id; int retval; int e; - pam_std_option(&options, other_options, argc, argv); - - PAM_LOG("Options processed"); - - conf_file = NULL; - pam_test_option(&options, PAM_OPT_CONF, &conf_file); - template_user = NULL; - pam_test_option(&options, PAM_OPT_TEMPLATE_USER, &template_user); - nas_id = NULL; - pam_test_option(&options, PAM_OPT_NAS_ID, &nas_id); + conf_file = openpam_get_option(pamh, PAM_OPT_CONF); + template_user = openpam_get_option(pamh, PAM_OPT_TEMPLATE_USER); + nas_id = openpam_get_option(pamh, PAM_OPT_NAS_ID); retval = pam_get_user(pamh, &user, NULL); if (retval != PAM_SUCCESS) return (retval); PAM_LOG("Got user: %s", user); retval = pam_get_authtok(pamh, PAM_AUTHTOK, &pass, PASSWORD_PROMPT); if (retval != PAM_SUCCESS) return (retval); PAM_LOG("Got password"); radh = rad_open(); if (radh == NULL) { syslog(LOG_CRIT, "rad_open failed"); return (PAM_SERVICE_ERR); } PAM_LOG("Radius opened"); if (rad_config(radh, conf_file) == -1) { syslog(LOG_ALERT, "rad_config: %s", rad_strerror(radh)); rad_close(radh); return (PAM_SERVICE_ERR); } PAM_LOG("Radius config file read"); if (build_access_request(radh, user, pass, nas_id, NULL, 0) == -1) { rad_close(radh); return (PAM_SERVICE_ERR); } PAM_LOG("Radius build access done"); for (;;) { switch (rad_send_request(radh)) { case RAD_ACCESS_ACCEPT: e = do_accept(pamh, radh); rad_close(radh); if (e == -1) return (PAM_SERVICE_ERR); if (template_user != NULL) { PAM_LOG("Trying template user: %s", template_user); /* * If the given user name doesn't exist in * the local password database, change it * to the value given in the "template_user" * option. */ retval = pam_get_item(pamh, PAM_USER, (const void **)&tmpuser); if (retval != PAM_SUCCESS) return (retval); if (getpwnam(tmpuser) == NULL) { pam_set_item(pamh, PAM_USER, template_user); PAM_LOG("Using template user"); } } return (PAM_SUCCESS); case RAD_ACCESS_REJECT: rad_close(radh); PAM_VERBOSE_ERROR("Radius rejection"); return (PAM_AUTH_ERR); case RAD_ACCESS_CHALLENGE: retval = do_challenge(pamh, radh, user); if (retval != PAM_SUCCESS) { rad_close(radh); return (retval); } break; case -1: syslog(LOG_CRIT, "rad_send_request: %s", rad_strerror(radh)); rad_close(radh); PAM_VERBOSE_ERROR("Radius failure"); return (PAM_AUTHINFO_UNAVAIL); default: syslog(LOG_CRIT, "rad_send_request: unexpected return value"); rad_close(radh); PAM_VERBOSE_ERROR("Radius error"); return (PAM_SERVICE_ERR); } } } PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh __unused, int flags __unused, int argc __unused, const char *argv[] __unused) { return (PAM_SUCCESS); } PAM_MODULE_ENTRY("pam_radius"); diff --git a/lib/libpam/modules/pam_tacplus/pam_tacplus.c b/lib/libpam/modules/pam_tacplus/pam_tacplus.c index 19e3185f9862..3a9a249572f9 100644 --- a/lib/libpam/modules/pam_tacplus/pam_tacplus.c +++ b/lib/libpam/modules/pam_tacplus/pam_tacplus.c @@ -1,297 +1,281 @@ /*- * Copyright 1998 Juniper Networks, Inc. * All rights reserved. * Copyright (c) 2001,2002 Networks Associates Technology, Inc. * All rights reserved. * * Portions of this software were developed for the FreeBSD Project by * ThinkSec AS and NAI Labs, the Security Research Division of Network * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 * ("CBOSS"), as part of the DARPA CHATS research program. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #define PAM_SM_AUTH #include #include #include -enum { - PAM_OPT_CONF = PAM_OPT_STD_MAX, - PAM_OPT_TEMPLATE_USER -}; - -static struct opttab other_options[] = { - { "conf", PAM_OPT_CONF }, - { "template_user", PAM_OPT_TEMPLATE_USER }, - { NULL, 0 } -}; +#define PAM_OPT_CONF "conf" +#define PAM_OPT_TEMPLATE_USER "template_user" typedef int (*set_func)(struct tac_handle *, const char *); static int do_item(pam_handle_t *, struct tac_handle *, int, set_func, const char *); static char *get_msg(struct tac_handle *); static int set_msg(struct tac_handle *, const char *); static int do_item(pam_handle_t *pamh, struct tac_handle *tach, int item, set_func func, const char *funcname) { int retval; const void *value; retval = pam_get_item(pamh, item, &value); if (retval != PAM_SUCCESS) return retval; if (value != NULL && (*func)(tach, (const char *)value) == -1) { syslog(LOG_CRIT, "%s: %s", funcname, tac_strerror(tach)); tac_close(tach); return PAM_SERVICE_ERR; } return PAM_SUCCESS; } static char * get_msg(struct tac_handle *tach) { char *msg; msg = tac_get_msg(tach); if (msg == NULL) { syslog(LOG_CRIT, "tac_get_msg: %s", tac_strerror(tach)); tac_close(tach); return NULL; } return msg; } static int set_msg(struct tac_handle *tach, const char *msg) { if (tac_set_msg(tach, msg) == -1) { syslog(LOG_CRIT, "tac_set_msg: %s", tac_strerror(tach)); tac_close(tach); return -1; } return 0; } PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags __unused, - int argc, const char *argv[]) + int argc __unused, const char *argv[] __unused) { - struct options options; int retval; struct tac_handle *tach; - char *conf_file; - char *template_user; - - pam_std_option(&options, other_options, argc, argv); - - PAM_LOG("Options processed"); + const char *conf_file, *template_user; - conf_file = NULL; - pam_test_option(&options, PAM_OPT_CONF, &conf_file); - template_user = NULL; - pam_test_option(&options, PAM_OPT_TEMPLATE_USER, &template_user); + conf_file = openpam_get_option(pamh, PAM_OPT_CONF); + template_user = openpam_get_option(pamh, PAM_OPT_TEMPLATE_USER); tach = tac_open(); if (tach == NULL) { syslog(LOG_CRIT, "tac_open failed"); return (PAM_SERVICE_ERR); } if (tac_config(tach, conf_file) == -1) { syslog(LOG_ALERT, "tac_config: %s", tac_strerror(tach)); tac_close(tach); return (PAM_SERVICE_ERR); } if (tac_create_authen(tach, TAC_AUTHEN_LOGIN, TAC_AUTHEN_TYPE_ASCII, TAC_AUTHEN_SVC_LOGIN) == -1) { syslog(LOG_CRIT, "tac_create_authen: %s", tac_strerror(tach)); tac_close(tach); return (PAM_SERVICE_ERR); } PAM_LOG("Done tac_open() ... tac_close()"); retval = do_item(pamh, tach, PAM_USER, tac_set_user, "tac_set_user"); if (retval != PAM_SUCCESS) return (retval); PAM_LOG("Done user"); retval = do_item(pamh, tach, PAM_TTY, tac_set_port, "tac_set_port"); if (retval != PAM_SUCCESS) return (retval); PAM_LOG("Done tty"); retval = do_item(pamh, tach, PAM_RHOST, tac_set_rem_addr, "tac_set_rem_addr"); if (retval != PAM_SUCCESS) return (retval); for (;;) { char *srvr_msg; size_t msg_len; const char *user_msg; char *data_msg; int sflags; int status; sflags = tac_send_authen(tach); if (sflags == -1) { syslog(LOG_CRIT, "tac_send_authen: %s", tac_strerror(tach)); tac_close(tach); return (PAM_AUTHINFO_UNAVAIL); } status = TAC_AUTHEN_STATUS(sflags); - if (!TAC_AUTHEN_NOECHO(sflags)) - pam_set_option(&options, PAM_OPT_ECHO_PASS); + openpam_set_option(pamh, PAM_OPT_ECHO_PASS, + TAC_AUTHEN_NOECHO(sflags) ? NULL : ""); switch (status) { case TAC_AUTHEN_STATUS_PASS: tac_close(tach); if (template_user != NULL) { const void *item; const char *user; PAM_LOG("Trying template user: %s", template_user); /* * If the given user name doesn't exist in * the local password database, change it * to the value given in the "template_user" * option. */ retval = pam_get_item(pamh, PAM_USER, &item); if (retval != PAM_SUCCESS) return (retval); user = (const char *)item; if (getpwnam(user) == NULL) { pam_set_item(pamh, PAM_USER, template_user); PAM_LOG("Using template user"); } } return (PAM_SUCCESS); case TAC_AUTHEN_STATUS_FAIL: tac_close(tach); PAM_VERBOSE_ERROR("TACACS+ authentication failed"); return (PAM_AUTH_ERR); case TAC_AUTHEN_STATUS_GETUSER: case TAC_AUTHEN_STATUS_GETPASS: if ((srvr_msg = get_msg(tach)) == NULL) return (PAM_SERVICE_ERR); if (status == TAC_AUTHEN_STATUS_GETUSER) retval = pam_get_user(pamh, &user_msg, *srvr_msg ? srvr_msg : NULL); else if (status == TAC_AUTHEN_STATUS_GETPASS) retval = pam_get_authtok(pamh, PAM_AUTHTOK, &user_msg, *srvr_msg ? srvr_msg : "Password:"); free(srvr_msg); if (retval != PAM_SUCCESS) { /* XXX - send a TACACS+ abort packet */ tac_close(tach); return (retval); } if (set_msg(tach, user_msg) == -1) return (PAM_SERVICE_ERR); break; case TAC_AUTHEN_STATUS_GETDATA: if ((srvr_msg = get_msg(tach)) == NULL) return (PAM_SERVICE_ERR); retval = pam_prompt(pamh, - pam_test_option(&options, PAM_OPT_ECHO_PASS, NULL) - ? PAM_PROMPT_ECHO_ON : PAM_PROMPT_ECHO_OFF, + openpam_get_option(pamh, PAM_OPT_ECHO_PASS) ? + PAM_PROMPT_ECHO_ON : PAM_PROMPT_ECHO_OFF, &data_msg, "%s", *srvr_msg ? srvr_msg : "Data:"); free(srvr_msg); if (retval != PAM_SUCCESS) { /* XXX - send a TACACS+ abort packet */ tac_close(tach); return (retval); } retval = set_msg(tach, data_msg); memset(data_msg, 0, strlen(data_msg)); free(data_msg); if (retval == -1) return (PAM_SERVICE_ERR); break; case TAC_AUTHEN_STATUS_ERROR: srvr_msg = (char *)tac_get_data(tach, &msg_len); if (srvr_msg != NULL && msg_len != 0) { syslog(LOG_CRIT, "tac_send_authen:" " server detected error: %s", srvr_msg); free(srvr_msg); } else syslog(LOG_CRIT, "tac_send_authen: server detected error"); tac_close(tach); return (PAM_AUTHINFO_UNAVAIL); break; case TAC_AUTHEN_STATUS_RESTART: case TAC_AUTHEN_STATUS_FOLLOW: default: syslog(LOG_CRIT, "tac_send_authen: unexpected status %#x", status); tac_close(tach); return (PAM_AUTHINFO_UNAVAIL); } } } PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh __unused, int flags __unused, int argc __unused, const char *argv[] __unused) { return (PAM_IGNORE); } PAM_MODULE_ENTRY("pam_tacplus"); diff --git a/lib/libpam/modules/pam_unix/pam_unix.c b/lib/libpam/modules/pam_unix/pam_unix.c index 56cdd1a9f4b9..5c4542584e3d 100644 --- a/lib/libpam/modules/pam_unix/pam_unix.c +++ b/lib/libpam/modules/pam_unix/pam_unix.c @@ -1,469 +1,442 @@ /*- * Copyright 1998 Juniper Networks, Inc. * All rights reserved. * Copyright (c) 2002 Networks Associates Technology, Inc. * All rights reserved. * * Portions of this software was developed for the FreeBSD Project by * ThinkSec AS and NAI Labs, the Security Research Division of Network * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 * ("CBOSS"), as part of the DARPA CHATS research program. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef YP #include #endif #define PAM_SM_AUTH #define PAM_SM_ACCOUNT #define PAM_SM_PASSWORD #include #include #include #define PASSWORD_HASH "md5" #define DEFAULT_WARN (2L * 7L * 86400L) /* Two weeks */ #define SALTSIZE 32 static void makesalt(char []); static char password_hash[] = PASSWORD_HASH; -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 } -}; +#define PAM_OPT_LOCAL_PASS "local_pass" +#define PAM_OPT_NIS_PASS "nis_pass" char *tempname = NULL; /* * authentication management */ PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags __unused, - int argc, const char *argv[]) + int argc __unused, const char *argv[] __unused) { login_cap_t *lc; - struct options options; struct passwd *pwd; int retval; const char *pass, *user, *realpw, *prompt; - pam_std_option(&options, other_options, argc, argv); - - PAM_LOG("Options processed"); - - if (pam_test_option(&options, PAM_OPT_AUTH_AS_SELF, NULL)) { + if (openpam_get_option(pamh, PAM_OPT_AUTH_AS_SELF)) { pwd = getpwnam(getlogin()); } else { retval = pam_get_user(pamh, &user, NULL); if (retval != PAM_SUCCESS) return (retval); pwd = getpwnam(user); } PAM_LOG("Got user: %s", user); if (pwd != NULL) { PAM_LOG("Doing real authentication"); realpw = pwd->pw_passwd; if (realpw[0] == '\0') { if (!(flags & PAM_DISALLOW_NULL_AUTHTOK) && - pam_test_option(&options, PAM_OPT_NULLOK, NULL)) + openpam_get_option(pamh, PAM_OPT_NULLOK)) return (PAM_SUCCESS); realpw = "*"; } lc = login_getpwclass(pwd); } else { PAM_LOG("Doing dummy authentication"); realpw = "*"; lc = login_getclass(NULL); } prompt = login_getcapstr(lc, "passwd_prompt", NULL, NULL); retval = pam_get_authtok(pamh, PAM_AUTHTOK, &pass, prompt); login_close(lc); if (retval != PAM_SUCCESS) return (retval); PAM_LOG("Got password"); if (strcmp(crypt(pass, realpw), realpw) == 0) return (PAM_SUCCESS); PAM_VERBOSE_ERROR("UNIX authentication refused"); return (PAM_AUTH_ERR); } PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh __unused, int flags __unused, int argc __unused, const char *argv[] __unused) { return (PAM_SUCCESS); } /* * account management */ PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags __unused, - int argc, const char *argv[]) + int argc __unused, const char *argv[] __unused) { struct addrinfo hints, *res; - struct options options; struct passwd *pwd; struct timeval tp; login_cap_t *lc; time_t warntime; int retval; const char *rhost, *tty, *user; char rhostip[MAXHOSTNAMELEN] = ""; - pam_std_option(&options, other_options, argc, argv); - - PAM_LOG("Options processed"); - retval = pam_get_user(pamh, &user, NULL); if (retval != PAM_SUCCESS) return (retval); if (user == NULL || (pwd = getpwnam(user)) == NULL) return (PAM_SERVICE_ERR); PAM_LOG("Got user: %s", user); retval = pam_get_item(pamh, PAM_RHOST, (const void **)&rhost); if (retval != PAM_SUCCESS) return (retval); retval = pam_get_item(pamh, PAM_TTY, (const void **)&tty); if (retval != PAM_SUCCESS) return (retval); if (*pwd->pw_passwd == '\0' && (flags & PAM_DISALLOW_NULL_AUTHTOK) != 0) return (PAM_NEW_AUTHTOK_REQD); lc = login_getpwclass(pwd); if (lc == NULL) { PAM_LOG("Unable to get login class for user %s", user); return (PAM_SERVICE_ERR); } PAM_LOG("Got login_cap"); if (pwd->pw_change || pwd->pw_expire) gettimeofday(&tp, NULL); /* * Check pw_expire before pw_change - no point in letting the * user change the password on an expired account. */ if (pwd->pw_expire) { warntime = login_getcaptime(lc, "warnexpire", DEFAULT_WARN, DEFAULT_WARN); if (tp.tv_sec >= pwd->pw_expire) { login_close(lc); return (PAM_ACCT_EXPIRED); } else if (pwd->pw_expire - tp.tv_sec < warntime && (flags & PAM_SILENT) == 0) { pam_error(pamh, "Warning: your account expires on %s", ctime(&pwd->pw_expire)); } } retval = PAM_SUCCESS; if (pwd->pw_change) { warntime = login_getcaptime(lc, "warnpassword", DEFAULT_WARN, DEFAULT_WARN); if (tp.tv_sec >= pwd->pw_change) { retval = PAM_NEW_AUTHTOK_REQD; } else if (pwd->pw_change - tp.tv_sec < warntime && (flags & PAM_SILENT) == 0) { pam_error(pamh, "Warning: your password expires on %s", ctime(&pwd->pw_change)); } } /* * From here on, we must leave retval untouched (unless we * know we're going to fail), because we need to remember * whether we're supposed to return PAM_SUCCESS or * PAM_NEW_AUTHTOK_REQD. */ if (rhost && *rhost) { memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; if (getaddrinfo(rhost, NULL, &hints, &res) == 0) { getnameinfo(res->ai_addr, res->ai_addrlen, rhostip, sizeof(rhostip), NULL, 0, NI_NUMERICHOST|NI_WITHSCOPEID); } if (res != NULL) freeaddrinfo(res); } /* * Check host / tty / time-of-day restrictions */ if (!auth_hostok(lc, rhost, rhostip) || !auth_ttyok(lc, tty) || !auth_timeok(lc, time(NULL))) retval = PAM_AUTH_ERR; login_close(lc); return (retval); } /* * 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[]) + int argc __unused, const char *argv[] __unused) { #ifdef YP struct ypclnt *ypclnt; const char *yp_domain, *yp_server; #endif - struct options options; char salt[SALTSIZE + 1]; login_cap_t * lc; struct passwd *pwd, *old_pwd; const char *user, *old_pass, *new_pass; char *encrypted; int pfd, tfd, retval; - pam_std_option(&options, other_options, argc, argv); - - PAM_LOG("Options processed"); - - if (pam_test_option(&options, PAM_OPT_AUTH_AS_SELF, NULL)) + if (openpam_get_option(pamh, PAM_OPT_AUTH_AS_SELF)) pwd = getpwnam(getlogin()); else { retval = pam_get_user(pamh, &user, NULL); if (retval != PAM_SUCCESS) return (retval); pwd = getpwnam(user); } if (pwd == NULL) return (PAM_AUTHTOK_RECOVERY_ERR); PAM_LOG("Got user: %s", user); if (flags & PAM_PRELIM_CHECK) { PAM_LOG("PRELIM round"); if (getuid() == 0 && (pwd->pw_fields & _PWF_SOURCE) == _PWF_FILES) /* root doesn't need the old password */ return (pam_set_item(pamh, PAM_OLDAUTHTOK, "")); if (pwd->pw_passwd[0] == '\0' - && pam_test_option(&options, PAM_OPT_NULLOK, NULL)) { + && openpam_get_option(pamh, PAM_OPT_NULLOK)) { /* * No password case. XXX Are we giving too much away * by not prompting for a password? * XXX check PAM_DISALLOW_NULL_AUTHTOK */ old_pass = ""; } else { retval = pam_get_authtok(pamh, PAM_OLDAUTHTOK, &old_pass, NULL); if (retval != PAM_SUCCESS) return (retval); } PAM_LOG("Got old password"); /* always encrypt first */ encrypted = crypt(old_pass, pwd->pw_passwd); if (old_pass[0] == '\0' && - !pam_test_option(&options, PAM_OPT_NULLOK, NULL)) + !openpam_get_option(pamh, PAM_OPT_NULLOK)) return (PAM_PERM_DENIED); if (strcmp(encrypted, pwd->pw_passwd) != 0) return (PAM_PERM_DENIED); } else if (flags & PAM_UPDATE_AUTHTOK) { PAM_LOG("UPDATE round"); retval = pam_get_authtok(pamh, PAM_OLDAUTHTOK, &old_pass, NULL); if (retval != PAM_SUCCESS) return (retval); PAM_LOG("Got old password"); /* get new password */ for (;;) { retval = pam_get_authtok(pamh, PAM_AUTHTOK, &new_pass, NULL); if (retval != PAM_TRY_AGAIN) break; pam_error(pamh, "Mismatch; try again, EOF to quit."); } PAM_LOG("Got new password"); if (retval != PAM_SUCCESS) { PAM_VERBOSE_ERROR("Unable to get new password"); return (retval); } if (getuid() != 0 && new_pass[0] == '\0' && - !pam_test_option(&options, PAM_OPT_NULLOK, NULL)) + !openpam_get_option(pamh, PAM_OPT_NULLOK)) return (PAM_PERM_DENIED); if ((old_pwd = pw_dup(pwd)) == NULL) return (PAM_BUF_ERR); pwd->pw_change = 0; lc = login_getclass(NULL); if (login_setcryptfmt(lc, password_hash, NULL) == NULL) openpam_log(PAM_LOG_ERROR, "can't set password cipher, relying on default"); login_close(lc); makesalt(salt); pwd->pw_passwd = crypt(new_pass, salt); #ifdef YP switch (old_pwd->pw_fields & _PWF_SOURCE) { case _PWF_FILES: #endif retval = PAM_SERVICE_ERR; if (pw_init(NULL, NULL)) openpam_log(PAM_LOG_ERROR, "pw_init() failed"); else if ((pfd = pw_lock()) == -1) openpam_log(PAM_LOG_ERROR, "pw_lock() failed"); else if ((tfd = pw_tmp(-1)) == -1) openpam_log(PAM_LOG_ERROR, "pw_tmp() failed"); else if (pw_copy(pfd, tfd, pwd, old_pwd) == -1) openpam_log(PAM_LOG_ERROR, "pw_copy() failed"); else if (pw_mkdb(pwd->pw_name) == -1) openpam_log(PAM_LOG_ERROR, "pw_mkdb() failed"); else retval = PAM_SUCCESS; pw_fini(); #ifdef YP break; case _PWF_NIS: yp_domain = yp_server = NULL; (void)pam_get_data(pamh, "yp_domain", (const void **)&yp_domain); (void)pam_get_data(pamh, "yp_server", (const void **)&yp_server); ypclnt = ypclnt_new(yp_domain, "passwd.byname", yp_server); if (ypclnt == NULL) { retval = PAM_BUF_ERR; } else if (ypclnt_connect(ypclnt) == -1 || ypclnt_passwd(ypclnt, pwd, old_pass) == -1) { openpam_log(PAM_LOG_ERROR, "%s", ypclnt->error); retval = PAM_SERVICE_ERR; } else { retval = PAM_SUCCESS; } ypclnt_free(ypclnt); break; default: openpam_log(PAM_LOG_ERROR, "unsupported source 0x%x", pwd->pw_fields & _PWF_SOURCE); retval = PAM_SERVICE_ERR; } #endif free(old_pwd); } else { /* Very bad juju */ retval = PAM_ABORT; PAM_LOG("Illegal 'flags'"); } 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; } } /* Salt suitable for traditional DES and MD5 */ void makesalt(char salt[SALTSIZE]) { int i; /* These are not really random numbers, they are just * numbers that change to thwart construction of a * dictionary. This is exposed to the public. */ for (i = 0; i < SALTSIZE; i += 4) to64(&salt[i], arc4random(), 4); salt[SALTSIZE] = '\0'; } PAM_MODULE_ENTRY("pam_unix");