From: Todd C. Miller Date: Thu, 3 Jun 1999 15:51:44 +0000 (+0000) Subject: move authentication code from check.c to auth.c X-Git-Tag: SUDO_1_6_0~277 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=7d768c1cec06198ac975b9b6b7c2d6abec23728b;p=sudo move authentication code from check.c to auth.c --- diff --git a/auth.c b/auth.c new file mode 100644 index 000000000..a169402e1 --- /dev/null +++ b/auth.c @@ -0,0 +1,884 @@ +/* + * CU sudo version 1.6 + * Copyright (c) 1994,1996,1998,1999 Todd C. Miller + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 1, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Please send bugs, changes, problems to sudo-bugs@courtesan.com + * + ******************************************************************* + * + * This module contains routines to authenticate a user. + */ + +#include "config.h" + +#include +#ifdef STDC_HEADERS +#include +#endif /* STDC_HEADERS */ +#ifdef HAVE_UNISTD_H +#include +#endif /* HAVE_UNISTD_H */ +#ifdef HAVE_STRING_H +#include +#endif /* HAVE_STRING_H */ +#ifdef HAVE_STRINGS_H +#include +#endif /* HAVE_STRINGS_H */ +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_KERB4 +# include +#endif /* HAVE_KERB4 */ +#ifdef HAVE_KERB5 +# include +#endif /* HAVE_KERB5 */ +#ifdef HAVE_PAM +# include +#endif /* HAVE_PAM */ +#ifdef HAVE_AFS +# include +# include +#endif /* HAVE_AFS */ +#ifdef HAVE_SECURID +# include +# include +# include +#endif /* HAVE_SECURID */ +#ifdef HAVE_SKEY +# include +#endif /* HAVE_SKEY */ +#ifdef HAVE_OPIE +# include +#endif /* HAVE_OPIE */ +#ifdef HAVE_AUTHSRV +# include +#endif + +#include "sudo.h" +#include "insults.h" + +#ifndef lint +static const char rcsid[] = "$Sudo$"; +#endif /* lint */ + +/* + * Prototypes for local functions + */ +#ifdef HAVE_KERB4 +static int sudo_krb_validate_user __P((struct passwd *, char *)); +#endif /* HAVE_KERB4 */ +#ifdef HAVE_KERB5 +static int sudo_krb5_validate_user __P((struct passwd *, char *)); +static int verify_krb_v5_tgt __P((krb5_ccache)); +#endif /* HAVE_KERB5 */ +#ifdef HAVE_PAM +static int pam_auth __P((char *, char *)); +static int PAM_conv __P((int, + PAM_CONST struct pam_message **, + struct pam_response **, void *)); +#endif /* HAVE_PAM */ +#ifdef HAVE_SKEY +static char *sudo_skeyprompt __P((struct skey *, char *)); +#endif /* HAVE_SKEY */ +#ifdef HAVE_OPIE +static char *sudo_opieprompt __P((struct opie *, char *)); +#endif /* HAVE_OPIE */ +int user_is_exempt __P((void)); + +/* + * Globals + */ +#ifdef HAVE_SECURID +union config_record configure; +#endif /* HAVE_SECURID */ +#ifdef HAVE_SKEY +struct skey skey; +#endif +#ifdef HAVE_OPIE +struct opie opie; +#endif +#ifdef HAVE_KERB5 +extern krb5_context sudo_context; +extern char *realm; +extern int xrealm; +#endif /* HAVE_KERB5 */ + + +/******************************************************************** + * + * check_passwd() + * + * This function grabs the user's password and checks with the password + * in /etc/passwd (or uses other specified authentication method). + */ + +#ifdef HAVE_SECURID +void +check_passwd() +{ + struct SD_CLIENT sd_dat, *sd; /* SecurID data block */ + register int counter = TRIES_FOR_PASSWORD; + + (void) memset((VOID *)&sd_dat, 0, sizeof(sd_dat)); + sd = &sd_dat; + + /* Initialize SecurID. */ + set_perms(PERM_ROOT, 0); + creadcfg(); + if (sd_init(sd) != 0) { + (void) fprintf(stderr, "%s: Cannot contact SecurID server\n", Argv[0]); + exit(1); + } + + /* + * you get TRIES_FOR_PASSWORD times to guess your password + */ + while (counter > 0) { + if (sd_auth(sd) == ACM_OK) { + set_perms(PERM_USER, 0); + return; + } + + --counter; /* otherwise, try again */ + pass_warn(stderr); + } + set_perms(PERM_USER, 0); + + if (counter > 0) { + log_error(PASSWORD_NOT_CORRECT); + inform_user(PASSWORD_NOT_CORRECT); + } else { + log_error(PASSWORDS_NOT_CORRECT); + inform_user(PASSWORDS_NOT_CORRECT); + } + + exit(1); +} +#else /* !HAVE_SECURID */ +#ifdef HAVE_AUTHSRV +void +check_passwd() +{ + char *pass; /* this is what gets entered */ + Cfg *confp; + + char cbuf[128]; + char ubuf[128], buf[128]; + register int counter = TRIES_FOR_PASSWORD; + + if ((confp = cfg_read("sudo")) == (Cfg *)-1) { + fprintf(stderr, "Cannot read config.\n"); + exit(1); + } + + /* Initialize Auth Client */ + auth_open(confp); + + /* get welcome message from auth server */ + if (auth_recv(buf, sizeof(buf))) { + sprintf(buf, "Lost connection to server"); + fprintf(stderr, "%s\n", buf); + exit(1); + } + + if (strncmp(buf, "Authsrv ready", 13)) { + fprintf(stderr, "Auth server error %s\n", buf); + auth_close(); + exit(1); + } + + /* + * you get TRIES_FOR_PASSWORD times to guess your password + */ + while (counter > 0) { + + sprintf(cbuf,"authorize %s sudo",user_name); + + auth_send(cbuf); + auth_recv(cbuf,sizeof(cbuf)); + + if (!strncmp(cbuf, "challenge ", 10)) { + sprintf(buf, "Challenge \"%s\": ", &cbuf[10]); + pass = GETPASS(buf, PASSWORD_TIMEOUT * 60); + } else if (!strncmp(cbuf, "password", 8)) { + pass = GETPASS(buf, PASSWORD_TIMEOUT * 60); + } else { + fprintf(stderr, "Server sent %s\n", cbuf); + auth_close(); + exit(1); + } + + sprintf(cbuf, "response '%s'", pass); + auth_send(cbuf); + auth_recv(cbuf, sizeof(cbuf)); + + if (!strncmp(cbuf, "ok", 2)) { + /* Success */ + /*inform_user(cbuf);*/ + set_perms(PERM_USER, 0); + auth_close(); + return; + } else { + fprintf(stderr, "Server returned %s\n", cbuf); + } + pass_warn(stderr); + --counter; /* otherwise, try again */ + } + + set_perms(PERM_USER, 0); + + auth_close(); + + if (counter > 0) { + log_error(PASSWORD_NOT_CORRECT); + inform_user(PASSWORD_NOT_CORRECT); + } else { + log_error(PASSWORDS_NOT_CORRECT); + inform_user(PASSWORDS_NOT_CORRECT); + } + exit(1); +} + +#else /* !HAVE_AUTHSRV */ + +void +check_passwd() +{ + char *pass; /* this is what gets entered */ + int counter = TRIES_FOR_PASSWORD; +#if defined(HAVE_KERB4) && defined(USE_GETPASS) + char kpass[SUDO_PASS_MAX + 1]; +#endif /* HAVE_KERB4 && USE_GETPASS */ +#ifdef HAVE_AUTHENTICATE + char *message; + int reenter; +#endif /* HAVE_AUTHENTICATE */ + +#ifdef HAVE_SKEY + (void) memset((VOID *)&skey, 0, sizeof(skey)); +#endif /* HAVE_SKEY */ +#ifdef HAVE_OPIE + (void) memset((VOID *)&opie, 0, sizeof(opie)); +#endif /* HAVE_OPIE */ + + /* + * you get TRIES_FOR_PASSWORD times to guess your password + */ + while (counter > 0) { + +#ifdef HAVE_AUTHENTICATE + /* use AIX authenticate() function */ + pass = GETPASS(prompt, PASSWORD_TIMEOUT * 60); + reenter = 1; + if (authenticate(user_name, pass, &reenter, &message) == 0) + return; /* valid password */ +#else /* HAVE_AUTHENTICATE */ +# ifdef HAVE_SKEY + /* rewrite the prompt if using s/key since the challenge can change */ + set_perms(PERM_ROOT, 0); + prompt = sudo_skeyprompt(&skey, prompt); + set_perms(PERM_USER, 0); +# endif /* HAVE_SKEY */ +# ifdef HAVE_OPIE + /* rewrite the prompt if using OPIE since the challenge can change */ + set_perms(PERM_ROOT, 0); + prompt = sudo_opieprompt(&opie, prompt); + set_perms(PERM_USER, 0); +# endif /* HAVE_OPIE */ + + /* get a password from the user */ +# if defined(HAVE_KERB4) && defined(USE_GETPASS) + (void) des_read_pw_string(kpass, sizeof(kpass) - 1, prompt, 0); + pass = kpass; +# else + pass = (char *) GETPASS(prompt, PASSWORD_TIMEOUT * 60); +# endif /* HAVE_KERB4 */ + +# ifdef HAVE_SKEY + /* Only check s/key db if the user exists there */ + if (skey.keyfile) { + set_perms(PERM_ROOT, 0); + if (skeyverify(&skey, pass) == 0) { + set_perms(PERM_USER, 0); + return; /* if the key is correct return() */ + } + set_perms(PERM_USER, 0); + } +# endif /* HAVE_SKEY */ +# ifdef HAVE_OPIE + /* Only check OPIE db if the user exists there */ + if (opie.opie_flags) { + set_perms(PERM_ROOT, 0); + if (opieverify(&opie, pass) == 0) { + set_perms(PERM_USER, 0); + return; /* if the key is correct return() */ + } + set_perms(PERM_USER, 0); + } +# endif /* HAVE_OPIE */ +# if !defined(OTP_ONLY) || (!defined(HAVE_SKEY) && !defined(HAVE_OPIE)) + /* + * If we use shadow passwords with a different crypt(3) + * check that here, else use standard crypt(3). + */ +# ifdef HAVE_GETAUTHUID + if (!strcmp(user_passwd, (char *) crypt16(pass, user_passwd))) + return; /* if the passwd is correct return() */ +# endif /* HAVE_GETAUTHUID */ + +# ifdef HAVE_GETPRPWNAM + if (check_secureware(pass)) + return; /* if the passwd is correct return() */ +# endif /* HAVE_HAVE_GETPRPWNAM */ + + /* Normal UN*X password check */ + if (!strcmp(user_passwd, (char *) crypt(pass, user_passwd))) + return; /* if the passwd is correct return() */ + +# ifdef HAVE_KERB4 + if (user_uid && sudo_krb_validate_user(user_pw_ent, pass) == 0) + return; +# endif /* HAVE_KERB4 */ + +# ifdef HAVE_KERB5 + if (sudo_krb5_validate_user(user_pw_ent, pass) == 0) + return; +# endif /* HAVE_KERB5 */ + +# ifdef HAVE_AFS + if (ka_UserAuthenticateGeneral(KA_USERAUTH_VERSION, + user_name, /* name */ + NULL, /* instance */ + NULL, /* realm */ + pass, /* password */ + 0, /* lifetime */ + 0, 0, /* spare */ + NULL) == 0) /* reason */ + return; +# endif /* HAVE_AFS */ +# ifdef HAVE_DCE + /* + * consult the DCE registry for password validation + * note that dce_pwent trashes pass upon return... + */ + if (dce_pwent(user_name, pass)) + return; +# endif /* HAVE_DCE */ +# endif /* !OTP_ONLY || (!HAVE_SKEY && !HAVE_OPIE) */ +#endif /* HAVE_AUTHENTICATE */ + + /* Exit loop on nil password, but give it a chance to match first. */ + if (!pass || *pass == '\0') { + if (counter == TRIES_FOR_PASSWORD) + exit(1); + else + break; + } + + --counter; /* otherwise, try again */ + pass_warn(stderr); + } + + if (counter > 0) { + log_error(PASSWORD_NOT_CORRECT); + inform_user(PASSWORD_NOT_CORRECT); + } else { + log_error(PASSWORDS_NOT_CORRECT); + inform_user(PASSWORDS_NOT_CORRECT); + } + + exit(1); +} +#endif /* HAVE_AUTHSRV */ +#endif /* HAVE_SECURID */ + + +#ifdef HAVE_KERB4 +/******************************************************************** + * + * sudo_krb_validate_user() + * + * Validate a user via kerberos. + */ +static int +sudo_krb_validate_user(pw, pass) + struct passwd *pw; + char *pass; +{ + char realm[REALM_SZ]; + char tkfile[sizeof(_PATH_SUDO_TIMEDIR) + 4 + MAX_UID_T_LEN]; + int k_errno; + + /* Get the local realm, or retrun failure (no krb.conf) */ + if (krb_get_lrealm(realm, 1) != KSUCCESS) + return(-1); + + /* + * Set the ticket file to be in sudo sudo timedir so we don't + * wipe out other kerberos tickets. + */ + (void) sprintf(tkfile, "%s/tkt%ld", _PATH_SUDO_TIMEDIR, + (long) pw->pw_uid); + (void) krb_set_tkt_string(tkfile); + + /* + * Update the ticket if password is ok. Kerb4 expects + * the ruid and euid to be the same here so we setuid to root. + */ + set_perms(PERM_ROOT, 0); + k_errno = krb_get_pw_in_tkt(pw->pw_name, "", realm, "krbtgt", realm, + DEFAULT_TKT_LIFE, pass); + + /* + * If we authenticated, destroy the ticket now that we are done with it. + * If not, warn on a "real" error. + */ + if (k_errno == INTK_OK) + dest_tkt(); + else if (k_errno != INTK_BADPW && k_errno != KDC_PR_UNKNOWN) + (void) fprintf(stderr, "Warning: Kerberos error: %s\n", + krb_err_txt[k_errno]); + + /* done with rootly stuff */ + set_perms(PERM_USER, 0); + + return(!(k_errno == INTK_OK)); +} +#endif /* HAVE_KERB4 */ + + +#ifdef HAVE_KERB5 +/******************************************************************** + * + * sudo_krb5_validate_user() + * + * Validate a user via Kerberos 5. We may lose a bit of memory, but it's + * OK since we're a short lived program. I'd rather do that than contort + * the code to handle the cleanup. + */ +static int +sudo_krb5_validate_user(pw, pass) + struct passwd *pw; + char *pass; +{ + krb5_error_code retval; + krb5_principal princ; + krb5_creds creds; + krb5_ccache ccache; + char cache_name[64]; + char *princ_name; + krb5_get_init_creds_opt opts; + + /* Initialize */ + if (!sudo_context) + return -1; + krb5_get_init_creds_opt_init(&opts); + + princ_name = emalloc(strlen(pw->pw_name) + strlen(realm) + 2); + + sprintf(princ_name, "%s@%s", pw->pw_name, realm); + if (retval = krb5_parse_name(sudo_context, princ_name, &princ)) + return retval; + + /* Set the ticket file to be in /tmp so we don't need to change perms. */ + (void) sprintf(cache_name, "FILE:/tmp/sudocc_%ld", getpid()); + if (retval = krb5_cc_resolve(sudo_context, cache_name, &ccache)) + return retval; + + if (retval = krb5_get_init_creds_password(sudo_context, &creds, princ, + pass, krb5_prompter_posix, NULL, + 0, NULL, &opts)) + return retval; + + /* Stash the TGT so we can verify it. */ + if (retval = krb5_cc_initialize(sudo_context, ccache, princ)) + return retval; + if (retval = krb5_cc_store_cred(sudo_context, ccache, &creds)) { + (void) krb5_cc_destroy(sudo_context, ccache); + return retval; + } + + retval = verify_krb_v5_tgt(ccache); + (void) krb5_cc_destroy(sudo_context, ccache); + return (retval == -1); +} + + +/* + * This routine with some modification is from the MIT V5B6 appl/bsd/login.c + * + * Verify the Kerberos ticket-granting ticket just retrieved for the + * user. If the Kerberos server doesn't respond, assume the user is + * trying to fake us out (since we DID just get a TGT from what is + * supposedly our KDC). If the host/ service is unknown (i.e., + * the local keytab doesn't have it), let her in. + * + * Returns 1 for confirmation, -1 for failure, 0 for uncertainty. + */ +static int +verify_krb_v5_tgt(ccache) + krb5_ccache ccache; +{ + char phost[BUFSIZ]; + krb5_error_code retval; + krb5_principal princ; + krb5_keyblock * keyblock = 0; + krb5_data packet; + krb5_auth_context auth_context = NULL; + + packet.data = 0; + + /* + * Get the server principal for the local host. + * (Use defaults of "host" and canonicalized local name.) + */ + if (krb5_sname_to_principal(sudo_context, NULL, NULL, + KRB5_NT_SRV_HST, &princ)) + return -1; + + /* Extract the name directly. */ + strncpy(phost, krb5_princ_component(c, princ, 1)->data, BUFSIZ); + phost[BUFSIZ - 1] = '\0'; + + /* + * Do we have host/ keys? + * (use default keytab, kvno IGNORE_VNO to get the first match, + * and enctype is currently ignored anyhow.) + */ + if (retval = krb5_kt_read_service_key(sudo_context, NULL, princ, 0, + ENCTYPE_DES_CBC_MD5, &keyblock)) { + /* Keytab or service key does not exist */ + if (xrealm) + retval = -1; + else + retval = 0; + goto cleanup; + } + if (keyblock) + krb5_free_keyblock(sudo_context, keyblock); + + /* Talk to the kdc and construct the ticket. */ + retval = krb5_mk_req(sudo_context, &auth_context, 0, "host", phost, + NULL, ccache, &packet); + if (auth_context) { + krb5_auth_con_free(sudo_context, auth_context); + auth_context = NULL; /* setup for rd_req */ + } + if (retval) { + retval = -1; + goto cleanup; + } + + /* Try to use the ticket. */ + retval = krb5_rd_req(sudo_context, &auth_context, &packet, princ, + NULL, NULL, NULL); + if (retval) { + retval = -1; + } else { + retval = 1; + } + +cleanup: + if (packet.data) + krb5_free_data_contents(sudo_context, &packet); + krb5_free_principal(sudo_context, princ); + return retval; + +} +#endif /* HAVE_KERB5 */ + + +#ifdef HAVE_PAM +/******************************************************************** + * pam_attempt_auth() + * + * Try to authenticate the user using Pluggable Authentication + * Modules (PAM). Added 9/11/98 by Gary J. Calvin + * Reworked for stock PAM by Amos Elberg and Todd Miller + */ +static char *PAM_username; +static char *PAM_password; + +static int +PAM_conv(num_msg, msg, resp, appdata_ptr) + int num_msg; + PAM_CONST struct pam_message **msg; + struct pam_response **resp; + void *appdata_ptr; +{ + int replies = 0; + struct pam_response *reply = NULL; + + if ((reply = malloc(sizeof(struct pam_response) * num_msg)) == NULL) + return(PAM_CONV_ERR); + + for (replies = 0; replies < num_msg; replies++) { + switch (msg[replies]->msg_style) { + case PAM_PROMPT_ECHO_ON: + reply[replies].resp_retcode = PAM_SUCCESS; + reply[replies].resp = estrdup(PAM_username); + /* PAM frees resp */ + break; + case PAM_PROMPT_ECHO_OFF: + reply[replies].resp_retcode = PAM_SUCCESS; + reply[replies].resp = estrdup(PAM_password); + /* PAM frees resp */ + break; + case PAM_TEXT_INFO: + /* fall through */ + case PAM_ERROR_MSG: + /* ignore it... */ + reply[replies].resp_retcode = PAM_SUCCESS; + reply[replies].resp = NULL; + break; + default: + /* Must be an error of some sort... */ + free(reply); + return(PAM_CONV_ERR); + } + } + if (reply) + *resp = reply; + + return(PAM_SUCCESS); +} + +static int +pam_auth(user, password) + char *user; + char *password; +{ + struct pam_conv PAM_conversation; + pam_handle_t *pamh; + + /* Initialize our variables for PAM */ + PAM_conversation.conv = PAM_conv; + PAM_conversation.appdata_ptr = NULL; + PAM_password = password; + PAM_username = user; + + /* + * Setting PAM_SILENT stops generation of error messages to syslog + * to enable debugging on Red Hat Linux set: + * /etc/pam.d/sudo: + * auth required /lib/security/pam_pwdb.so shadow nullok audit + * _OR_ change PAM_SILENT to 0 to force detailed reporting (logging) + */ + if (pam_start("sudo", user, &PAM_conversation, &pamh) != PAM_SUCCESS || + pam_authenticate(pamh, PAM_SILENT) != PAM_SUCCESS) { + pam_end(pamh, 0); + return(0); + } + + /* User authenticated successfully */ + pam_end(pamh, PAM_SUCCESS); + + return(1); +} + +void +pam_attempt_auth() +{ + int i = TRIES_FOR_PASSWORD; + + set_perms(PERM_ROOT, 0); + while (i > 0) { + char *pamPass = (char *) GETPASS(prompt, PASSWORD_TIMEOUT * 60); + + if (pam_auth(user_name, pamPass)) { + set_perms(PERM_USER, 0); + return; + } + --i; + pass_warn(stderr); + } + set_perms(PERM_USER, 0); + + if (i == 0) { + log_error(PASSWORD_NOT_CORRECT); + inform_user(PASSWORD_NOT_CORRECT); + } else { + log_error(PASSWORDS_NOT_CORRECT); + inform_user(PASSWORDS_NOT_CORRECT); + } + exit(1); +} +#endif /* HAVE_PAM */ + +#ifdef HAVE_SKEY +/******************************************************************** + * + * sudo_skeyprompt() + * + * This function rewrites and return the prompt based the + * s/key challenge * and fills in the user's skey structure. + */ + +static char * +sudo_skeyprompt(user_skey, p) + struct skey *user_skey; + char *p; +{ + char challenge[256]; + int rval; + static char *orig_prompt = NULL, *new_prompt = NULL; + static int op_len, np_size; + + /* save the original prompt */ + if (orig_prompt == NULL) { + orig_prompt = p; + op_len = strlen(p); + + /* ignore trailing colon */ + if (p[op_len - 1] == ':') + op_len--; + } + + /* close old stream */ + if (user_skey->keyfile) + (void) fclose(user_skey->keyfile); + + /* get the skey part of the prompt */ + if ((rval = skeychallenge(user_skey, user_name, challenge)) != 0) { +#ifdef OTP_ONLY + (void) fprintf(stderr, + "%s: You do not exist in the s/key database.\n", + Argv[0]); + exit(1); +#else + /* return the original prompt if we cannot get s/key info */ + return(orig_prompt); +#endif /* OTP_ONLY */ + } + + /* get space for new prompt with embedded s/key challenge */ + if (new_prompt == NULL) { + /* allocate space for new prompt */ + np_size = op_len + strlen(challenge) + 7; + new_prompt = (char *) emalloc(np_size); + } else { + /* already have space allocated, is it enough? */ + if (np_size < op_len + strlen(challenge) + 7) { + np_size = op_len + strlen(challenge) + 7; + new_prompt = (char *) erealloc(new_prompt, np_size); + } + } + + /* embed the s/key challenge into the new password prompt */ +#ifdef LONG_OTP_PROMPT + (void) sprintf(new_prompt, "%s\n%s", challenge, orig_prompt); +#else + (void) sprintf(new_prompt, "%.*s [ %s ]:", op_len, orig_prompt, challenge); +#endif /* LONG_OTP_PROMPT */ + + return(new_prompt); +} +#endif /* HAVE_SKEY */ + + +#ifdef HAVE_OPIE +/******************************************************************** + * + * sudo_opieprompt() + * + * This function rewrites and return the prompt based the + * OPIE challenge * and fills in the user's opie structure. + */ + +static char * +sudo_opieprompt(user_opie, p) + struct opie *user_opie; + char *p; +{ + char challenge[OPIE_CHALLENGE_MAX]; + int rval; + static char *orig_prompt = NULL, *new_prompt = NULL; + static int op_len, np_size; + + /* save the original prompt */ + if (orig_prompt == NULL) { + orig_prompt = p; + op_len = strlen(p); + + /* ignore trailing colon */ + if (p[op_len - 1] == ':') + op_len--; + } + + /* get the opie part of the prompt */ + if ((rval = opiechallenge(user_opie, user_name, challenge)) != 0) { +#ifdef OTP_ONLY + (void) fprintf(stderr, + "%s: You do not exist in the opie database.\n", + Argv[0]); + exit(1); +#else + /* return the original prompt if we cannot get opie info */ + return(orig_prompt); +#endif /* OTP_ONLY */ + } + + /* get space for new prompt with embedded opie challenge */ + if (new_prompt == NULL) { + /* allocate space for new prompt */ + np_size = op_len + strlen(challenge) + 7; + new_prompt = (char *) emalloc(np_size); + } else { + /* already have space allocated, is it enough? */ + if (np_size < op_len + strlen(challenge) + 7) { + np_size = op_len + strlen(challenge) + 7; + new_prompt = (char *) erealloc(new_prompt, np_size); + } + } + + /* embed the s/key challenge into the new password prompt */ +#ifdef LONG_OTP_PROMPT + (void) sprintf(new_prompt, "%s\n%s", challenge, orig_prompt); +#else + (void) sprintf(new_prompt, "%.*s [ %s ]:", op_len, orig_prompt, challenge); +#endif /* LONG_OTP_PROMPT */ + + return(new_prompt); +} +#endif /* HAVE_OPIE */ + + +/******************************************************************** + * + * pass_warn() + * + * warn the user that the password was incorrect + * (and insult them if insults are configured). + */ + +void +pass_warn(fp) + FILE *fp; +{ + +#ifdef USE_INSULTS + (void) fprintf(fp, "%s\n", INSULT); +#else + (void) fprintf(fp, "%s\n", INCORRECT_PASSWORD); +#endif /* USE_INSULTS */ +}