diff -cr openssh-3.5p1/auth-krb5.c openssh-3.5p1-krb5-kbdint/auth-krb5.c *** openssh-3.5p1/auth-krb5.c Wed Sep 11 19:47:30 2002 --- openssh-3.5p1-krb5-kbdint/auth-krb5.c Tue Apr 22 10:52:49 2003 *************** *** 32,37 **** --- 32,39 ---- #include "ssh.h" #include "ssh1.h" + #include "ssh2.h" + #include "dispatch.h" #include "packet.h" #include "xmalloc.h" #include "log.h" *************** *** 45,50 **** --- 47,83 ---- #define krb5_get_err_text(context,code) error_message(code) #endif /* !HEIMDAL */ + /* Some defines for auth_krb5_password_via_kbd_int */ + + #define KRB5_MAX_MSGS 5 + #define KRB5_MSG_SIZE 160 + + /* Keep track of the banner, not printing if the same as last time */ + + static char prev_banner[KRB5_MSG_SIZE]; + + /* keep track of the responses from the client with this struct */ + + struct { + int finished, num_received, num_expected; + int *prompts; + char responses[KRB5_MAX_MSGS][KRB5_MSG_SIZE]; /* way too many, hopefully */ + } context_krb5 = {0, 0, 0, NULL}; + + extern ServerOptions options; + static struct Authctxt *global_authctxt; + + /* propmter declaration */ + + krb5_error_code + krb5_prompter_openssh(krb5_context context, + void *data, + const char *name, + const char *banner, + int num_prompts, + krb5_prompt prompts[]); + + void dispatch_callback(int type, u_int32_t num, void *ctxt); extern ServerOptions options; static int *************** *** 375,380 **** --- 408,709 ---- return (0); } return (1); + } + + /* + * A copy of auth_krb5_password, but krb5_g_i_c_p() is called with the + * krb5_prompter_openssh() prompter + */ + + int + auth_krb5_password_via_kbd_int(Authctxt *authctxt) + { + #ifndef HEIMDAL + krb5_creds creds; + krb5_principal server; + char ccname[40]; + int tmpfd; + #endif + krb5_error_code problem; + + debug("In auth_krb5_password_via_kbd_int\n"); + + if (authctxt->pw == NULL) + return (0); + + temporarily_use_uid(authctxt->pw); + + problem = krb5_init(authctxt); + if (problem) { + debug("problem in krb5_init"); + goto out; + } + + problem = krb5_parse_name(authctxt->krb5_ctx, authctxt->pw->pw_name, + &authctxt->krb5_user); + if (problem) { + debug("problem in krb5_parse_name"); + goto out; + } + + /* End Get pass */ + + #ifdef HEIMDAL + problem = krb5_cc_gen_new(authctxt->krb5_ctx, &krb5_mcc_ops, + &authctxt->krb5_fwd_ccache); + if (problem) + goto out; + + problem = krb5_cc_initialize(authctxt->krb5_ctx, + authctxt->krb5_fwd_ccache, authctxt->krb5_user); + if (problem) + goto out; + + restore_uid(); + problem = krb5_verify_user(authctxt->krb5_ctx, authctxt->krb5_user, + authctxt->krb5_fwd_ccache, password, 1, NULL); + temporarily_use_uid(authctxt->pw); + + if (problem) + goto out; + + #else + global_authctxt = authctxt; + problem = krb5_get_init_creds_password(authctxt->krb5_ctx, &creds, + authctxt->krb5_user, NULL, krb5_prompter_openssh, NULL, 0, NULL, NULL); + if (problem) { + debug("problem in krb5_get_init_creds_password"); + goto out; + } + + problem = krb5_sname_to_principal(authctxt->krb5_ctx, NULL, NULL, + KRB5_NT_SRV_HST, &server); + if (problem) { + debug("problem in krb5_sname_to_principal"); + goto out; + } + + restore_uid(); + problem = krb5_verify_init_creds(authctxt->krb5_ctx, &creds, server, + NULL, NULL, NULL); + krb5_free_principal(authctxt->krb5_ctx, server); + temporarily_use_uid(authctxt->pw); + if (problem) { + debug("problem in krb5_verify_init_creds"); + goto out; + } + + if (!krb5_kuserok(authctxt->krb5_ctx, authctxt->krb5_user, + authctxt->pw->pw_name)) { + debug("problem in krb5_kuserok"); + problem = -1; + goto out; + } + + snprintf(ccname,sizeof(ccname),"FILE:/tmp/krb5cc_%d_XXXXXX",geteuid()); + + if ((tmpfd = mkstemp(ccname+strlen("FILE:")))==-1) { + log("mkstemp(): %.100s", strerror(errno)); + problem = errno; + goto out; + } + + if (fchmod(tmpfd,S_IRUSR | S_IWUSR) == -1) { + log("fchmod(): %.100s", strerror(errno)); + close(tmpfd); + problem = errno; + goto out; + } + close(tmpfd); + + problem = krb5_cc_resolve(authctxt->krb5_ctx, ccname, &authctxt->krb5_fwd_ccache); + if (problem) { + debug("problem in krb5_cc_resolve"); + goto out; + } + + problem = krb5_cc_initialize(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache, + authctxt->krb5_user); + if (problem) { + debug("problem in krb5_cc_initialize"); + goto out; + } + + problem= krb5_cc_store_cred(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache, + &creds); + if (problem) { + debug("problem in krb5_cc_store_cred"); + goto out; + } + #endif + + authctxt->krb5_ticket_file = (char *)krb5_cc_get_name(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache); + + out: + restore_uid(); + + if (problem) { + if (authctxt->krb5_ctx != NULL && problem!=-1) + debug("Kerberos password authentication failed: %s", + krb5_get_err_text(authctxt->krb5_ctx, problem)); + else + debug("Kerberos password authentication failed: %d", + problem); + + krb5_cleanup_proc(authctxt); + + if (options.kerberos_or_local_passwd) + return (-1); + else + return (0); + } + return (1); + } + + /* + * krb5_prompter_openssh() : this could probably be refactored, but it's + * working for now. + */ + + krb5_error_code + krb5_prompter_openssh(krb5_context context, + void *data, + const char *name, + const char *banner, + int num_prompts, + krb5_prompt prompts[]) + { + /* adapted from Kerberos v5 krb5_prompter_posix(), and openssh + do_pam_conversation_kbd_int() */ + + char prompt_buf[KRB5_MSG_SIZE]; + int done = 1; + dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, + &dispatch_callback); + + volatile krb5_error_code errcode; + int i; + static int prev_banner_init = 0; + + if (!prev_banner_init) { + prev_banner_init = 1; + memset(prev_banner, 0, KRB5_MSG_SIZE); + } + + memset((char *) &context_krb5, 0, sizeof(context_krb5)); + context_krb5.num_expected = num_prompts; + + /* Banner only */ + + if (context_krb5.num_expected == 0 && banner) { + packet_start(SSH2_MSG_USERAUTH_INFO_REQUEST); + packet_put_cstring(""); /* Name */ + packet_put_cstring(banner); /* Inst */ + packet_put_cstring(""); /* Language */ + packet_put_int(0); /* No prompts, thanks */ + packet_send(); + packet_write_wait(); + + /* Get back any dummy response */ + dispatch_run(DISPATCH_BLOCK, &done, (void *) global_authctxt); + return 0; + } + + /* + * Send over any prompts, and run dispatch_callback() to get responses + * Follow SSH2_MSG_USERAUTH_INFO_REQUEST protocol (client side found in + * sshconnect2.c:input_userauth_info_req()) + */ + + for (i = 0; i < context_krb5.num_expected; i++) { + snprintf(prompt_buf, KRB5_MSG_SIZE - 1, "%s:", + prompts[i].prompt); + + packet_start(SSH2_MSG_USERAUTH_INFO_REQUEST); + packet_put_cstring(""); /* Name */ + if (banner) { + if (strncmp(banner, prev_banner, sizeof(banner))) { + packet_put_cstring(banner); + strlcpy(prev_banner, banner, sizeof(prev_banner)); + } else { + packet_put_cstring(""); + } + } else { + packet_put_cstring(""); + } + packet_put_cstring(""); /* Language */ + packet_put_int(1); /* Num prompts */ + packet_put_cstring(prompt_buf); /* Prompt */ + debug("sending %s\n", prompt_buf); + packet_put_char(!prompts[i].hidden); /* ECHO */ + + packet_send(); + packet_write_wait(); + + done = 1; + dispatch_run(DISPATCH_BLOCK, &done, (void *) global_authctxt); + + prompts[i].reply->length = strlen(context_krb5.responses[i]); + /* KRB5_MSG_SIZE < the 1024 bytes allocated in krb5_g_i_c_p() */ + strlcpy(prompts[i].reply->data, context_krb5.responses[i], KRB5_MSG_SIZE); + } + + memset(prev_banner, 0, KRB5_MSG_SIZE); + errcode = 0; + + cleanup: + + return(errcode); + } + + /* + * dispatch_callback() : get (typically) passwords from ssh client, + * unless we're expecting no responses, then act like a noop. + */ + + void dispatch_callback(int type, u_int32_t num, void *ctxt) + { + Authctxt *authctxt = ctxt; + unsigned int nresp = 0, len = 0, i = 0; + char *password, *ptr; + + debug("In dispatch_callback\n"); + + nresp = packet_get_int(); /* Number of responses. */ + debug("nresp: %d\n", nresp); + + debug("got %d responses", nresp); + if (nresp > 1) + fatal("%s: Received incorrect number of responses " + "(expected %d, received %u)", __func__, + context_krb5.num_expected, nresp); + + /* + * The way this is written, nresp should either be 0 (banner only) + * or 1 (one prompt at a time regardless of total prompts). + */ + + for (i = 0; i < nresp; i++) { + password = packet_get_string(&len); + + /* replace newline with null */ + if ((ptr = strchr(password, '\n'))) + *ptr = '\0'; + + strlcpy(context_krb5.responses[context_krb5.num_received], password, KRB5_MSG_SIZE); + + xfree(password); + context_krb5.num_received++; + + if (context_krb5.num_received > KRB5_MAX_MSGS) + fatal("%s: Received > %d responses ", __func__, KRB5_MAX_MSGS); + + if (context_krb5.num_received == context_krb5.num_expected) + context_krb5.finished = 1; + } + + packet_check_eom(); + } void diff -cr openssh-3.5p1/auth2-kbdint.c openssh-3.5p1-krb5-kbdint/auth2-kbdint.c *** openssh-3.5p1/auth2-kbdint.c Thu Jun 6 16:27:56 2002 --- openssh-3.5p1-krb5-kbdint/auth2-kbdint.c Tue Apr 22 08:14:30 2003 *************** *** 34,39 **** --- 34,41 ---- /* import */ extern ServerOptions options; + int auth_krb5_password_via_kbd_int(Authctxt *authctxt); + static int userauth_kbdint(Authctxt *authctxt) { *************** *** 53,58 **** --- 55,66 ---- if (authenticated == 0 && options.pam_authentication_via_kbd_int) authenticated = auth2_pam(authctxt); #endif + + #ifdef KRB5 + if (authenticated == 0 && options.kerberos_authentication) + authenticated = auth_krb5_password_via_kbd_int(authctxt); + #endif + xfree(devs); xfree(lang); #ifdef HAVE_CYGWIN