int do_krb5_login (int infd, struct auth_data *ap, const char **err_msg) { krb5_auth_context auth_ctx = NULL; krb5_error_code status; krb5_data inbuf; krb5_data version; krb5_authenticator *authenticator; krb5_rcache rcache; krb5_keyblock *key; krb5_ticket *ticket; struct sockaddr_in laddr; int len; struct passwd *pwd; char *name; if (status = krb5_init_context (&ap->context)) { syslog (LOG_ERR, "Error initializing krb5: %s", error_message (status)); return status; } if ((status = krb5_auth_con_init (ap->context, &auth_ctx)) || (status = krb5_auth_con_genaddrs (ap->context, auth_ctx, infd, KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR)) || (status = krb5_auth_con_getrcache (ap->context, auth_ctx, &rcache))) return status; if (!rcache) { krb5_principal server; status = krb5_sname_to_principal (ap->context, 0, 0, KRB5_NT_SRV_HST, &server); if (status) return status; status = krb5_get_server_rcache (ap->context, krb5_princ_component (ap->context, server, 0), &rcache); krb5_free_principal (ap->context, server); if (status) return status; status = krb5_auth_con_setrcache (ap->context, auth_ctx, rcache); if (status) return status; } len = sizeof (laddr); if (getsockname (infd, (struct sockaddr *) &laddr, &len)) return errno; status = krb5_recvauth (ap->context, &auth_ctx, &infd, NULL, 0, 0, ap->keytab, &ticket); if (status) return status; if ((status = krb5_auth_con_getauthenticator (ap->context, auth_ctx, &authenticator))) return status; getstr (infd, &ap->lusername, NULL); getstr (infd, &ap->term, "TERM="); pwd = getpwnam (ap->lusername); if (pwd == NULL) { *err_msg = "getpwnam failed"; syslog (LOG_ERR, "getpwnam failed: %m"); return 1; } getstr (infd, &ap->rusername, NULL); if ((status = krb5_copy_principal (ap->context, ticket->enc_part2->client, &ap->client))) return status; /*OK:: */ if (ap->client && !krb5_kuserok (ap->context, ap->client, ap->lusername)) return 1; krb5_unparse_name (ap->context, ap->client, &name); syslog (LOG_INFO | LOG_AUTH, "%sKerberos V login from %s on %s\n", (pwd->pw_uid == 0) ? "ROOT " : "", name, ap->hostname); free (name); return 0; }
/* Given krb5 service name in KSSL_CTX *kssl_ctx (typically "kssl"), ** and krb5 AP_REQ message & message length, ** Return Kerberos session key and client principle ** to SSL Server in KSSL_CTX *kssl_ctx. ** ** 19990702 VRS Started. */ krb5_error_code kssl_sget_tkt( /* UPDATE */ KSSL_CTX *kssl_ctx, /* IN */ krb5_data *indata, /* OUT */ krb5_ticket_times *ttimes, /* OUT */ KSSL_ERR *kssl_err ) { krb5_error_code krb5rc = KRB5KRB_ERR_GENERIC; static krb5_context krb5context = NULL; static krb5_auth_context krb5auth_context = NULL; krb5_ticket *krb5ticket = NULL; KRB5_TKTBODY *asn1ticket = NULL; const unsigned char *p; krb5_keytab krb5keytab = NULL; krb5_keytab_entry kt_entry; krb5_principal krb5server; krb5_rcache rcache = NULL; kssl_err_set(kssl_err, 0, ""); if (!kssl_ctx) { kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT, "No kssl_ctx defined.\n"); goto err; } #ifdef KSSL_DEBUG printf("in kssl_sget_tkt(%s)\n", kstring(kssl_ctx->service_name)); #endif /* KSSL_DEBUG */ if (!krb5context && (krb5rc = krb5_init_context(&krb5context))) { kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT, "krb5_init_context() fails.\n"); goto err; } if (krb5auth_context && (krb5rc = krb5_auth_con_free(krb5context, krb5auth_context))) { kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT, "krb5_auth_con_free() fails.\n"); goto err; } else krb5auth_context = NULL; if (!krb5auth_context && (krb5rc = krb5_auth_con_init(krb5context, &krb5auth_context))) { kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT, "krb5_auth_con_init() fails.\n"); goto err; } if ((krb5rc = krb5_auth_con_getrcache(krb5context, krb5auth_context, &rcache))) { kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT, "krb5_auth_con_getrcache() fails.\n"); goto err; } if ((krb5rc = krb5_sname_to_principal(krb5context, NULL, (kssl_ctx->service_name) ? kssl_ctx->service_name : KRB5SVC, KRB5_NT_SRV_HST, &krb5server)) != 0) { kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT, "krb5_sname_to_principal() fails.\n"); goto err; } if (rcache == NULL) { if ((krb5rc = krb5_get_server_rcache(krb5context, krb5_princ_component(krb5context, krb5server, 0), &rcache))) { kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT, "krb5_get_server_rcache() fails.\n"); goto err; } } if ((krb5rc = krb5_auth_con_setrcache(krb5context, krb5auth_context, rcache))) { kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT, "krb5_auth_con_setrcache() fails.\n"); goto err; } /* kssl_ctx->keytab_file == NULL ==> use Kerberos default */ if (kssl_ctx->keytab_file) { krb5rc = krb5_kt_resolve(krb5context, kssl_ctx->keytab_file, &krb5keytab); if (krb5rc) { kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT, "krb5_kt_resolve() fails.\n"); goto err; } } else { krb5rc = krb5_kt_default(krb5context, &krb5keytab); if (krb5rc) { kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT, "krb5_kt_default() fails.\n"); goto err; } } /* Actual Kerberos5 krb5_recvauth() has initial conversation here ** o check KRB5_SENDAUTH_BADAUTHVERS ** unless KRB5_RECVAUTH_SKIP_VERSION ** o check KRB5_SENDAUTH_BADAPPLVERS ** o send "0" msg if all OK */ /* 20010411 was using AP_REQ instead of true KerberosWrapper ** ** if ((krb5rc = krb5_rd_req(krb5context, &krb5auth_context, ** &krb5in_data, krb5server, krb5keytab, ** &ap_option, &krb5ticket)) != 0) { Error } */ p = (unsigned char *)indata->data; if ((asn1ticket = (KRB5_TKTBODY *) d2i_KRB5_TICKET(NULL, &p, (long)indata->length)) == NULL) { (void) snprintf(kssl_err->text, KSSL_ERR_MAX, "d2i_KRB5_TICKET() ASN.1 decode failure.\n"); kssl_err->reason = SSL_R_KRB5_S_RD_REQ; goto err; } /* Was: krb5rc = krb5_decode_ticket(krb5in_data,&krb5ticket)) != 0) */ if ((krb5rc = kssl_TKT2tkt(krb5context, asn1ticket, &krb5ticket, kssl_err)) != 0) { (void) snprintf(kssl_err->text, KSSL_ERR_MAX, "Error converting ASN.1 ticket to krb5_ticket.\n"); kssl_err->reason = SSL_R_KRB5_S_RD_REQ; goto err; } if (!krb5_principal_compare(krb5context, krb5server, krb5ticket->server)) { krb5rc = KRB5_PRINC_NOMATCH; (void) snprintf(kssl_err->text, KSSL_ERR_MAX, "server principal != ticket principal\n"); kssl_err->reason = SSL_R_KRB5_S_RD_REQ; goto err; } if ((krb5rc = krb5_kt_get_entry(krb5context, krb5keytab, krb5ticket->server, krb5ticket->enc_part.kvno, krb5ticket->enc_part.enctype, &kt_entry)) != 0) { (void) snprintf(kssl_err->text, KSSL_ERR_MAX, "krb5_kt_get_entry() fails with %x.\n", krb5rc); kssl_err->reason = SSL_R_KRB5_S_RD_REQ; goto err; } if ((krb5rc = krb5_decrypt_tkt_part(krb5context, &kt_entry.key, krb5ticket)) != 0) { (void) snprintf(kssl_err->text, KSSL_ERR_MAX, "krb5_decrypt_tkt_part() failed.\n"); kssl_err->reason = SSL_R_KRB5_S_RD_REQ; goto err; } else { krb5_kt_free_entry(krb5context, &kt_entry); #ifdef KSSL_DEBUG { int i; krb5_address **paddr = krb5ticket->enc_part2->caddrs; printf("Decrypted ticket fields:\n"); printf("\tflags: %X, transit-type: %X", krb5ticket->enc_part2->flags, krb5ticket->enc_part2->transited.tr_type); print_krb5_data("\ttransit-data: ", &(krb5ticket->enc_part2->transited.tr_contents)); printf("\tcaddrs: %p, authdata: %p\n", krb5ticket->enc_part2->caddrs, krb5ticket->enc_part2->authorization_data); if (paddr) { printf("\tcaddrs:\n"); for (i = 0; paddr[i] != NULL; i++) { krb5_data d; d.length = paddr[i]->length; d.data = paddr[i]->contents; print_krb5_data("\t\tIP: ", &d); } } printf("\tstart/auth/end times: %d / %d / %d\n", krb5ticket->enc_part2->times.starttime, krb5ticket->enc_part2->times.authtime, krb5ticket->enc_part2->times.endtime); } #endif /* KSSL_DEBUG */ } krb5rc = KRB5_NO_TKT_SUPPLIED; if (!krb5ticket || !krb5ticket->enc_part2 || !krb5ticket->enc_part2->client || !krb5ticket->enc_part2->client->data || !krb5ticket->enc_part2->session) { kssl_err_set(kssl_err, SSL_R_KRB5_S_BAD_TICKET, "bad ticket from krb5_rd_req.\n"); } else if (kssl_ctx_setprinc(kssl_ctx, KSSL_CLIENT, &krb5ticket->enc_part2->client->realm, krb5ticket->enc_part2->client->data, krb5ticket->enc_part2->client->length)) { kssl_err_set(kssl_err, SSL_R_KRB5_S_BAD_TICKET, "kssl_ctx_setprinc() fails.\n"); } else if (kssl_ctx_setkey(kssl_ctx, krb5ticket->enc_part2->session)) { kssl_err_set(kssl_err, SSL_R_KRB5_S_BAD_TICKET, "kssl_ctx_setkey() fails.\n"); } else if (krb5ticket->enc_part2->flags & TKT_FLG_INVALID) { krb5rc = KRB5KRB_AP_ERR_TKT_INVALID; kssl_err_set(kssl_err, SSL_R_KRB5_S_BAD_TICKET, "invalid ticket from krb5_rd_req.\n"); } else krb5rc = 0; kssl_ctx->enctype = krb5ticket->enc_part.enctype; ttimes->authtime = krb5ticket->enc_part2->times.authtime; ttimes->starttime = krb5ticket->enc_part2->times.starttime; ttimes->endtime = krb5ticket->enc_part2->times.endtime; ttimes->renew_till = krb5ticket->enc_part2->times.renew_till; err: #ifdef KSSL_DEBUG kssl_ctx_show(kssl_ctx); #endif /* KSSL_DEBUG */ if (asn1ticket) KRB5_TICKET_free((KRB5_TICKET *) asn1ticket); if (krb5keytab) krb5_kt_close(krb5context, krb5keytab); if (krb5ticket) krb5_free_ticket(krb5context, krb5ticket); if (krb5server) krb5_free_principal(krb5context, krb5server); return (krb5rc); }
static krb5_error_code recvauth(int f, krb5_context krb_context, unsigned int *valid_checksum, krb5_ticket **ticket, int *auth_type, krb5_principal *client, int encr_flag, krb5_keytab keytab) { krb5_error_code status = 0; krb5_auth_context auth_context = NULL; krb5_rcache rcache; krb5_authenticator *authenticator; krb5_data inbuf; krb5_data auth_version; *valid_checksum = 0; if ((status = krb5_auth_con_init(krb_context, &auth_context))) return (status); /* Only need remote address for rd_cred() to verify client */ if ((status = krb5_auth_con_genaddrs(krb_context, auth_context, f, KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR))) return (status); status = krb5_auth_con_getrcache(krb_context, auth_context, &rcache); if (status) return (status); if (!rcache) { krb5_principal server; status = krb5_sname_to_principal(krb_context, 0, 0, KRB5_NT_SRV_HST, &server); if (status) return (status); status = krb5_get_server_rcache(krb_context, krb5_princ_component(krb_context, server, 0), &rcache); krb5_free_principal(krb_context, server); if (status) return (status); status = krb5_auth_con_setrcache(krb_context, auth_context, rcache); if (status) return (status); } if ((status = krb5_compat_recvauth(krb_context, &auth_context, &f, NULL, /* Specify daemon principal */ 0, /* no flags */ keytab, /* NULL to use v5srvtab */ ticket, /* return ticket */ auth_type, /* authentication system */ &auth_version))) { if (*auth_type == KRB5_RECVAUTH_V5) { /* * clean up before exiting */ getstr(f, rusername, sizeof (rusername), "remuser"); getstr(f, lusername, sizeof (lusername), "locuser"); getstr(f, term, sizeof (term), "Terminal type"); } return (status); } getstr(f, lusername, sizeof (lusername), "locuser"); getstr(f, term, sizeof (term), "Terminal type"); kcmd_protocol = KCMD_UNKNOWN_PROTOCOL; if (auth_version.length != 9 || auth_version.data == NULL) { syslog(LOG_ERR, "Bad application protocol version length in " "KRB5 exchange, exiting"); fatal(f, "Bad application version length, exiting."); } /* * Determine which Kerberos CMD protocol was used. */ if (strncmp(auth_version.data, "KCMDV0.1", 9) == 0) { kcmd_protocol = KCMD_OLD_PROTOCOL; } else if (strncmp(auth_version.data, "KCMDV0.2", 9) == 0) { kcmd_protocol = KCMD_NEW_PROTOCOL; } else { syslog(LOG_ERR, "Unrecognized KCMD protocol (%s), exiting", (char *)auth_version.data); fatal(f, "Unrecognized KCMD protocol, exiting"); } if ((*auth_type == KRB5_RECVAUTH_V5) && chksum_flag && kcmd_protocol == KCMD_OLD_PROTOCOL) { if ((status = krb5_auth_con_getauthenticator(krb_context, auth_context, &authenticator))) return (status); if (authenticator->checksum) { struct sockaddr_storage adr; int adr_length = sizeof (adr); int buflen; krb5_data input; krb5_keyblock key; char *chksumbuf; /* * Define the lenght of the chksum buffer. * chksum string = "[portnum]:termstr:username" * The extra 32 is to hold a integer string for * the portnumber. */ buflen = strlen(term) + strlen(lusername) + 32; chksumbuf = (char *)malloc(buflen); if (chksumbuf == 0) { krb5_free_authenticator(krb_context, authenticator); fatal(f, "Out of memory error"); } if (getsockname(f, (struct sockaddr *)&adr, &adr_length) != 0) { krb5_free_authenticator(krb_context, authenticator); fatal(f, "getsockname error"); } (void) snprintf(chksumbuf, buflen, "%u:%s%s", ntohs(SOCK_PORT(adr)), term, lusername); input.data = chksumbuf; input.length = strlen(chksumbuf); key.contents = (*ticket)->enc_part2->session->contents; key.length = (*ticket)->enc_part2->session->length; status = krb5_c_verify_checksum(krb_context, &key, 0, &input, authenticator->checksum, valid_checksum); if (status == 0 && *valid_checksum == 0) status = KRB5KRB_AP_ERR_BAD_INTEGRITY; if (chksumbuf) krb5_xfree(chksumbuf); if (status) { krb5_free_authenticator(krb_context, authenticator); return (status); } } krb5_free_authenticator(krb_context, authenticator); } if ((status = krb5_copy_principal(krb_context, (*ticket)->enc_part2->client, client))) return (status); /* Get the Unix username of the remote user */ getstr(f, rusername, sizeof (rusername), "remuser"); /* Get the Kerberos principal name string of the remote user */ if ((status = krb5_unparse_name(krb_context, *client, &krusername))) return (status); #ifdef DEBUG syslog(LOG_DEBUG | LOG_AUTH, "rlogind: got krb5 credentials for %s", (krusername != NULL ? krusername : "******")); #endif if (encr_flag) { status = krb5_auth_con_getremotesubkey(krb_context, auth_context, &session_key); if (status) { syslog(LOG_ERR, "Error getting KRB5 session " "subkey, exiting"); fatal(f, "Error getting KRB5 session subkey, exiting"); } /* * The "new" protocol requires that a subkey be sent. */ if (session_key == NULL && kcmd_protocol == KCMD_NEW_PROTOCOL) { syslog(LOG_ERR, "No KRB5 session subkey sent, exiting"); fatal(f, "No KRB5 session subkey sent, exiting"); } /* * The "old" protocol does not permit an authenticator subkey. * The key is taken from the ticket instead (see below). */ if (session_key != NULL && kcmd_protocol == KCMD_OLD_PROTOCOL) { syslog(LOG_ERR, "KRB5 session subkey not permitted " "with old KCMD protocol, exiting"); fatal(f, "KRB5 session subkey not permitted " "with old KCMD protocol, exiting"); } /* * If no key at this point, use the session key from * the ticket. */ if (session_key == NULL) { /* * Save the session key so we can configure the crypto * module later. */ status = krb5_copy_keyblock(krb_context, (*ticket)->enc_part2->session, &session_key); if (status) { syslog(LOG_ERR, "krb5_copy_keyblock failed"); fatal(f, "krb5_copy_keyblock failed"); } } /* * If session key still cannot be found, we must * exit because encryption is required here * when encr_flag (-x) is set. */ if (session_key == NULL) { syslog(LOG_ERR, "Could not find an encryption key," "exiting"); fatal(f, "Encryption required but key not found, " "exiting"); } } /* * Use krb5_read_message to read the principal stuff. */ if ((status = krb5_read_message(krb_context, (krb5_pointer)&f, &inbuf))) fatal(f, "Error reading krb5 message"); if (inbuf.length) { /* Forwarding being done, read creds */ krb5_creds **creds = NULL; if (status = krb5_rd_cred(krb_context, auth_context, &inbuf, &creds, NULL)) { if (rcache) (void) krb5_rc_close(krb_context, rcache); krb5_free_creds(krb_context, *creds); fatal(f, "Can't get forwarded credentials"); } /* Store the forwarded creds in the ccache */ if (status = store_forw_creds(krb_context, creds, *ticket, lusername, &ccache)) { if (rcache) (void) krb5_rc_close(krb_context, rcache); krb5_free_creds(krb_context, *creds); fatal(f, "Can't store forwarded credentials"); } krb5_free_creds(krb_context, *creds); } if (rcache) (void) krb5_rc_close(krb_context, rcache); return (status); }
int kerberos5_is_auth (TN_Authenticator * ap, unsigned char *data, int cnt, char *errbuf, int errbuflen) { int r = 0; krb5_keytab keytabid = 0; krb5_authenticator *authenticator; char *name; krb5_data outbuf; krb5_keyblock *newkey = NULL; krb5_principal server; # ifdef ENCRYPTION Session_Key skey; # endif auth.data = (char *) data; auth.length = cnt; if (!r && !auth_context) r = krb5_auth_con_init (telnet_context, &auth_context); if (!r) { krb5_rcache rcache; r = krb5_auth_con_getrcache (telnet_context, auth_context, &rcache); if (!r && !rcache) { r = krb5_sname_to_principal (telnet_context, 0, 0, KRB5_NT_SRV_HST, &server); if (!r) { r = krb5_get_server_rcache (telnet_context, krb5_princ_component (telnet_context, server, 0), &rcache); krb5_free_principal (telnet_context, server); } } if (!r) r = krb5_auth_con_setrcache (telnet_context, auth_context, rcache); } if (!r && telnet_srvtab) r = krb5_kt_resolve (telnet_context, telnet_srvtab, &keytabid); if (!r) r = krb5_rd_req (telnet_context, &auth_context, &auth, NULL, keytabid, NULL, &ticket); if (r) { snprintf (errbuf, errbuflen, "krb5_rd_req failed: %s", error_message (r)); return r; } /* 256 bytes should be much larger than any reasonable first component of a service name especially since the default is of length 4. */ if (krb5_princ_component (telnet_context, ticket->server, 0)->length < 256) { char princ[256]; strncpy (princ, krb5_princ_component (telnet_context, ticket->server, 0)->data, krb5_princ_component (telnet_context, ticket->server, 0)->length); princ[krb5_princ_component (telnet_context, ticket->server, 0)-> length] = '\0'; if (strcmp ("host", princ)) { snprintf (errbuf, errbuflen, "incorrect service name: \"%s\" != \"host\"", princ); return 1; } } else { strncpy (errbuf, "service name too long", errbuflen); return 1; } r = krb5_auth_con_getauthenticator (telnet_context, auth_context, &authenticator); if (r) { snprintf (errbuf, errbuflen, "krb5_auth_con_getauthenticator failed: %s", error_message (r)); return 1; } # ifdef AUTH_ENCRYPT_MASK if ((ap->way & AUTH_ENCRYPT_MASK) == AUTH_ENCRYPT_ON && !authenticator->checksum) { snprintf (errbuf, errbuflen, "authenticator is missing required checksum"); return 1; } # endif if (authenticator->checksum) { char type_check[2]; krb5_checksum *cksum = authenticator->checksum; krb5_keyblock *key; type_check[0] = ap->type; type_check[1] = ap->way; r = krb5_auth_con_getkey (telnet_context, auth_context, &key); if (r) { snprintf (errbuf, errbuflen, "krb5_auth_con_getkey failed: %s", error_message (r)); return 1; } r = krb5_verify_checksum (telnet_context, cksum->checksum_type, cksum, &type_check, 2, key->contents, key->length); if (r) { snprintf (errbuf, errbuflen, "checksum verification failed: %s", error_message (r)); return 1; } krb5_free_keyblock (telnet_context, key); } krb5_free_authenticator (telnet_context, authenticator); if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) { if ((r = krb5_mk_rep (telnet_context, auth_context, &outbuf))) { snprintf (errbuf, errbuflen, "Make reply failed: %s", error_message (r)); return 1; } Data (ap, KRB_RESPONSE, outbuf.data, outbuf.length); } if (krb5_unparse_name (telnet_context, ticket->enc_part2->client, &name)) name = 0; Data (ap, KRB_ACCEPT, name, name ? -1 : 0); DEBUG (("telnetd: Kerberos5 identifies him as ``%s''\r\n", name ? name : "")); auth_finished (ap, AUTH_USER); free (name); krb5_auth_con_getremotesubkey (telnet_context, auth_context, &newkey); if (session_key) { krb5_free_keyblock (telnet_context, session_key); session_key = 0; } if (newkey) { krb5_copy_keyblock (telnet_context, newkey, &session_key); krb5_free_keyblock (telnet_context, newkey); } else { krb5_copy_keyblock (telnet_context, ticket->enc_part2->session, &session_key); } telnet_encrypt_key (&skey); return 0; }