/* * src/clients/ksu/pam.c * * Copyright 2007,2009,2010 Red Hat, Inc. * * All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of Red Hat, Inc. nor the names of its contributors may be * used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * Convenience wrappers for using PAM. */ #include "autoconf.h" #ifdef USE_PAM #include #include #include #include #include #include "k5-int.h" #include "pam.h" #ifndef MAXPWSIZE #define MAXPWSIZE 128 #endif static int appl_pam_started; static pid_t appl_pam_starter = -1; static int appl_pam_session_opened; static int appl_pam_creds_initialized; static int appl_pam_pwchange_required; static pam_handle_t *appl_pamh; static struct pam_conv appl_pam_conv; static char *appl_pam_user; struct appl_pam_non_interactive_args { const char *user; const char *password; }; int appl_pam_enabled(krb5_context context, const char *section) { int enabled = 1; if ((context != NULL) && (context->profile != NULL)) { if (profile_get_boolean(context->profile, section, USE_PAM_CONFIGURATION_KEYWORD, NULL, enabled, &enabled) != 0) { enabled = 1; } } return enabled; } void appl_pam_cleanup(void) { if (getpid() != appl_pam_starter) { return; } #ifdef DEBUG printf("Called to clean up PAM.\n"); #endif if (appl_pam_creds_initialized) { #ifdef DEBUG printf("Deleting PAM credentials.\n"); #endif pam_setcred(appl_pamh, PAM_DELETE_CRED); appl_pam_creds_initialized = 0; } if (appl_pam_session_opened) { #ifdef DEBUG printf("Closing PAM session.\n"); #endif pam_close_session(appl_pamh, 0); appl_pam_session_opened = 0; } appl_pam_pwchange_required = 0; if (appl_pam_started) { #ifdef DEBUG printf("Shutting down PAM.\n"); #endif pam_end(appl_pamh, 0); appl_pam_started = 0; appl_pam_starter = -1; free(appl_pam_user); appl_pam_user = NULL; } } static int appl_pam_interactive_converse(int num_msg, const struct pam_message **msg, struct pam_response **presp, void *appdata_ptr) { const struct pam_message *message; struct pam_response *resp; int i, code; char *pwstring, pwbuf[MAXPWSIZE]; unsigned int pwsize; resp = malloc(sizeof(struct pam_response) * num_msg); if (resp == NULL) { return PAM_BUF_ERR; } memset(resp, 0, sizeof(struct pam_response) * num_msg); code = PAM_SUCCESS; for (i = 0; i < num_msg; i++) { message = &(msg[0][i]); /* XXX */ message = msg[i]; /* XXX */ pwstring = NULL; switch (message->msg_style) { case PAM_TEXT_INFO: case PAM_ERROR_MSG: printf("[%s]\n", message->msg ? message->msg : ""); fflush(stdout); resp[i].resp = NULL; resp[i].resp_retcode = PAM_SUCCESS; break; case PAM_PROMPT_ECHO_ON: case PAM_PROMPT_ECHO_OFF: if (message->msg_style == PAM_PROMPT_ECHO_ON) { if (fgets(pwbuf, sizeof(pwbuf), stdin) != NULL) { pwbuf[strcspn(pwbuf, "\r\n")] = '\0'; pwstring = pwbuf; } } else { pwstring = getpass(message->msg ? message->msg : ""); } if ((pwstring != NULL) && (pwstring[0] != '\0')) { pwsize = strlen(pwstring); resp[i].resp = malloc(pwsize + 1); if (resp[i].resp == NULL) { resp[i].resp_retcode = PAM_BUF_ERR; } else { memcpy(resp[i].resp, pwstring, pwsize); resp[i].resp[pwsize] = '\0'; resp[i].resp_retcode = PAM_SUCCESS; } } else { resp[i].resp_retcode = PAM_CONV_ERR; code = PAM_CONV_ERR; } break; default: break; } } *presp = resp; return code; } static int appl_pam_non_interactive_converse(int num_msg, const struct pam_message **msg, struct pam_response **presp, void *appdata_ptr) { const struct pam_message *message; struct pam_response *resp; int i, code; unsigned int pwsize; struct appl_pam_non_interactive_args *args; const char *pwstring; resp = malloc(sizeof(struct pam_response) * num_msg); if (resp == NULL) { return PAM_BUF_ERR; } args = appdata_ptr; memset(resp, 0, sizeof(struct pam_response) * num_msg); code = PAM_SUCCESS; for (i = 0; i < num_msg; i++) { message = &((*msg)[i]); message = msg[i]; pwstring = NULL; switch (message->msg_style) { case PAM_TEXT_INFO: case PAM_ERROR_MSG: break; case PAM_PROMPT_ECHO_ON: case PAM_PROMPT_ECHO_OFF: if (message->msg_style == PAM_PROMPT_ECHO_ON) { /* assume "user" */ pwstring = args->user; } else { /* assume "password" */ pwstring = args->password; } if ((pwstring != NULL) && (pwstring[0] != '\0')) { pwsize = strlen(pwstring); resp[i].resp = malloc(pwsize + 1); if (resp[i].resp == NULL) { resp[i].resp_retcode = PAM_BUF_ERR; } else { memcpy(resp[i].resp, pwstring, pwsize); resp[i].resp[pwsize] = '\0'; resp[i].resp_retcode = PAM_SUCCESS; } } else { resp[i].resp_retcode = PAM_CONV_ERR; code = PAM_CONV_ERR; } break; default: break; } } *presp = resp; return code; } static int appl_pam_start(const char *service, int interactive, const char *login_username, const char *non_interactive_password, const char *hostname, const char *ruser, const char *tty) { static int exit_handler_registered; static struct appl_pam_non_interactive_args args; int ret = 0; if (appl_pam_started && (strcmp(login_username, appl_pam_user) != 0)) { appl_pam_cleanup(); appl_pam_user = NULL; } if (!appl_pam_started) { #ifdef DEBUG printf("Starting PAM up (service=\"%s\",user=\"%s\").\n", service, login_username); #endif memset(&appl_pam_conv, 0, sizeof(appl_pam_conv)); appl_pam_conv.conv = interactive ? &appl_pam_interactive_converse : &appl_pam_non_interactive_converse; memset(&args, 0, sizeof(args)); args.user = strdup(login_username); args.password = non_interactive_password ? strdup(non_interactive_password) : NULL; appl_pam_conv.appdata_ptr = &args; ret = pam_start(service, login_username, &appl_pam_conv, &appl_pamh); if (ret == 0) { if (hostname != NULL) { #ifdef DEBUG printf("Setting PAM_RHOST to \"%s\".\n", hostname); #endif pam_set_item(appl_pamh, PAM_RHOST, hostname); } if (ruser != NULL) { #ifdef DEBUG printf("Setting PAM_RUSER to \"%s\".\n", ruser); #endif pam_set_item(appl_pamh, PAM_RUSER, ruser); } if (tty != NULL) { #ifdef DEBUG printf("Setting PAM_TTY to \"%s\".\n", tty); #endif pam_set_item(appl_pamh, PAM_TTY, tty); } if (!exit_handler_registered && (atexit(appl_pam_cleanup) != 0)) { pam_end(appl_pamh, 0); appl_pamh = NULL; ret = -1; } else { appl_pam_started = 1; appl_pam_starter = getpid(); appl_pam_user = strdup(login_username); exit_handler_registered = 1; } } } return ret; } int appl_pam_acct_mgmt(const char *service, int interactive, const char *login_username, const char *non_interactive_password, const char *hostname, const char *ruser, const char *tty) { int ret; appl_pam_pwchange_required = 0; ret = appl_pam_start(service, interactive, login_username, non_interactive_password, hostname, ruser, tty); if (ret == 0) { #ifdef DEBUG printf("Calling pam_acct_mgmt().\n"); #endif ret = pam_acct_mgmt(appl_pamh, 0); switch (ret) { case PAM_IGNORE: ret = 0; break; case PAM_NEW_AUTHTOK_REQD: appl_pam_pwchange_required = 1; ret = 0; break; default: break; } } return ret; } int appl_pam_requires_chauthtok(void) { return appl_pam_pwchange_required; } int appl_pam_session_open(void) { int ret = 0; if (appl_pam_started) { #ifdef DEBUG printf("Opening PAM session.\n"); #endif ret = pam_open_session(appl_pamh, 0); if (ret == 0) { appl_pam_session_opened = 1; } } return ret; } int appl_pam_setenv(void) { int ret = 0; #ifdef HAVE_PAM_GETENVLIST #ifdef HAVE_PUTENV int i; char **list; if (appl_pam_started) { list = pam_getenvlist(appl_pamh); for (i = 0; ((list != NULL) && (list[i] != NULL)); i++) { #ifdef DEBUG printf("Setting \"%s\" in environment.\n", list[i]); #endif putenv(list[i]); } } #endif #endif return ret; } int appl_pam_cred_init(void) { int ret = 0; if (appl_pam_started) { #ifdef DEBUG printf("Initializing PAM credentials.\n"); #endif ret = pam_setcred(appl_pamh, PAM_ESTABLISH_CRED); if (ret == 0) { appl_pam_creds_initialized = 1; } } return ret; } #endif