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 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 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 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 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