17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate * CDDL HEADER START
37c478bd9Sstevel@tonic-gate *
47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the
5a0709436Smp153739 * Common Development and Distribution License (the "License").
6a0709436Smp153739 * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate *
87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate * and limitations under the License.
127c478bd9Sstevel@tonic-gate *
137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate *
197c478bd9Sstevel@tonic-gate * CDDL HEADER END
207c478bd9Sstevel@tonic-gate */
215ad42b1bSSurya Prakki
227c478bd9Sstevel@tonic-gate /*
23488060a6SWill Fiveash * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
247c478bd9Sstevel@tonic-gate */
257c478bd9Sstevel@tonic-gate
267c478bd9Sstevel@tonic-gate #include <security/pam_appl.h>
277c478bd9Sstevel@tonic-gate #include <security/pam_modules.h>
287c478bd9Sstevel@tonic-gate #include <security/pam_impl.h>
297c478bd9Sstevel@tonic-gate #include <string.h>
307c478bd9Sstevel@tonic-gate #include <stdio.h>
317c478bd9Sstevel@tonic-gate #include <stdlib.h>
327c478bd9Sstevel@tonic-gate #include <sys/types.h>
337c478bd9Sstevel@tonic-gate #include <sys/stat.h>
347c478bd9Sstevel@tonic-gate #include <pwd.h>
357c478bd9Sstevel@tonic-gate #include <syslog.h>
367c478bd9Sstevel@tonic-gate #include <libintl.h>
377c478bd9Sstevel@tonic-gate #include <k5-int.h>
387c478bd9Sstevel@tonic-gate #include "profile/prof_int.h"
397c478bd9Sstevel@tonic-gate #include <netdb.h>
407c478bd9Sstevel@tonic-gate #include <ctype.h>
417c478bd9Sstevel@tonic-gate #include "utils.h"
427c478bd9Sstevel@tonic-gate #include "krb5_repository.h"
437c478bd9Sstevel@tonic-gate
447c478bd9Sstevel@tonic-gate #define KRB5_DEFAULT_OPTIONS 0
457c478bd9Sstevel@tonic-gate
467c478bd9Sstevel@tonic-gate int forwardable_flag = 0;
477c478bd9Sstevel@tonic-gate int renewable_flag = 0;
487c478bd9Sstevel@tonic-gate int proxiable_flag = 0;
497c478bd9Sstevel@tonic-gate int no_address_flag = 0;
507c478bd9Sstevel@tonic-gate profile_options_boolean config_option[] = {
517c478bd9Sstevel@tonic-gate { "forwardable", &forwardable_flag, 0 },
527c478bd9Sstevel@tonic-gate { "renewable", &renewable_flag, 0 },
537c478bd9Sstevel@tonic-gate { "proxiable", &proxiable_flag, 0 },
547c478bd9Sstevel@tonic-gate { "no_addresses", &no_address_flag, 0 },
557c478bd9Sstevel@tonic-gate { NULL, NULL, 0 }
567c478bd9Sstevel@tonic-gate };
577c478bd9Sstevel@tonic-gate char *renew_timeval;
587c478bd9Sstevel@tonic-gate char *life_timeval;
597c478bd9Sstevel@tonic-gate profile_option_strings config_times[] = {
607c478bd9Sstevel@tonic-gate { "max_life", &life_timeval, 0 },
617c478bd9Sstevel@tonic-gate { "max_renewable_life", &renew_timeval, 0 },
627c478bd9Sstevel@tonic-gate { NULL, NULL, 0 }
637c478bd9Sstevel@tonic-gate };
647c478bd9Sstevel@tonic-gate char *realmdef[] = { "realms", NULL, NULL, NULL };
657c478bd9Sstevel@tonic-gate char *appdef[] = { "appdefaults", "kinit", NULL };
667c478bd9Sstevel@tonic-gate
677c478bd9Sstevel@tonic-gate #define krb_realm (*(realmdef + 1))
687c478bd9Sstevel@tonic-gate
6970f41fc1SWill Fiveash int attempt_krb5_auth(pam_handle_t *, krb5_module_data_t *, char *,
7070f41fc1SWill Fiveash char **, boolean_t);
717c478bd9Sstevel@tonic-gate void krb5_cleanup(pam_handle_t *, void *, int);
727c478bd9Sstevel@tonic-gate
737c478bd9Sstevel@tonic-gate extern errcode_t profile_get_options_boolean();
747c478bd9Sstevel@tonic-gate extern errcode_t profile_get_options_string();
752278144aSsemery extern int krb5_verifypw(char *, char *, int);
767c478bd9Sstevel@tonic-gate extern krb5_error_code krb5_verify_init_creds(krb5_context,
777c478bd9Sstevel@tonic-gate krb5_creds *, krb5_principal, krb5_keytab, krb5_ccache *,
787c478bd9Sstevel@tonic-gate krb5_verify_init_creds_opt *);
793125ebfcSsemery extern krb5_error_code __krb5_get_init_creds_password(krb5_context,
803125ebfcSsemery krb5_creds *, krb5_principal, char *, krb5_prompter_fct, void *,
813125ebfcSsemery krb5_deltat, char *, krb5_get_init_creds_opt *,
823125ebfcSsemery krb5_kdc_rep **);
837c478bd9Sstevel@tonic-gate
847c478bd9Sstevel@tonic-gate /*
857c478bd9Sstevel@tonic-gate * pam_sm_authenticate - Authenticate user
867c478bd9Sstevel@tonic-gate */
877c478bd9Sstevel@tonic-gate int
pam_sm_authenticate(pam_handle_t * pamh,int flags,int argc,const char ** argv)887c478bd9Sstevel@tonic-gate pam_sm_authenticate(
897c478bd9Sstevel@tonic-gate pam_handle_t *pamh,
907c478bd9Sstevel@tonic-gate int flags,
917c478bd9Sstevel@tonic-gate int argc,
927c478bd9Sstevel@tonic-gate const char **argv)
937c478bd9Sstevel@tonic-gate {
940be37caaSsemery char *user = NULL;
957c478bd9Sstevel@tonic-gate int err;
967c478bd9Sstevel@tonic-gate int result = PAM_AUTH_ERR;
977c478bd9Sstevel@tonic-gate /* pam.conf options */
987c478bd9Sstevel@tonic-gate int debug = 0;
997c478bd9Sstevel@tonic-gate int warn = 1;
1007c478bd9Sstevel@tonic-gate /* return an error on password expire */
1017c478bd9Sstevel@tonic-gate int err_on_exp = 0;
1027c478bd9Sstevel@tonic-gate int i;
1037c478bd9Sstevel@tonic-gate char *password = NULL;
1047c478bd9Sstevel@tonic-gate uid_t pw_uid;
1057c478bd9Sstevel@tonic-gate krb5_module_data_t *kmd = NULL;
1067c478bd9Sstevel@tonic-gate krb5_repository_data_t *krb5_data = NULL;
1077c478bd9Sstevel@tonic-gate pam_repository_t *rep_data = NULL;
10870f41fc1SWill Fiveash boolean_t do_pkinit = FALSE;
1097c478bd9Sstevel@tonic-gate
1107c478bd9Sstevel@tonic-gate for (i = 0; i < argc; i++) {
1117c478bd9Sstevel@tonic-gate if (strcmp(argv[i], "debug") == 0) {
1127c478bd9Sstevel@tonic-gate debug = 1;
1137c478bd9Sstevel@tonic-gate } else if (strcmp(argv[i], "nowarn") == 0) {
1147c478bd9Sstevel@tonic-gate warn = 0;
1157c478bd9Sstevel@tonic-gate } else if (strcmp(argv[i], "err_on_exp") == 0) {
1167c478bd9Sstevel@tonic-gate err_on_exp = 1;
11770f41fc1SWill Fiveash } else if (strcmp(argv[i], "pkinit") == 0) {
11870f41fc1SWill Fiveash do_pkinit = TRUE;
1197c478bd9Sstevel@tonic-gate } else {
1203bfb48feSsemery __pam_log(LOG_AUTH | LOG_ERR,
12170f41fc1SWill Fiveash "PAM-KRB5 (auth) unrecognized option %s", argv[i]);
1227c478bd9Sstevel@tonic-gate }
1237c478bd9Sstevel@tonic-gate }
1247c478bd9Sstevel@tonic-gate if (flags & PAM_SILENT) warn = 0;
1257c478bd9Sstevel@tonic-gate
1267c478bd9Sstevel@tonic-gate if (debug)
1273bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG,
1287c478bd9Sstevel@tonic-gate "PAM-KRB5 (auth): pam_sm_authenticate flags=%d",
1297c478bd9Sstevel@tonic-gate flags);
1307c478bd9Sstevel@tonic-gate
1317c478bd9Sstevel@tonic-gate /*
1327c478bd9Sstevel@tonic-gate * pam_get_data could fail if we are being called for the first time
1337c478bd9Sstevel@tonic-gate * or if the module is not found, PAM_NO_MODULE_DATA is not an error
1347c478bd9Sstevel@tonic-gate */
1357c478bd9Sstevel@tonic-gate err = pam_get_data(pamh, KRB5_DATA, (const void**)&kmd);
1367c478bd9Sstevel@tonic-gate if (!(err == PAM_SUCCESS || err == PAM_NO_MODULE_DATA))
1373bfb48feSsemery return (PAM_SYSTEM_ERR);
1387c478bd9Sstevel@tonic-gate
13970f41fc1SWill Fiveash /*
14070f41fc1SWill Fiveash * If pam_krb5 was stacked higher in the auth stack and did PKINIT
14170f41fc1SWill Fiveash * preauth sucessfully then this instance is a fallback to password
14270f41fc1SWill Fiveash * based preauth and should just return PAM_IGNORE.
14370f41fc1SWill Fiveash *
14470f41fc1SWill Fiveash * The else clause is handled further down.
14570f41fc1SWill Fiveash */
14670f41fc1SWill Fiveash if (kmd != NULL) {
14770f41fc1SWill Fiveash if (++(kmd->auth_calls) > 2) {
14870f41fc1SWill Fiveash /*
14970f41fc1SWill Fiveash * pam_krb5 has been stacked > 2 times in the auth
15070f41fc1SWill Fiveash * stack. Clear out the current kmd and proceed as if
15170f41fc1SWill Fiveash * this is the first time pam_krb5 auth has been called.
15270f41fc1SWill Fiveash */
15370f41fc1SWill Fiveash if (debug) {
15470f41fc1SWill Fiveash __pam_log(LOG_AUTH | LOG_DEBUG,
15570f41fc1SWill Fiveash "PAM-KRB5 (auth): stacked more than"
15670f41fc1SWill Fiveash " two times, clearing kmd");
15770f41fc1SWill Fiveash }
15870f41fc1SWill Fiveash /* clear out/free current kmd */
15970f41fc1SWill Fiveash err = pam_set_data(pamh, KRB5_DATA, NULL, NULL);
16070f41fc1SWill Fiveash if (err != PAM_SUCCESS) {
16170f41fc1SWill Fiveash krb5_cleanup(pamh, kmd, err);
16270f41fc1SWill Fiveash result = err;
16370f41fc1SWill Fiveash goto out;
16470f41fc1SWill Fiveash }
16570f41fc1SWill Fiveash kmd = NULL;
16670f41fc1SWill Fiveash } else if (kmd->auth_calls == 2 &&
16770f41fc1SWill Fiveash kmd->auth_status == PAM_SUCCESS) {
16870f41fc1SWill Fiveash /*
16970f41fc1SWill Fiveash * The previous instance of pam_krb5 succeeded and this
17070f41fc1SWill Fiveash * instance was a fall back in case it didn't succeed so
17170f41fc1SWill Fiveash * return ignore.
17270f41fc1SWill Fiveash */
17370f41fc1SWill Fiveash if (debug) {
17470f41fc1SWill Fiveash __pam_log(LOG_AUTH | LOG_DEBUG,
17570f41fc1SWill Fiveash "PAM-KRB5 (auth): PKINIT succeeded "
17670f41fc1SWill Fiveash "earlier so returning PAM_IGNORE");
17770f41fc1SWill Fiveash }
17870f41fc1SWill Fiveash return (PAM_IGNORE);
17970f41fc1SWill Fiveash }
18070f41fc1SWill Fiveash }
18170f41fc1SWill Fiveash
18270f41fc1SWill Fiveash (void) pam_get_item(pamh, PAM_USER, (void**) &user);
18370f41fc1SWill Fiveash
18470f41fc1SWill Fiveash if (user == NULL || *user == '\0') {
18570f41fc1SWill Fiveash if (do_pkinit) {
18670f41fc1SWill Fiveash /*
18770f41fc1SWill Fiveash * If doing PKINIT it is okay to prompt for the user
18870f41fc1SWill Fiveash * name.
18970f41fc1SWill Fiveash */
19070f41fc1SWill Fiveash if ((err = pam_get_user(pamh, &user, NULL)) !=
19170f41fc1SWill Fiveash PAM_SUCCESS) {
19270f41fc1SWill Fiveash if (debug) {
19370f41fc1SWill Fiveash __pam_log(LOG_AUTH | LOG_DEBUG,
19470f41fc1SWill Fiveash "PAM-KRB5 (auth): get user failed: "
19570f41fc1SWill Fiveash "%s", pam_strerror(pamh, err));
19670f41fc1SWill Fiveash }
19770f41fc1SWill Fiveash return (err);
19870f41fc1SWill Fiveash }
19970f41fc1SWill Fiveash } else {
20070f41fc1SWill Fiveash if (debug)
20170f41fc1SWill Fiveash __pam_log(LOG_AUTH | LOG_DEBUG,
20270f41fc1SWill Fiveash "PAM-KRB5 (auth): user empty or null");
20370f41fc1SWill Fiveash return (PAM_USER_UNKNOWN);
20470f41fc1SWill Fiveash }
20570f41fc1SWill Fiveash }
20670f41fc1SWill Fiveash
20770f41fc1SWill Fiveash /* make sure a password entry exists for this user */
20870f41fc1SWill Fiveash if (!get_pw_uid(user, &pw_uid))
20970f41fc1SWill Fiveash return (PAM_USER_UNKNOWN);
21070f41fc1SWill Fiveash
2117c478bd9Sstevel@tonic-gate if (kmd == NULL) {
2127c478bd9Sstevel@tonic-gate kmd = calloc(1, sizeof (krb5_module_data_t));
2137c478bd9Sstevel@tonic-gate if (kmd == NULL) {
2147c478bd9Sstevel@tonic-gate result = PAM_BUF_ERR;
2157c478bd9Sstevel@tonic-gate goto out;
2167c478bd9Sstevel@tonic-gate }
2177c478bd9Sstevel@tonic-gate
2187c478bd9Sstevel@tonic-gate err = pam_set_data(pamh, KRB5_DATA, kmd, &krb5_cleanup);
2197c478bd9Sstevel@tonic-gate if (err != PAM_SUCCESS) {
2207c478bd9Sstevel@tonic-gate free(kmd);
2217c478bd9Sstevel@tonic-gate result = err;
2227c478bd9Sstevel@tonic-gate goto out;
2237c478bd9Sstevel@tonic-gate }
2247c478bd9Sstevel@tonic-gate }
2257c478bd9Sstevel@tonic-gate
2267c478bd9Sstevel@tonic-gate if (!kmd->env) {
2277c478bd9Sstevel@tonic-gate char buffer[512];
2287c478bd9Sstevel@tonic-gate
2297c478bd9Sstevel@tonic-gate if (snprintf(buffer, sizeof (buffer),
2307c478bd9Sstevel@tonic-gate "%s=FILE:/tmp/krb5cc_%d",
2317c478bd9Sstevel@tonic-gate KRB5_ENV_CCNAME, (int)pw_uid) >= sizeof (buffer)) {
2327c478bd9Sstevel@tonic-gate result = PAM_SYSTEM_ERR;
2337c478bd9Sstevel@tonic-gate goto out;
2347c478bd9Sstevel@tonic-gate }
2357c478bd9Sstevel@tonic-gate
2367c478bd9Sstevel@tonic-gate /* we MUST copy this to the heap for the putenv to work! */
2377c478bd9Sstevel@tonic-gate kmd->env = strdup(buffer);
2387c478bd9Sstevel@tonic-gate if (!kmd->env) {
2397c478bd9Sstevel@tonic-gate result = PAM_BUF_ERR;
2407c478bd9Sstevel@tonic-gate goto out;
2417c478bd9Sstevel@tonic-gate } else {
2427c478bd9Sstevel@tonic-gate if (putenv(kmd->env)) {
2437c478bd9Sstevel@tonic-gate result = PAM_SYSTEM_ERR;
2447c478bd9Sstevel@tonic-gate goto out;
2457c478bd9Sstevel@tonic-gate }
2467c478bd9Sstevel@tonic-gate }
2477c478bd9Sstevel@tonic-gate }
2487c478bd9Sstevel@tonic-gate
24967c90040Ssemery if (kmd->user != NULL)
25067c90040Ssemery free(kmd->user);
2513bfb48feSsemery if ((kmd->user = strdup(user)) == NULL) {
2523bfb48feSsemery result = PAM_BUF_ERR;
2533bfb48feSsemery goto out;
2543bfb48feSsemery }
2553bfb48feSsemery
2567c478bd9Sstevel@tonic-gate kmd->auth_status = PAM_AUTH_ERR;
2577c478bd9Sstevel@tonic-gate kmd->debug = debug;
2587c478bd9Sstevel@tonic-gate kmd->warn = warn;
2597c478bd9Sstevel@tonic-gate kmd->err_on_exp = err_on_exp;
2607c478bd9Sstevel@tonic-gate kmd->ccache = NULL;
2617c478bd9Sstevel@tonic-gate kmd->kcontext = NULL;
2627c478bd9Sstevel@tonic-gate kmd->password = NULL;
2637c478bd9Sstevel@tonic-gate kmd->age_status = PAM_SUCCESS;
2647c478bd9Sstevel@tonic-gate (void) memset((char *)&kmd->initcreds, 0, sizeof (krb5_creds));
26570f41fc1SWill Fiveash kmd->auth_calls = 1;
26670f41fc1SWill Fiveash kmd->preauth_type = do_pkinit ? KRB_PKINIT : KRB_PASSWD;
2677c478bd9Sstevel@tonic-gate
2687c478bd9Sstevel@tonic-gate /*
2697c478bd9Sstevel@tonic-gate * For apps that already did krb5 auth exchange...
2707c478bd9Sstevel@tonic-gate * Now that we've created the kmd structure, we can
2717c478bd9Sstevel@tonic-gate * return SUCCESS. 'kmd' may be needed later by other
2727c478bd9Sstevel@tonic-gate * PAM functions, thats why we wait until this point to
2737c478bd9Sstevel@tonic-gate * return.
2747c478bd9Sstevel@tonic-gate */
2750be37caaSsemery (void) pam_get_item(pamh, PAM_REPOSITORY, (void **)&rep_data);
2767c478bd9Sstevel@tonic-gate
2777c478bd9Sstevel@tonic-gate if (rep_data != NULL) {
2787c478bd9Sstevel@tonic-gate if (strcmp(rep_data->type, KRB5_REPOSITORY_NAME) != 0) {
2797c478bd9Sstevel@tonic-gate if (debug)
2803bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG,
2813bfb48feSsemery "PAM-KRB5 (auth): wrong"
2827c478bd9Sstevel@tonic-gate "repository found (%s), returning "
2837c478bd9Sstevel@tonic-gate "PAM_IGNORE", rep_data->type);
2847c478bd9Sstevel@tonic-gate return (PAM_IGNORE);
2857c478bd9Sstevel@tonic-gate }
2867c478bd9Sstevel@tonic-gate if (rep_data->scope_len == sizeof (krb5_repository_data_t)) {
2877c478bd9Sstevel@tonic-gate krb5_data = (krb5_repository_data_t *)rep_data->scope;
2887c478bd9Sstevel@tonic-gate
2897c478bd9Sstevel@tonic-gate if (krb5_data->flags ==
2907c478bd9Sstevel@tonic-gate SUNW_PAM_KRB5_ALREADY_AUTHENTICATED &&
2917c478bd9Sstevel@tonic-gate krb5_data->principal != NULL &&
2927c478bd9Sstevel@tonic-gate strlen(krb5_data->principal)) {
2937c478bd9Sstevel@tonic-gate if (debug)
2943bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG,
2957c478bd9Sstevel@tonic-gate "PAM-KRB5 (auth): Principal "
2967c478bd9Sstevel@tonic-gate "%s already authenticated",
2977c478bd9Sstevel@tonic-gate krb5_data->principal);
2987c478bd9Sstevel@tonic-gate kmd->auth_status = PAM_SUCCESS;
2997c478bd9Sstevel@tonic-gate return (PAM_SUCCESS);
3007c478bd9Sstevel@tonic-gate }
3017c478bd9Sstevel@tonic-gate }
3027c478bd9Sstevel@tonic-gate }
3037c478bd9Sstevel@tonic-gate
3047c478bd9Sstevel@tonic-gate /*
3057c478bd9Sstevel@tonic-gate * if root key exists in the keytab, it's a random key so no
3067c478bd9Sstevel@tonic-gate * need to prompt for pw and we just return IGNORE.
3077c478bd9Sstevel@tonic-gate *
3087c478bd9Sstevel@tonic-gate * note we don't need to force a prompt for pw as authtok_get
3097c478bd9Sstevel@tonic-gate * is required to be stacked above this module.
3107c478bd9Sstevel@tonic-gate */
3117c478bd9Sstevel@tonic-gate if ((strcmp(user, ROOT_UNAME) == 0) &&
3127c478bd9Sstevel@tonic-gate key_in_keytab(user, debug)) {
3137c478bd9Sstevel@tonic-gate if (debug)
3143bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG,
3157c478bd9Sstevel@tonic-gate "PAM-KRB5 (auth): "
3167c478bd9Sstevel@tonic-gate "key for '%s' in keytab, returning IGNORE", user);
3177c478bd9Sstevel@tonic-gate result = PAM_IGNORE;
3187c478bd9Sstevel@tonic-gate goto out;
3197c478bd9Sstevel@tonic-gate }
3207c478bd9Sstevel@tonic-gate
3210be37caaSsemery (void) pam_get_item(pamh, PAM_AUTHTOK, (void **)&password);
3227c478bd9Sstevel@tonic-gate
32370f41fc1SWill Fiveash result = attempt_krb5_auth(pamh, kmd, user, &password, 1);
3247c478bd9Sstevel@tonic-gate
3257c478bd9Sstevel@tonic-gate out:
3267c478bd9Sstevel@tonic-gate if (kmd) {
3277c478bd9Sstevel@tonic-gate if (debug)
3283bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG,
3297c478bd9Sstevel@tonic-gate "PAM-KRB5 (auth): pam_sm_auth finalize"
3307c478bd9Sstevel@tonic-gate " ccname env, result =%d, env ='%s',"
3317c478bd9Sstevel@tonic-gate " age = %d, status = %d",
3327c478bd9Sstevel@tonic-gate result, kmd->env ? kmd->env : "<null>",
3337c478bd9Sstevel@tonic-gate kmd->age_status, kmd->auth_status);
3347c478bd9Sstevel@tonic-gate
3357c478bd9Sstevel@tonic-gate if (kmd->env &&
3367c478bd9Sstevel@tonic-gate !(kmd->age_status == PAM_NEW_AUTHTOK_REQD &&
3377c478bd9Sstevel@tonic-gate kmd->auth_status == PAM_SUCCESS)) {
3387c478bd9Sstevel@tonic-gate
3397c478bd9Sstevel@tonic-gate
3407c478bd9Sstevel@tonic-gate if (result == PAM_SUCCESS) {
3417c478bd9Sstevel@tonic-gate /*
3427c478bd9Sstevel@tonic-gate * Put ccname into the pamh so that login
3437c478bd9Sstevel@tonic-gate * apps can pick this up when they run
3447c478bd9Sstevel@tonic-gate * pam_getenvlist().
3457c478bd9Sstevel@tonic-gate */
3467c478bd9Sstevel@tonic-gate if ((result = pam_putenv(pamh, kmd->env))
3477c478bd9Sstevel@tonic-gate != PAM_SUCCESS) {
3487c478bd9Sstevel@tonic-gate /* should not happen but... */
3493bfb48feSsemery __pam_log(LOG_AUTH | LOG_ERR,
3507c478bd9Sstevel@tonic-gate "PAM-KRB5 (auth):"
3513bfb48feSsemery " pam_putenv failed: result: %d",
3527c478bd9Sstevel@tonic-gate result);
3537c478bd9Sstevel@tonic-gate goto cleanupccname;
3547c478bd9Sstevel@tonic-gate }
3557c478bd9Sstevel@tonic-gate } else {
3567c478bd9Sstevel@tonic-gate cleanupccname:
3577c478bd9Sstevel@tonic-gate /* for lack of a Solaris unputenv() */
3587c478bd9Sstevel@tonic-gate krb5_unsetenv(KRB5_ENV_CCNAME);
3597c478bd9Sstevel@tonic-gate free(kmd->env);
3607c478bd9Sstevel@tonic-gate kmd->env = NULL;
3617c478bd9Sstevel@tonic-gate }
3627c478bd9Sstevel@tonic-gate }
3637c478bd9Sstevel@tonic-gate kmd->auth_status = result;
3647c478bd9Sstevel@tonic-gate }
3657c478bd9Sstevel@tonic-gate
3667c478bd9Sstevel@tonic-gate if (debug)
3673bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG,
3687c478bd9Sstevel@tonic-gate "PAM-KRB5 (auth): end: %s", pam_strerror(pamh, result));
3697c478bd9Sstevel@tonic-gate
3707c478bd9Sstevel@tonic-gate return (result);
3717c478bd9Sstevel@tonic-gate }
3727c478bd9Sstevel@tonic-gate
37370f41fc1SWill Fiveash static krb5_error_code
pam_krb5_prompter(krb5_context ctx,void * data,const char * name,const char * banner,int num_prompts,krb5_prompt prompts[])37470f41fc1SWill Fiveash pam_krb5_prompter(
37570f41fc1SWill Fiveash krb5_context ctx,
37670f41fc1SWill Fiveash void *data,
37770f41fc1SWill Fiveash /* ARGSUSED1 */
37870f41fc1SWill Fiveash const char *name,
37970f41fc1SWill Fiveash const char *banner,
38070f41fc1SWill Fiveash int num_prompts,
38170f41fc1SWill Fiveash krb5_prompt prompts[])
38270f41fc1SWill Fiveash {
383*dde44d91SWill Fiveash krb5_error_code rc = KRB5_LIBOS_CANTREADPWD;
38470f41fc1SWill Fiveash pam_handle_t *pamh = (pam_handle_t *)data;
38570f41fc1SWill Fiveash struct pam_conv *pam_convp;
386*dde44d91SWill Fiveash struct pam_message *msgs = NULL;
387*dde44d91SWill Fiveash struct pam_response *ret_respp = NULL;
38870f41fc1SWill Fiveash int i;
38970f41fc1SWill Fiveash krb5_prompt_type *prompt_type = krb5_get_prompt_types(ctx);
39070f41fc1SWill Fiveash char tmpbuf[PAM_MAX_MSG_SIZE];
39170f41fc1SWill Fiveash
392*dde44d91SWill Fiveash if (prompts) {
393*dde44d91SWill Fiveash assert(num_prompts > 0);
394*dde44d91SWill Fiveash }
39570f41fc1SWill Fiveash /*
39670f41fc1SWill Fiveash * Because this function should never be used for password prompts,
39770f41fc1SWill Fiveash * disallow password prompts.
39870f41fc1SWill Fiveash */
39970f41fc1SWill Fiveash for (i = 0; i < num_prompts; i++) {
400a1219d13SWill Fiveash switch (prompt_type[i]) {
401a1219d13SWill Fiveash case KRB5_PROMPT_TYPE_PASSWORD:
402a1219d13SWill Fiveash case KRB5_PROMPT_TYPE_NEW_PASSWORD:
403a1219d13SWill Fiveash case KRB5_PROMPT_TYPE_NEW_PASSWORD_AGAIN:
404*dde44d91SWill Fiveash return (rc);
40570f41fc1SWill Fiveash }
406a1219d13SWill Fiveash }
40770f41fc1SWill Fiveash
408*dde44d91SWill Fiveash if (pam_get_item(pamh, PAM_CONV, (void **)&pam_convp) != PAM_SUCCESS) {
40970f41fc1SWill Fiveash return (rc);
41070f41fc1SWill Fiveash }
41170f41fc1SWill Fiveash if (pam_convp == NULL) {
412*dde44d91SWill Fiveash return (rc);
41370f41fc1SWill Fiveash }
41470f41fc1SWill Fiveash
41570f41fc1SWill Fiveash msgs = (struct pam_message *)calloc(num_prompts,
41670f41fc1SWill Fiveash sizeof (struct pam_message));
41770f41fc1SWill Fiveash if (msgs == NULL) {
418*dde44d91SWill Fiveash return (rc);
41970f41fc1SWill Fiveash }
42070f41fc1SWill Fiveash (void) memset(msgs, 0, sizeof (struct pam_message) * num_prompts);
42170f41fc1SWill Fiveash
42270f41fc1SWill Fiveash for (i = 0; i < num_prompts; i++) {
42370f41fc1SWill Fiveash /* convert krb prompt style to PAM style */
42470f41fc1SWill Fiveash if (prompts[i].hidden) {
42570f41fc1SWill Fiveash msgs[i].msg_style = PAM_PROMPT_ECHO_OFF;
42670f41fc1SWill Fiveash } else {
42770f41fc1SWill Fiveash msgs[i].msg_style = PAM_PROMPT_ECHO_ON;
42870f41fc1SWill Fiveash }
42970f41fc1SWill Fiveash /*
43070f41fc1SWill Fiveash * krb expects the prompting function to append ": " to the
43170f41fc1SWill Fiveash * prompt string.
43270f41fc1SWill Fiveash */
43370f41fc1SWill Fiveash if (snprintf(tmpbuf, sizeof (tmpbuf), "%s: ",
43470f41fc1SWill Fiveash prompts[i].prompt) < 0) {
43570f41fc1SWill Fiveash goto cleanup;
43670f41fc1SWill Fiveash }
43770f41fc1SWill Fiveash msgs[i].msg = strdup(tmpbuf);
43870f41fc1SWill Fiveash if (msgs[i].msg == NULL) {
43970f41fc1SWill Fiveash goto cleanup;
44070f41fc1SWill Fiveash }
44170f41fc1SWill Fiveash }
44270f41fc1SWill Fiveash
44370f41fc1SWill Fiveash /*
44470f41fc1SWill Fiveash * Call PAM conv function to display the prompt.
44570f41fc1SWill Fiveash */
44670f41fc1SWill Fiveash
447*dde44d91SWill Fiveash if ((pam_convp->conv)(num_prompts, &msgs, &ret_respp,
448*dde44d91SWill Fiveash pam_convp->appdata_ptr) == PAM_SUCCESS) {
44970f41fc1SWill Fiveash for (i = 0; i < num_prompts; i++) {
45070f41fc1SWill Fiveash /* convert PAM response to krb prompt reply format */
451*dde44d91SWill Fiveash assert(prompts[i].reply->data != NULL);
452*dde44d91SWill Fiveash assert(ret_respp[i].resp != NULL);
453*dde44d91SWill Fiveash
454*dde44d91SWill Fiveash if (strlcpy(prompts[i].reply->data,
455*dde44d91SWill Fiveash ret_respp[i].resp, prompts[i].reply->length) >=
456*dde44d91SWill Fiveash prompts[i].reply->length) {
457*dde44d91SWill Fiveash char errmsg[1][PAM_MAX_MSG_SIZE];
458*dde44d91SWill Fiveash
459*dde44d91SWill Fiveash (void) snprintf(errmsg[0], PAM_MAX_MSG_SIZE,
460*dde44d91SWill Fiveash "%s", dgettext(TEXT_DOMAIN,
461*dde44d91SWill Fiveash "Reply too long: "));
462*dde44d91SWill Fiveash (void) __pam_display_msg(pamh, PAM_ERROR_MSG,
463*dde44d91SWill Fiveash 1, errmsg, NULL);
464*dde44d91SWill Fiveash goto cleanup;
465*dde44d91SWill Fiveash } else {
466*dde44d91SWill Fiveash char *retp;
467*dde44d91SWill Fiveash
46870f41fc1SWill Fiveash /*
469*dde44d91SWill Fiveash * newline must be replaced with \0 terminator
47070f41fc1SWill Fiveash */
471*dde44d91SWill Fiveash retp = strchr(prompts[i].reply->data, '\n');
472*dde44d91SWill Fiveash if (retp != NULL)
473*dde44d91SWill Fiveash *retp = '\0';
474*dde44d91SWill Fiveash /* NULL terminator should not be counted */
475*dde44d91SWill Fiveash prompts[i].reply->length =
476*dde44d91SWill Fiveash strlen(prompts[i].reply->data);
477*dde44d91SWill Fiveash }
478*dde44d91SWill Fiveash }
479*dde44d91SWill Fiveash rc = 0;
48070f41fc1SWill Fiveash }
48170f41fc1SWill Fiveash
48270f41fc1SWill Fiveash cleanup:
48370f41fc1SWill Fiveash for (i = 0; i < num_prompts; i++) {
48470f41fc1SWill Fiveash if (msgs[i].msg) {
48570f41fc1SWill Fiveash free(msgs[i].msg);
48670f41fc1SWill Fiveash }
487*dde44d91SWill Fiveash if (ret_respp[i].resp) {
488*dde44d91SWill Fiveash /* 0 out sensitive data before free() */
489*dde44d91SWill Fiveash (void) memset(ret_respp[i].resp, 0,
490*dde44d91SWill Fiveash strlen(ret_respp[i].resp));
491*dde44d91SWill Fiveash free(ret_respp[i].resp);
492*dde44d91SWill Fiveash }
49370f41fc1SWill Fiveash }
49470f41fc1SWill Fiveash free(msgs);
495*dde44d91SWill Fiveash free(ret_respp);
49670f41fc1SWill Fiveash return (rc);
49770f41fc1SWill Fiveash }
49870f41fc1SWill Fiveash
4997c478bd9Sstevel@tonic-gate int
attempt_krb5_auth(pam_handle_t * pamh,krb5_module_data_t * kmd,char * user,char ** krb5_pass,boolean_t verify_tik)5007c478bd9Sstevel@tonic-gate attempt_krb5_auth(
50170f41fc1SWill Fiveash pam_handle_t *pamh,
5027c478bd9Sstevel@tonic-gate krb5_module_data_t *kmd,
5037c478bd9Sstevel@tonic-gate char *user,
5047c478bd9Sstevel@tonic-gate char **krb5_pass,
5050be37caaSsemery boolean_t verify_tik)
5067c478bd9Sstevel@tonic-gate {
50767c90040Ssemery krb5_principal me = NULL, clientp = NULL;
50867c90040Ssemery krb5_principal server = NULL, serverp = NULL;
5097c478bd9Sstevel@tonic-gate krb5_creds *my_creds;
5107c478bd9Sstevel@tonic-gate krb5_timestamp now;
5117c478bd9Sstevel@tonic-gate krb5_error_code code = 0;
5127c478bd9Sstevel@tonic-gate char kuser[2*MAXHOSTNAMELEN];
5137c478bd9Sstevel@tonic-gate krb5_deltat lifetime;
5147c478bd9Sstevel@tonic-gate krb5_deltat rlife;
5157c478bd9Sstevel@tonic-gate krb5_deltat krb5_max_duration;
5167c478bd9Sstevel@tonic-gate int options = KRB5_DEFAULT_OPTIONS;
5177c478bd9Sstevel@tonic-gate krb5_data tgtname = {
5187c478bd9Sstevel@tonic-gate 0,
5197c478bd9Sstevel@tonic-gate KRB5_TGS_NAME_SIZE,
5207c478bd9Sstevel@tonic-gate KRB5_TGS_NAME
5217c478bd9Sstevel@tonic-gate };
522488060a6SWill Fiveash krb5_get_init_creds_opt *opts = NULL;
5233125ebfcSsemery krb5_kdc_rep *as_reply = NULL;
5247c478bd9Sstevel@tonic-gate /*
5257c478bd9Sstevel@tonic-gate * "result" should not be assigned PAM_SUCCESS unless
5267c478bd9Sstevel@tonic-gate * authentication has succeeded and there are no other errors.
5277c478bd9Sstevel@tonic-gate *
5287c478bd9Sstevel@tonic-gate * "code" is sometimes used for PAM codes, sometimes for krb5
5297c478bd9Sstevel@tonic-gate * codes. Be careful.
5307c478bd9Sstevel@tonic-gate */
5317c478bd9Sstevel@tonic-gate int result = PAM_AUTH_ERR;
5327c478bd9Sstevel@tonic-gate
5337c478bd9Sstevel@tonic-gate if (kmd->debug)
5343bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG,
5357c478bd9Sstevel@tonic-gate "PAM-KRB5 (auth): attempt_krb5_auth: start: user='%s'",
5367c478bd9Sstevel@tonic-gate user ? user : "<null>");
5377c478bd9Sstevel@tonic-gate
5387c478bd9Sstevel@tonic-gate /* need to free context with krb5_free_context */
5398ce3ffdfSPeter Shoults if (code = krb5_init_secure_context(&kmd->kcontext)) {
5403bfb48feSsemery __pam_log(LOG_AUTH | LOG_ERR,
5417c478bd9Sstevel@tonic-gate "PAM-KRB5 (auth): Error initializing "
5423bfb48feSsemery "krb5: %s",
5437c478bd9Sstevel@tonic-gate error_message(code));
5447c478bd9Sstevel@tonic-gate return (PAM_SYSTEM_ERR);
5457c478bd9Sstevel@tonic-gate }
5467c478bd9Sstevel@tonic-gate
5477c478bd9Sstevel@tonic-gate if ((code = get_kmd_kuser(kmd->kcontext, (const char *)user, kuser,
5487c478bd9Sstevel@tonic-gate 2*MAXHOSTNAMELEN)) != 0) {
5497c478bd9Sstevel@tonic-gate /* get_kmd_kuser returns proper PAM error statuses */
5507c478bd9Sstevel@tonic-gate return (code);
5517c478bd9Sstevel@tonic-gate }
5527c478bd9Sstevel@tonic-gate
5537c478bd9Sstevel@tonic-gate if ((code = krb5_parse_name(kmd->kcontext, kuser, &me)) != 0) {
5547c478bd9Sstevel@tonic-gate krb5_free_context(kmd->kcontext);
5557c478bd9Sstevel@tonic-gate kmd->kcontext = NULL;
5563bfb48feSsemery return (PAM_SYSTEM_ERR);
5577c478bd9Sstevel@tonic-gate }
5587c478bd9Sstevel@tonic-gate
5597c478bd9Sstevel@tonic-gate /* call krb5_free_cred_contents() on error */
5607c478bd9Sstevel@tonic-gate my_creds = &kmd->initcreds;
5617c478bd9Sstevel@tonic-gate
5623bfb48feSsemery if ((code =
5633bfb48feSsemery krb5_copy_principal(kmd->kcontext, me, &my_creds->client))) {
5643bfb48feSsemery result = PAM_SYSTEM_ERR;
5657c478bd9Sstevel@tonic-gate goto out_err;
5663bfb48feSsemery }
56767c90040Ssemery clientp = my_creds->client;
5687c478bd9Sstevel@tonic-gate
5697c478bd9Sstevel@tonic-gate if (code = krb5_build_principal_ext(kmd->kcontext, &server,
5707c478bd9Sstevel@tonic-gate krb5_princ_realm(kmd->kcontext, me)->length,
5717c478bd9Sstevel@tonic-gate krb5_princ_realm(kmd->kcontext, me)->data,
5727c478bd9Sstevel@tonic-gate tgtname.length, tgtname.data,
5737c478bd9Sstevel@tonic-gate krb5_princ_realm(kmd->kcontext, me)->length,
5747c478bd9Sstevel@tonic-gate krb5_princ_realm(kmd->kcontext, me)->data, 0)) {
5753bfb48feSsemery __pam_log(LOG_AUTH | LOG_ERR,
5767c478bd9Sstevel@tonic-gate "PAM-KRB5 (auth): attempt_krb5_auth: "
5773bfb48feSsemery "krb5_build_princ_ext failed: %s",
5787c478bd9Sstevel@tonic-gate error_message(code));
5793bfb48feSsemery result = PAM_SYSTEM_ERR;
5807c478bd9Sstevel@tonic-gate goto out;
5817c478bd9Sstevel@tonic-gate }
5827c478bd9Sstevel@tonic-gate
5837c478bd9Sstevel@tonic-gate if (code = krb5_copy_principal(kmd->kcontext, server,
5847c478bd9Sstevel@tonic-gate &my_creds->server)) {
5853bfb48feSsemery result = PAM_SYSTEM_ERR;
5867c478bd9Sstevel@tonic-gate goto out_err;
5877c478bd9Sstevel@tonic-gate }
58867c90040Ssemery serverp = my_creds->server;
5897c478bd9Sstevel@tonic-gate
5907c478bd9Sstevel@tonic-gate if (code = krb5_timeofday(kmd->kcontext, &now)) {
5913bfb48feSsemery __pam_log(LOG_AUTH | LOG_ERR,
5927c478bd9Sstevel@tonic-gate "PAM-KRB5 (auth): attempt_krb5_auth: "
5933bfb48feSsemery "krb5_timeofday failed: %s",
5947c478bd9Sstevel@tonic-gate error_message(code));
5953bfb48feSsemery result = PAM_SYSTEM_ERR;
5967c478bd9Sstevel@tonic-gate goto out;
5977c478bd9Sstevel@tonic-gate }
5987c478bd9Sstevel@tonic-gate
5997c478bd9Sstevel@tonic-gate /*
6007c478bd9Sstevel@tonic-gate * set the values for lifetime and rlife to be the maximum
6017c478bd9Sstevel@tonic-gate * possible
6027c478bd9Sstevel@tonic-gate */
6037c478bd9Sstevel@tonic-gate krb5_max_duration = KRB5_KDB_EXPIRATION - now - 60*60;
6047c478bd9Sstevel@tonic-gate lifetime = krb5_max_duration;
6057c478bd9Sstevel@tonic-gate rlife = krb5_max_duration;
6067c478bd9Sstevel@tonic-gate
6077c478bd9Sstevel@tonic-gate /*
6087c478bd9Sstevel@tonic-gate * Let us get the values for various options
6097c478bd9Sstevel@tonic-gate * from Kerberos configuration file
6107c478bd9Sstevel@tonic-gate */
6117c478bd9Sstevel@tonic-gate
6127c478bd9Sstevel@tonic-gate krb_realm = krb5_princ_realm(kmd->kcontext, me)->data;
6137c478bd9Sstevel@tonic-gate profile_get_options_boolean(kmd->kcontext->profile,
6147c478bd9Sstevel@tonic-gate realmdef, config_option);
6157c478bd9Sstevel@tonic-gate profile_get_options_boolean(kmd->kcontext->profile,
6167c478bd9Sstevel@tonic-gate appdef, config_option);
6177c478bd9Sstevel@tonic-gate profile_get_options_string(kmd->kcontext->profile,
6187c478bd9Sstevel@tonic-gate realmdef, config_times);
6197c478bd9Sstevel@tonic-gate profile_get_options_string(kmd->kcontext->profile,
6207c478bd9Sstevel@tonic-gate appdef, config_times);
6217c478bd9Sstevel@tonic-gate
6227c478bd9Sstevel@tonic-gate if (renew_timeval) {
6237c478bd9Sstevel@tonic-gate code = krb5_string_to_deltat(renew_timeval, &rlife);
6247c478bd9Sstevel@tonic-gate if (code != 0 || rlife == 0 || rlife > krb5_max_duration) {
6253bfb48feSsemery __pam_log(LOG_AUTH | LOG_ERR,
6267c478bd9Sstevel@tonic-gate "PAM-KRB5 (auth): Bad max_renewable_life "
6273bfb48feSsemery " value '%s' in Kerberos config file",
6287c478bd9Sstevel@tonic-gate renew_timeval);
6297c478bd9Sstevel@tonic-gate result = PAM_SYSTEM_ERR;
6307c478bd9Sstevel@tonic-gate goto out;
6317c478bd9Sstevel@tonic-gate }
6327c478bd9Sstevel@tonic-gate }
6337c478bd9Sstevel@tonic-gate if (life_timeval) {
6347c478bd9Sstevel@tonic-gate code = krb5_string_to_deltat(life_timeval, &lifetime);
6357c478bd9Sstevel@tonic-gate if (code != 0 || lifetime == 0 ||
6367c478bd9Sstevel@tonic-gate lifetime > krb5_max_duration) {
6373bfb48feSsemery __pam_log(LOG_AUTH | LOG_ERR,
6383bfb48feSsemery "lifetime value '%s' in Kerberos config file",
6397c478bd9Sstevel@tonic-gate life_timeval);
6407c478bd9Sstevel@tonic-gate result = PAM_SYSTEM_ERR;
6417c478bd9Sstevel@tonic-gate goto out;
6427c478bd9Sstevel@tonic-gate }
6437c478bd9Sstevel@tonic-gate }
6447c478bd9Sstevel@tonic-gate /* start timer when request gets to KDC */
6457c478bd9Sstevel@tonic-gate my_creds->times.starttime = 0;
6467c478bd9Sstevel@tonic-gate my_creds->times.endtime = now + lifetime;
6477c478bd9Sstevel@tonic-gate
6487c478bd9Sstevel@tonic-gate if (options & KDC_OPT_RENEWABLE) {
6497c478bd9Sstevel@tonic-gate my_creds->times.renew_till = now + rlife;
6507c478bd9Sstevel@tonic-gate } else
6517c478bd9Sstevel@tonic-gate my_creds->times.renew_till = 0;
6527c478bd9Sstevel@tonic-gate
653488060a6SWill Fiveash code = krb5_get_init_creds_opt_alloc(kmd->kcontext, &opts);
654488060a6SWill Fiveash if (code != 0) {
655488060a6SWill Fiveash __pam_log(LOG_AUTH | LOG_ERR,
656488060a6SWill Fiveash "Error allocating gic opts: %s",
657488060a6SWill Fiveash error_message(code));
658488060a6SWill Fiveash result = PAM_SYSTEM_ERR;
659488060a6SWill Fiveash goto out;
660488060a6SWill Fiveash }
661488060a6SWill Fiveash
662488060a6SWill Fiveash krb5_get_init_creds_opt_set_tkt_life(opts, lifetime);
6637c478bd9Sstevel@tonic-gate
6647c478bd9Sstevel@tonic-gate if (proxiable_flag) { /* Set in config file */
6657c478bd9Sstevel@tonic-gate if (kmd->debug)
6663bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG,
6677c478bd9Sstevel@tonic-gate "PAM-KRB5 (auth): Proxiable tickets "
6683bfb48feSsemery "requested");
669488060a6SWill Fiveash krb5_get_init_creds_opt_set_proxiable(opts, TRUE);
6707c478bd9Sstevel@tonic-gate }
6717c478bd9Sstevel@tonic-gate if (forwardable_flag) {
6727c478bd9Sstevel@tonic-gate if (kmd->debug)
6733bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG,
6747c478bd9Sstevel@tonic-gate "PAM-KRB5 (auth): Forwardable tickets "
6753bfb48feSsemery "requested");
676488060a6SWill Fiveash krb5_get_init_creds_opt_set_forwardable(opts, TRUE);
6777c478bd9Sstevel@tonic-gate }
6787c478bd9Sstevel@tonic-gate if (renewable_flag) {
6797c478bd9Sstevel@tonic-gate if (kmd->debug)
6803bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG,
6817c478bd9Sstevel@tonic-gate "PAM-KRB5 (auth): Renewable tickets "
6823bfb48feSsemery "requested");
683488060a6SWill Fiveash krb5_get_init_creds_opt_set_renew_life(opts, rlife);
6847c478bd9Sstevel@tonic-gate }
6857c478bd9Sstevel@tonic-gate if (no_address_flag) {
6867c478bd9Sstevel@tonic-gate if (kmd->debug)
6873bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG,
6887c478bd9Sstevel@tonic-gate "PAM-KRB5 (auth): Addressless tickets "
6893bfb48feSsemery "requested");
690488060a6SWill Fiveash krb5_get_init_creds_opt_set_address_list(opts, NULL);
6917c478bd9Sstevel@tonic-gate }
6927c478bd9Sstevel@tonic-gate
6937c478bd9Sstevel@tonic-gate /*
69470f41fc1SWill Fiveash * mech_krb5 interprets empty passwords as NULL passwords and tries to
69570f41fc1SWill Fiveash * read a password from stdin. Since we are in pam this is bad and
69670f41fc1SWill Fiveash * should not be allowed.
69770f41fc1SWill Fiveash *
69870f41fc1SWill Fiveash * Note, the logic now is that if the preauth_type is PKINIT then
69970f41fc1SWill Fiveash * provide a proper PAMcentric prompt function that the underlying
70070f41fc1SWill Fiveash * PKINIT preauth plugin will use to prompt for the PIN.
70170f41fc1SWill Fiveash */
70270f41fc1SWill Fiveash if (kmd->preauth_type == KRB_PKINIT) {
70370f41fc1SWill Fiveash /*
70470f41fc1SWill Fiveash * Do PKINIT preauth
70570f41fc1SWill Fiveash *
70670f41fc1SWill Fiveash * Note: we want to limit preauth types to just those for PKINIT
70770f41fc1SWill Fiveash * but krb5_get_init_creds() doesn't support that at this point.
70870f41fc1SWill Fiveash * Instead we rely on pam_krb5_prompter() to limit prompts to
70970f41fc1SWill Fiveash * non-password types. So all we can do here is set the preauth
71070f41fc1SWill Fiveash * list so krb5_get_init_creds() will try that first.
71170f41fc1SWill Fiveash */
71270f41fc1SWill Fiveash krb5_preauthtype pk_pa_list[] = {
71370f41fc1SWill Fiveash KRB5_PADATA_PK_AS_REQ,
71470f41fc1SWill Fiveash KRB5_PADATA_PK_AS_REQ_OLD
71570f41fc1SWill Fiveash };
716488060a6SWill Fiveash krb5_get_init_creds_opt_set_preauth_list(opts, pk_pa_list, 2);
71770f41fc1SWill Fiveash
718488060a6SWill Fiveash if (*krb5_pass == NULL || strlen(*krb5_pass) != 0) {
719488060a6SWill Fiveash if (*krb5_pass != NULL) {
720488060a6SWill Fiveash /* treat the krb5_pass as a PIN */
721488060a6SWill Fiveash code = krb5_get_init_creds_opt_set_pa(
722488060a6SWill Fiveash kmd->kcontext, opts, "PIN", *krb5_pass);
723488060a6SWill Fiveash }
724488060a6SWill Fiveash
725488060a6SWill Fiveash if (!code) {
726488060a6SWill Fiveash code = __krb5_get_init_creds_password(
727488060a6SWill Fiveash kmd->kcontext,
72870f41fc1SWill Fiveash my_creds,
72970f41fc1SWill Fiveash me,
73070f41fc1SWill Fiveash NULL, /* clear text passwd */
73170f41fc1SWill Fiveash pam_krb5_prompter, /* prompter */
73270f41fc1SWill Fiveash pamh, /* prompter data */
73370f41fc1SWill Fiveash 0, /* start time */
73470f41fc1SWill Fiveash NULL, /* defaults to krbtgt@REALM */
735488060a6SWill Fiveash opts,
73670f41fc1SWill Fiveash &as_reply);
737488060a6SWill Fiveash }
73870f41fc1SWill Fiveash } else {
739488060a6SWill Fiveash /* invalid PIN */
74070f41fc1SWill Fiveash code = KRB5KRB_AP_ERR_BAD_INTEGRITY;
74170f41fc1SWill Fiveash }
74270f41fc1SWill Fiveash } else {
74370f41fc1SWill Fiveash /*
74470f41fc1SWill Fiveash * Do password based preauths
74570f41fc1SWill Fiveash *
74670f41fc1SWill Fiveash * See earlier PKINIT comment. We are doing something similar
74770f41fc1SWill Fiveash * here but we do not pass in a prompter (we assume
74870f41fc1SWill Fiveash * pam_authtok_get has already prompted for that).
7497c478bd9Sstevel@tonic-gate */
7507c478bd9Sstevel@tonic-gate if (*krb5_pass == NULL || strlen(*krb5_pass) == 0) {
7517c478bd9Sstevel@tonic-gate code = KRB5KRB_AP_ERR_BAD_INTEGRITY;
7527c478bd9Sstevel@tonic-gate } else {
75370f41fc1SWill Fiveash krb5_preauthtype pk_pa_list[] = {
75470f41fc1SWill Fiveash KRB5_PADATA_ENC_TIMESTAMP
75570f41fc1SWill Fiveash };
75670f41fc1SWill Fiveash
757488060a6SWill Fiveash krb5_get_init_creds_opt_set_preauth_list(opts,
75870f41fc1SWill Fiveash pk_pa_list, 1);
7593125ebfcSsemery
7603125ebfcSsemery /*
76170f41fc1SWill Fiveash * We call our own private version of gic_pwd, because
76270f41fc1SWill Fiveash * we need more information, such as password/account
76370f41fc1SWill Fiveash * expiration, that is found in the as_reply. The
76470f41fc1SWill Fiveash * "prompter" interface is not granular enough for PAM
76570f41fc1SWill Fiveash * to make use of.
7663125ebfcSsemery */
7673125ebfcSsemery code = __krb5_get_init_creds_password(kmd->kcontext,
7687c478bd9Sstevel@tonic-gate my_creds,
7697c478bd9Sstevel@tonic-gate me,
7707c478bd9Sstevel@tonic-gate *krb5_pass, /* clear text passwd */
7717c478bd9Sstevel@tonic-gate NULL, /* prompter */
7727c478bd9Sstevel@tonic-gate NULL, /* data */
7737c478bd9Sstevel@tonic-gate 0, /* start time */
7747c478bd9Sstevel@tonic-gate NULL, /* defaults to krbtgt@REALM */
775488060a6SWill Fiveash opts,
7763125ebfcSsemery &as_reply);
7777c478bd9Sstevel@tonic-gate }
77870f41fc1SWill Fiveash }
7797c478bd9Sstevel@tonic-gate
7807c478bd9Sstevel@tonic-gate if (kmd->debug)
7813bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG,
7827c478bd9Sstevel@tonic-gate "PAM-KRB5 (auth): attempt_krb5_auth: "
7837c478bd9Sstevel@tonic-gate "krb5_get_init_creds_password returns: %s",
7847c478bd9Sstevel@tonic-gate code == 0 ? "SUCCESS" : error_message(code));
7857c478bd9Sstevel@tonic-gate
7867c478bd9Sstevel@tonic-gate switch (code) {
7877c478bd9Sstevel@tonic-gate case 0:
7887c478bd9Sstevel@tonic-gate /* got a tgt, let's verify it */
7897c478bd9Sstevel@tonic-gate if (verify_tik) {
7907c478bd9Sstevel@tonic-gate krb5_verify_init_creds_opt vopts;
7917c478bd9Sstevel@tonic-gate
792a0709436Smp153739 krb5_principal sp = NULL;
793a0709436Smp153739 char kt_name[MAX_KEYTAB_NAME_LEN];
794a0709436Smp153739 char *fqdn;
795a0709436Smp153739
7967c478bd9Sstevel@tonic-gate krb5_verify_init_creds_opt_init(&vopts);
7977c478bd9Sstevel@tonic-gate
7987c478bd9Sstevel@tonic-gate code = krb5_verify_init_creds(kmd->kcontext,
7997c478bd9Sstevel@tonic-gate my_creds,
8007c478bd9Sstevel@tonic-gate NULL, /* defaults to host/localhost@REALM */
8017c478bd9Sstevel@tonic-gate NULL,
8027c478bd9Sstevel@tonic-gate NULL,
8037c478bd9Sstevel@tonic-gate &vopts);
8047c478bd9Sstevel@tonic-gate
8057c478bd9Sstevel@tonic-gate if (code) {
8067c478bd9Sstevel@tonic-gate result = PAM_SYSTEM_ERR;
8077c478bd9Sstevel@tonic-gate
808a0709436Smp153739 /*
8090be37caaSsemery * Give a better error message when the
8100be37caaSsemery * keytable entry isn't found or the keytab
8110be37caaSsemery * file cannot be found.
812a0709436Smp153739 */
813a0709436Smp153739 if (krb5_sname_to_principal(kmd->kcontext, NULL,
814a0709436Smp153739 NULL, KRB5_NT_SRV_HST, &sp))
815a0709436Smp153739 fqdn = "<fqdn>";
816a0709436Smp153739 else
817a0709436Smp153739 fqdn = sp->data[1].data;
818a0709436Smp153739
819a0709436Smp153739 if (krb5_kt_default_name(kmd->kcontext, kt_name,
820a0709436Smp153739 sizeof (kt_name)))
821b0c1f5b7SWill Fiveash (void) strlcpy(kt_name,
822a0709436Smp153739 "default keytab",
823a0709436Smp153739 sizeof (kt_name));
824a0709436Smp153739
825a0709436Smp153739 switch (code) {
826a0709436Smp153739 case KRB5_KT_NOTFOUND:
8273bfb48feSsemery __pam_log(LOG_AUTH | LOG_ERR,
8287c478bd9Sstevel@tonic-gate "PAM-KRB5 (auth): "
829a0709436Smp153739 "krb5_verify_init_creds failed:"
830a0709436Smp153739 " Key table entry \"host/%s\""
8313bfb48feSsemery " not found in %s",
832a0709436Smp153739 fqdn, kt_name);
833a0709436Smp153739 break;
834a0709436Smp153739 case ENOENT:
8353bfb48feSsemery __pam_log(LOG_AUTH | LOG_ERR,
836a0709436Smp153739 "PAM-KRB5 (auth): "
837a0709436Smp153739 "krb5_verify_init_creds failed:"
838a0709436Smp153739 " Keytab file \"%s\""
8393bfb48feSsemery " does not exist.\n",
840a0709436Smp153739 kt_name);
841a0709436Smp153739 break;
842a0709436Smp153739 default:
8433bfb48feSsemery __pam_log(LOG_AUTH | LOG_ERR,
844a0709436Smp153739 "PAM-KRB5 (auth): "
845a0709436Smp153739 "krb5_verify_init_creds failed:"
8463bfb48feSsemery " %s",
8477c478bd9Sstevel@tonic-gate error_message(code));
848a0709436Smp153739 break;
849a0709436Smp153739 }
850a0709436Smp153739
851a0709436Smp153739 if (sp)
852a0709436Smp153739 krb5_free_principal(kmd->kcontext, sp);
8537c478bd9Sstevel@tonic-gate }
8547c478bd9Sstevel@tonic-gate }
8553125ebfcSsemery
8563125ebfcSsemery if (code == 0)
8573125ebfcSsemery kmd->expiration = as_reply->enc_part2->key_exp;
8583125ebfcSsemery
8597c478bd9Sstevel@tonic-gate break;
8607c478bd9Sstevel@tonic-gate
8617c478bd9Sstevel@tonic-gate case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN:
8627c478bd9Sstevel@tonic-gate /*
8637c478bd9Sstevel@tonic-gate * Since this principal is not part of the local
8647c478bd9Sstevel@tonic-gate * Kerberos realm, we just return PAM_USER_UNKNOWN.
8657c478bd9Sstevel@tonic-gate */
8667c478bd9Sstevel@tonic-gate result = PAM_USER_UNKNOWN;
8677c478bd9Sstevel@tonic-gate
8687c478bd9Sstevel@tonic-gate if (kmd->debug)
8693bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG,
8703bfb48feSsemery "PAM-KRB5 (auth): attempt_krb5_auth:"
8717c478bd9Sstevel@tonic-gate " User is not part of the local Kerberos"
8727c478bd9Sstevel@tonic-gate " realm: %s", error_message(code));
8737c478bd9Sstevel@tonic-gate break;
8747c478bd9Sstevel@tonic-gate
8757c478bd9Sstevel@tonic-gate case KRB5KDC_ERR_PREAUTH_FAILED:
8767c478bd9Sstevel@tonic-gate case KRB5KRB_AP_ERR_BAD_INTEGRITY:
8777c478bd9Sstevel@tonic-gate /*
8787c478bd9Sstevel@tonic-gate * We could be trying the password from a previous
8797c478bd9Sstevel@tonic-gate * pam authentication module, but we don't want to
8807c478bd9Sstevel@tonic-gate * generate an error if the unix password is different
8817c478bd9Sstevel@tonic-gate * than the Kerberos password...
8827c478bd9Sstevel@tonic-gate */
8837c478bd9Sstevel@tonic-gate break;
8847c478bd9Sstevel@tonic-gate
8857c478bd9Sstevel@tonic-gate case KRB5KDC_ERR_KEY_EXP:
8867c478bd9Sstevel@tonic-gate if (!kmd->err_on_exp) {
8877c478bd9Sstevel@tonic-gate /*
88870f41fc1SWill Fiveash * Request a tik for changepw service and it will tell
88970f41fc1SWill Fiveash * us if pw is good or not. If PKINIT is being done it
89070f41fc1SWill Fiveash * is possible that *krb5_pass may be NULL so check for
89170f41fc1SWill Fiveash * that. If that is the case this function will return
89270f41fc1SWill Fiveash * an error.
8937c478bd9Sstevel@tonic-gate */
89470f41fc1SWill Fiveash if (*krb5_pass != NULL) {
89570f41fc1SWill Fiveash code = krb5_verifypw(kuser, *krb5_pass,
89670f41fc1SWill Fiveash kmd->debug);
89770f41fc1SWill Fiveash if (kmd->debug) {
8983bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG,
89970f41fc1SWill Fiveash "PAM-KRB5 (auth): "
90070f41fc1SWill Fiveash "attempt_krb5_auth: "
9012278144aSsemery "verifypw %d", code);
90270f41fc1SWill Fiveash }
9037c478bd9Sstevel@tonic-gate if (code == 0) {
90470f41fc1SWill Fiveash /*
90570f41fc1SWill Fiveash * pw is good, set age status for
90670f41fc1SWill Fiveash * acct_mgmt.
90770f41fc1SWill Fiveash */
9087c478bd9Sstevel@tonic-gate kmd->age_status = PAM_NEW_AUTHTOK_REQD;
9097c478bd9Sstevel@tonic-gate }
9107c478bd9Sstevel@tonic-gate }
91170f41fc1SWill Fiveash
91270f41fc1SWill Fiveash }
9137c478bd9Sstevel@tonic-gate break;
9147c478bd9Sstevel@tonic-gate
9157c478bd9Sstevel@tonic-gate default:
9167c478bd9Sstevel@tonic-gate result = PAM_SYSTEM_ERR;
9177c478bd9Sstevel@tonic-gate if (kmd->debug)
9183bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG,
9193bfb48feSsemery "PAM-KRB5 (auth): error %d - %s",
9207c478bd9Sstevel@tonic-gate code, error_message(code));
9217c478bd9Sstevel@tonic-gate break;
9227c478bd9Sstevel@tonic-gate }
9237c478bd9Sstevel@tonic-gate
9247c478bd9Sstevel@tonic-gate if (code == 0) {
9257c478bd9Sstevel@tonic-gate /*
92670f41fc1SWill Fiveash * success for the entered pw or PKINIT succeeded.
9277c478bd9Sstevel@tonic-gate *
9287c478bd9Sstevel@tonic-gate * we can't rely on the pw in PAM_AUTHTOK
9297c478bd9Sstevel@tonic-gate * to be the (correct) krb5 one so
9307c478bd9Sstevel@tonic-gate * store krb5 pw in module data for
93170f41fc1SWill Fiveash * use in acct_mgmt. Note that *krb5_pass may be NULL if we're
93270f41fc1SWill Fiveash * doing PKINIT.
9337c478bd9Sstevel@tonic-gate */
93470f41fc1SWill Fiveash if (*krb5_pass != NULL &&
93570f41fc1SWill Fiveash !(kmd->password = strdup(*krb5_pass))) {
93670f41fc1SWill Fiveash __pam_log(LOG_AUTH | LOG_ERR,
93770f41fc1SWill Fiveash "Cannot strdup password");
9387c478bd9Sstevel@tonic-gate result = PAM_BUF_ERR;
9397c478bd9Sstevel@tonic-gate goto out_err;
9407c478bd9Sstevel@tonic-gate }
94170f41fc1SWill Fiveash
9427c478bd9Sstevel@tonic-gate result = PAM_SUCCESS;
9437c478bd9Sstevel@tonic-gate goto out;
9447c478bd9Sstevel@tonic-gate }
9457c478bd9Sstevel@tonic-gate
9467c478bd9Sstevel@tonic-gate out_err:
9477c478bd9Sstevel@tonic-gate /* jump (or reach) here if error and cred cache has been init */
9487c478bd9Sstevel@tonic-gate
9497c478bd9Sstevel@tonic-gate if (kmd->debug)
9503bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG,
9517c478bd9Sstevel@tonic-gate "PAM-KRB5 (auth): clearing initcreds in "
9527c478bd9Sstevel@tonic-gate "pam_authenticate()");
9537c478bd9Sstevel@tonic-gate
9547c478bd9Sstevel@tonic-gate krb5_free_cred_contents(kmd->kcontext, &kmd->initcreds);
9557c478bd9Sstevel@tonic-gate (void) memset((char *)&kmd->initcreds, 0, sizeof (krb5_creds));
9567c478bd9Sstevel@tonic-gate
9577c478bd9Sstevel@tonic-gate out:
9587c478bd9Sstevel@tonic-gate if (server)
9597c478bd9Sstevel@tonic-gate krb5_free_principal(kmd->kcontext, server);
9607c478bd9Sstevel@tonic-gate if (me)
9617c478bd9Sstevel@tonic-gate krb5_free_principal(kmd->kcontext, me);
9623125ebfcSsemery if (as_reply)
9633125ebfcSsemery krb5_free_kdc_rep(kmd->kcontext, as_reply);
96467c90040Ssemery
96567c90040Ssemery /*
96667c90040Ssemery * clientp or serverp could be NULL in certain error cases in this
96767c90040Ssemery * function. mycreds->[client|server] could also be NULL in case
96867c90040Ssemery * of error in this function, see out_err above. The pointers clientp
96967c90040Ssemery * and serverp reference the input argument in my_creds for
97067c90040Ssemery * get_init_creds and must be freed if the input argument does not
97167c90040Ssemery * match the output argument, which occurs during a successful call
97267c90040Ssemery * to get_init_creds.
97367c90040Ssemery */
97467c90040Ssemery if (clientp && my_creds->client && clientp != my_creds->client)
97567c90040Ssemery krb5_free_principal(kmd->kcontext, clientp);
97667c90040Ssemery if (serverp && my_creds->server && serverp != my_creds->server)
97767c90040Ssemery krb5_free_principal(kmd->kcontext, serverp);
97867c90040Ssemery
9797c478bd9Sstevel@tonic-gate if (kmd->kcontext) {
9807c478bd9Sstevel@tonic-gate krb5_free_context(kmd->kcontext);
9817c478bd9Sstevel@tonic-gate kmd->kcontext = NULL;
9827c478bd9Sstevel@tonic-gate }
983488060a6SWill Fiveash if (opts)
984488060a6SWill Fiveash krb5_get_init_creds_opt_free(kmd->kcontext, opts);
9857c478bd9Sstevel@tonic-gate
9867c478bd9Sstevel@tonic-gate if (kmd->debug)
9873bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG,
9887c478bd9Sstevel@tonic-gate "PAM-KRB5 (auth): attempt_krb5_auth returning %d",
9897c478bd9Sstevel@tonic-gate result);
9907c478bd9Sstevel@tonic-gate
9917c478bd9Sstevel@tonic-gate return (kmd->auth_status = result);
9927c478bd9Sstevel@tonic-gate }
9937c478bd9Sstevel@tonic-gate
9947c478bd9Sstevel@tonic-gate /*ARGSUSED*/
9957c478bd9Sstevel@tonic-gate void
krb5_cleanup(pam_handle_t * pamh,void * data,int pam_status)9967c478bd9Sstevel@tonic-gate krb5_cleanup(pam_handle_t *pamh, void *data, int pam_status)
9977c478bd9Sstevel@tonic-gate {
9987c478bd9Sstevel@tonic-gate krb5_module_data_t *kmd = (krb5_module_data_t *)data;
9997c478bd9Sstevel@tonic-gate
10007c478bd9Sstevel@tonic-gate if (kmd == NULL)
10017c478bd9Sstevel@tonic-gate return;
10027c478bd9Sstevel@tonic-gate
10037c478bd9Sstevel@tonic-gate if (kmd->debug) {
10043bfb48feSsemery __pam_log(LOG_AUTH | LOG_DEBUG,
10053bfb48feSsemery "PAM-KRB5 (auth): krb5_cleanup auth_status = %d",
10067c478bd9Sstevel@tonic-gate kmd->auth_status);
10077c478bd9Sstevel@tonic-gate }
10087c478bd9Sstevel@tonic-gate
10097c478bd9Sstevel@tonic-gate /*
101067c90040Ssemery * Apps could be calling pam_end here, so we should always clean
101167c90040Ssemery * up regardless of success or failure here.
10127c478bd9Sstevel@tonic-gate */
101367c90040Ssemery if (kmd->ccache)
10145ad42b1bSSurya Prakki (void) krb5_cc_close(kmd->kcontext, kmd->ccache);
10157c478bd9Sstevel@tonic-gate
10167c478bd9Sstevel@tonic-gate if (kmd->password) {
10177c478bd9Sstevel@tonic-gate (void) memset(kmd->password, 0, strlen(kmd->password));
10187c478bd9Sstevel@tonic-gate free(kmd->password);
10197c478bd9Sstevel@tonic-gate }
10207c478bd9Sstevel@tonic-gate
102167c90040Ssemery if (kmd->user)
10223bfb48feSsemery free(kmd->user);
10233bfb48feSsemery
102467c90040Ssemery if (kmd->env)
102567c90040Ssemery free(kmd->env);
102667c90040Ssemery
10277c478bd9Sstevel@tonic-gate krb5_free_cred_contents(kmd->kcontext, &kmd->initcreds);
10287c478bd9Sstevel@tonic-gate (void) memset((char *)&kmd->initcreds, 0, sizeof (krb5_creds));
10297c478bd9Sstevel@tonic-gate
10307c478bd9Sstevel@tonic-gate free(kmd);
10317c478bd9Sstevel@tonic-gate }
1032