static krb5_error_code fast_armor_ap_request(krb5_context context, struct krb5int_fast_request_state *state, krb5_ccache ccache, krb5_principal target_principal) { krb5_error_code retval = 0; krb5_creds creds, *out_creds = NULL; krb5_auth_context authcontext = NULL; krb5_data encoded_authenticator; krb5_fast_armor *armor = NULL; krb5_keyblock *subkey = NULL, *armor_key = NULL; encoded_authenticator.data = NULL; memset(&creds, 0, sizeof(creds)); creds.server = target_principal; retval = krb5_cc_get_principal(context, ccache, &creds.client); if (retval == 0) retval = krb5_get_credentials(context, 0, ccache, &creds, &out_creds); if (retval == 0) { TRACE_FAST_ARMOR_CCACHE_KEY(context, &out_creds->keyblock); retval = krb5_mk_req_extended(context, &authcontext, AP_OPTS_USE_SUBKEY, NULL /*data*/, out_creds, &encoded_authenticator); } if (retval == 0) retval = krb5_auth_con_getsendsubkey(context, authcontext, &subkey); if (retval == 0) retval = krb5_c_fx_cf2_simple(context, subkey, "subkeyarmor", &out_creds->keyblock, "ticketarmor", &armor_key); if (retval == 0) { TRACE_FAST_ARMOR_KEY(context, armor_key); armor = calloc(1, sizeof(krb5_fast_armor)); if (armor == NULL) retval = ENOMEM; } if (retval == 0) { armor->armor_type = KRB5_FAST_ARMOR_AP_REQUEST; armor->armor_value = encoded_authenticator; encoded_authenticator.data = NULL; encoded_authenticator.length = 0; state->armor = armor; armor = NULL; state->armor_key = armor_key; armor_key = NULL; } krb5_free_keyblock(context, armor_key); krb5_free_keyblock(context, subkey); if (out_creds) krb5_free_creds(context, out_creds); /* target_principal is owned by caller. */ creds.server = NULL; krb5_free_cred_contents(context, &creds); if (encoded_authenticator.data) krb5_free_data_contents(context, &encoded_authenticator); krb5_auth_con_free(context, authcontext); return retval; }
/* * @Description: Make a TGS_REQ * @Param: input context, principal, time expire, kdc_option * @return: 0 if success, otherwise -1 * */ int ads_krb5_mk_req(/*krb5_context context, krb5_auth_context *auth_context, const krb5_flags ap_req_options, const char *principal, krb5_ccache ccache, krb5_data *outbuf, time_t *expire_time, const char *impersonate_princ_s*/) { /*get credentials */ smb_krb5_get_credentials(); /* make request*/ krb5_mk_req_extended(); }
krb5_error_code KRB5_LIB_FUNCTION krb5_mk_req_exact(krb5_context context, krb5_auth_context *auth_context, const krb5_flags ap_req_options, const krb5_principal server, krb5_data *in_data, krb5_ccache ccache, krb5_data *outbuf) { krb5_error_code ret; krb5_creds this_cred, *cred; memset(&this_cred, 0, sizeof(this_cred)); ret = krb5_cc_get_principal(context, ccache, &this_cred.client); if(ret) return ret; ret = krb5_copy_principal (context, server, &this_cred.server); if (ret) { krb5_free_cred_contents (context, &this_cred); return ret; } this_cred.times.endtime = 0; if (auth_context && *auth_context && (*auth_context)->keytype) this_cred.session.keytype = (*auth_context)->keytype; ret = krb5_get_credentials (context, 0, ccache, &this_cred, &cred); krb5_free_cred_contents(context, &this_cred); if (ret) return ret; ret = krb5_mk_req_extended (context, auth_context, ap_req_options, in_data, cred, outbuf); krb5_free_creds(context, cred); return ret; }
krb5_error_code KRB5_CALLCONV krb5_mk_req(krb5_context context, krb5_auth_context *auth_context, krb5_flags ap_req_options, char *service, char *hostname, krb5_data *in_data, krb5_ccache ccache, krb5_data *outbuf) { krb5_error_code retval; krb5_principal server; krb5_creds * credsp; krb5_creds creds; retval = krb5_sname_to_principal(context, hostname, service, KRB5_NT_SRV_HST, &server); if (retval) return retval; /* obtain ticket & session key */ memset((char *)&creds, 0, sizeof(creds)); if ((retval = krb5_copy_principal(context, server, &creds.server))) goto cleanup_princ; if ((retval = krb5_cc_get_principal(context, ccache, &creds.client)) != 0) goto cleanup_creds; if ((retval = krb5_get_credentials(context, 0, ccache, &creds, &credsp)) != 0) goto cleanup_creds; retval = krb5_mk_req_extended(context, auth_context, ap_req_options, in_data, credsp, outbuf); krb5_free_creds(context, credsp); cleanup_creds: krb5_free_cred_contents(context, &creds); cleanup_princ: krb5_free_principal(context, server); return retval; }
static krb5_error_code chgpw_prexmit(krb5_context context, int proto, void *ctx, rk_socket_t fd, krb5_data *data) { struct request *request = ctx; krb5_data ap_req_data, krb_priv_data, passwd_data; krb5_storage *sp = NULL; krb5_error_code ret; krb5_ssize_t slen; size_t len; krb5_data_zero(&ap_req_data); krb5_data_zero(&krb_priv_data); ret = krb5_auth_con_genaddrs(context, request->ac, fd, KRB5_AUTH_CONTEXT_GENERATE_LOCAL_ADDR); if (ret) goto out; ret = krb5_mk_req_extended(context, &request->ac, AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY, NULL, request->creds, &ap_req_data); if (ret) goto out; passwd_data.data = rk_UNCONST(request->password); passwd_data.length = strlen(request->password); ret = krb5_mk_priv(context, request->ac, &passwd_data, &krb_priv_data, NULL); if (ret) goto out; sp = krb5_storage_emem(); if (sp == NULL) { ret = ENOMEM; goto out; } len = 6 + ap_req_data.length + krb_priv_data.length; ret = krb5_store_uint16(sp, len); if (ret) goto out; ret = krb5_store_uint16(sp, 1); if (ret) goto out; ret = krb5_store_uint16(sp, ap_req_data.length); if (ret) goto out; slen = krb5_storage_write(sp, ap_req_data.data, ap_req_data.length); if (slen != ap_req_data.length) { ret = EINVAL; goto out; } slen = krb5_storage_write(sp, krb_priv_data.data, krb_priv_data.length); if (slen != krb_priv_data.length) { ret = EINVAL; goto out; } ret = krb5_storage_to_data(sp, data); out: if (ret) _krb5_debugx(context, 10, "chgpw_prexmit failed with: %d", ret); if (sp) krb5_storage_free(sp); krb5_data_free(&krb_priv_data); krb5_data_free(&ap_req_data); return ret; }
krb5_error_code KRB5_CALLCONV krb5_sendauth(krb5_context context, krb5_auth_context *auth_context, krb5_pointer fd, char *appl_version, krb5_principal client, krb5_principal server, krb5_flags ap_req_options, krb5_data *in_data, krb5_creds *in_creds, krb5_ccache ccache, krb5_error **error, krb5_ap_rep_enc_part **rep_result, krb5_creds **out_creds) { krb5_octet result; krb5_creds creds; krb5_creds * credsp = NULL; krb5_creds * credspout = NULL; krb5_error_code retval = 0; krb5_data inbuf, outbuf; int len; krb5_ccache use_ccache = 0; if (error) *error = 0; /* * First, send over the length of the sendauth version string; * then, we send over the sendauth version. Next, we send * over the length of the application version strings followed * by the string itself. */ outbuf.length = strlen(sendauth_version) + 1; outbuf.data = (char *) sendauth_version; if ((retval = krb5_write_message(context, fd, &outbuf))) return(retval); outbuf.length = strlen(appl_version) + 1; outbuf.data = appl_version; if ((retval = krb5_write_message(context, fd, &outbuf))) return(retval); /* * Now, read back a byte: 0 means no error, 1 means bad sendauth * version, 2 means bad application version */ len = krb5_net_read(context, *((int *) fd), (char *)&result, 1); if (len != 1) return((len < 0) ? errno : ECONNABORTED); if (result == 1) return(KRB5_SENDAUTH_BADAUTHVERS); else if (result == 2) return(KRB5_SENDAUTH_BADAPPLVERS); else if (result != 0) return(KRB5_SENDAUTH_BADRESPONSE); /* * We're finished with the initial negotiations; let's get and * send over the authentication header. (The AP_REQ message) */ /* * If no credentials were provided, try getting it from the * credentials cache. */ memset((char *)&creds, 0, sizeof(creds)); /* * See if we need to access the credentials cache */ if (!in_creds || !in_creds->ticket.length) { if (ccache) use_ccache = ccache; /* Solaris Kerberos */ else if ((retval = krb5int_cc_default(context, &use_ccache)) != 0) goto error_return; } if (!in_creds) { if ((retval = krb5_copy_principal(context, server, &creds.server))) goto error_return; if (client) retval = krb5_copy_principal(context, client, &creds.client); else retval = krb5_cc_get_principal(context, use_ccache, &creds.client); if (retval) { krb5_free_principal(context, creds.server); goto error_return; } /* creds.times.endtime = 0; -- memset 0 takes care of this zero means "as long as possible" */ /* creds.keyblock.enctype = 0; -- as well as this. zero means no session enctype preference */ in_creds = &creds; } if (!in_creds->ticket.length) { /* Solaris Kerberos */ if ((retval = krb5_get_credentials(context, 0, use_ccache, in_creds, &credsp)) != 0) goto error_return; credspout = credsp; } else { credsp = in_creds; } if (ap_req_options & AP_OPTS_USE_SUBKEY) { /* Provide some more fodder for random number code. This isn't strong cryptographically; the point here is not to guarantee randomness, but to make it less likely that multiple sessions could pick the same subkey. */ char rnd_data[1024]; GETPEERNAME_ARG3_TYPE len2; krb5_data d; d.length = sizeof (rnd_data); d.data = rnd_data; len2 = sizeof (rnd_data); if (getpeername (*(int*)fd, (GETPEERNAME_ARG2_TYPE *) rnd_data, &len2) == 0) { d.length = len2; /* Solaris Kerberos */ (void) krb5_c_random_seed (context, &d); } len2 = sizeof (rnd_data); if (getsockname (*(int*)fd, (GETSOCKNAME_ARG2_TYPE *) rnd_data, &len2) == 0) { d.length = len2; /* Solaris Kerberos */ (void) krb5_c_random_seed (context, &d); } } /* Solaris Kerberos */ if ((retval = krb5_mk_req_extended(context, auth_context, ap_req_options, in_data, credsp, &outbuf)) != 0) goto error_return; /* * First write the length of the AP_REQ message, then write * the message itself. */ retval = krb5_write_message(context, fd, &outbuf); free(outbuf.data); if (retval) goto error_return; /* * Now, read back a message. If it was a null message (the * length was zero) then there was no error. If not, we the * authentication was rejected, and we need to return the * error structure. */ /* Solaris Kerberos */ if ((retval = krb5_read_message(context, fd, &inbuf)) != 0) goto error_return; if (inbuf.length) { if (error) { /* Solaris Kerberos */ if ((retval = krb5_rd_error(context, &inbuf, error)) != 0) { krb5_xfree(inbuf.data); goto error_return; } } retval = KRB5_SENDAUTH_REJECTED; krb5_xfree(inbuf.data); goto error_return; } /* * If we asked for mutual authentication, we should now get a * length field, followed by a AP_REP message */ if ((ap_req_options & AP_OPTS_MUTUAL_REQUIRED)) { krb5_ap_rep_enc_part *repl = 0; /* Solaris Kerberos */ if ((retval = krb5_read_message(context, fd, &inbuf)) != 0) goto error_return; /* Solaris Kerberos */ if ((retval = krb5_rd_rep(context, *auth_context, &inbuf, &repl)) != 0) { if (repl) krb5_free_ap_rep_enc_part(context, repl); krb5_xfree(inbuf.data); goto error_return; } krb5_xfree(inbuf.data); /* * If the user wants to look at the AP_REP message, * copy it for them. */ if (rep_result) *rep_result = repl; else krb5_free_ap_rep_enc_part(context, repl); } retval = 0; /* Normal return */ if (out_creds) { *out_creds = credsp; credspout = NULL; } error_return: krb5_free_cred_contents(context, &creds); if (credspout != NULL) krb5_free_creds(context, credspout); /* Solaris Kerberos */ if (!ccache && use_ccache) (void) krb5_cc_close(context, use_ccache); return(retval); }
static krb5_error_code setpw_send_request (krb5_context context, krb5_auth_context *auth_context, krb5_creds *creds, krb5_principal targprinc, int is_stream, int sock, char *passwd, const char *host) { krb5_error_code ret; krb5_data ap_req_data; krb5_data krb_priv_data; krb5_data pwd_data; ChangePasswdDataMS chpw; size_t len; u_char header[4 + 6]; u_char *p; struct iovec iov[3]; struct msghdr msghdr; krb5_data_zero (&ap_req_data); ret = krb5_mk_req_extended (context, auth_context, AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY, NULL, /* in_data */ creds, &ap_req_data); if (ret) return ret; chpw.newpasswd.length = strlen(passwd); chpw.newpasswd.data = passwd; if (targprinc) { chpw.targname = &targprinc->name; chpw.targrealm = &targprinc->realm; } else { chpw.targname = NULL; chpw.targrealm = NULL; } ASN1_MALLOC_ENCODE(ChangePasswdDataMS, pwd_data.data, pwd_data.length, &chpw, &len, ret); if (ret) { krb5_data_free (&ap_req_data); return ret; } if(pwd_data.length != len) krb5_abortx(context, "internal error in ASN.1 encoder"); ret = krb5_mk_priv (context, *auth_context, &pwd_data, &krb_priv_data, NULL); if (ret) goto out2; len = 6 + ap_req_data.length + krb_priv_data.length; p = header; if (is_stream) { _krb5_put_int(p, len, 4); p += 4; } *p++ = (len >> 8) & 0xFF; *p++ = (len >> 0) & 0xFF; *p++ = 0xff; *p++ = 0x80; *p++ = (ap_req_data.length >> 8) & 0xFF; *p++ = (ap_req_data.length >> 0) & 0xFF; memset(&msghdr, 0, sizeof(msghdr)); msghdr.msg_name = NULL; msghdr.msg_namelen = 0; msghdr.msg_iov = iov; msghdr.msg_iovlen = sizeof(iov)/sizeof(*iov); #if 0 msghdr.msg_control = NULL; msghdr.msg_controllen = 0; #endif iov[0].iov_base = (void*)header; if (is_stream) iov[0].iov_len = 10; else iov[0].iov_len = 6; iov[1].iov_base = ap_req_data.data; iov[1].iov_len = ap_req_data.length; iov[2].iov_base = krb_priv_data.data; iov[2].iov_len = krb_priv_data.length; if (sendmsg (sock, &msghdr, 0) < 0) { ret = errno; krb5_set_error_string(context, "sendmsg %s: %s", host, strerror(ret)); } krb5_data_free (&krb_priv_data); out2: krb5_data_free (&ap_req_data); krb5_data_free (&pwd_data); return ret; }
static ADS_STATUS do_krb5_kpasswd_request(krb5_context context, const char *kdc_host, uint16 pversion, krb5_creds *credsp, const char *princ, const char *newpw) { krb5_auth_context auth_context = NULL; krb5_data ap_req, chpw_req, chpw_rep; int ret, sock; socklen_t addr_len; struct sockaddr_storage remote_addr, local_addr; struct sockaddr_storage addr; krb5_address local_kaddr, remote_kaddr; bool use_tcp = False; if (!interpret_string_addr(&addr, kdc_host, 0)) { } ret = krb5_mk_req_extended(context, &auth_context, AP_OPTS_USE_SUBKEY, NULL, credsp, &ap_req); if (ret) { DEBUG(1,("krb5_mk_req_extended failed (%s)\n", error_message(ret))); return ADS_ERROR_KRB5(ret); } do { if (!use_tcp) { sock = open_udp_socket(kdc_host, DEFAULT_KPASSWD_PORT); if (sock == -1) { int rc = errno; SAFE_FREE(ap_req.data); krb5_auth_con_free(context, auth_context); DEBUG(1,("failed to open kpasswd socket to %s " "(%s)\n", kdc_host, strerror(errno))); return ADS_ERROR_SYSTEM(rc); } } else { NTSTATUS status; status = open_socket_out(&addr, DEFAULT_KPASSWD_PORT, LONG_CONNECT_TIMEOUT, &sock); if (!NT_STATUS_IS_OK(status)) { SAFE_FREE(ap_req.data); krb5_auth_con_free(context, auth_context); DEBUG(1,("failed to open kpasswd socket to %s " "(%s)\n", kdc_host, nt_errstr(status))); return ADS_ERROR_NT(status); } } addr_len = sizeof(remote_addr); if (getpeername(sock, (struct sockaddr *)&remote_addr, &addr_len) != 0) { close(sock); SAFE_FREE(ap_req.data); krb5_auth_con_free(context, auth_context); DEBUG(1,("getpeername() failed (%s)\n", error_message(errno))); return ADS_ERROR_SYSTEM(errno); } addr_len = sizeof(local_addr); if (getsockname(sock, (struct sockaddr *)&local_addr, &addr_len) != 0) { close(sock); SAFE_FREE(ap_req.data); krb5_auth_con_free(context, auth_context); DEBUG(1,("getsockname() failed (%s)\n", error_message(errno))); return ADS_ERROR_SYSTEM(errno); } if (!setup_kaddr(&remote_kaddr, &remote_addr) || !setup_kaddr(&local_kaddr, &local_addr)) { DEBUG(1,("do_krb5_kpasswd_request: " "Failed to setup addresses.\n")); close(sock); SAFE_FREE(ap_req.data); krb5_auth_con_free(context, auth_context); errno = EINVAL; return ADS_ERROR_SYSTEM(EINVAL); } ret = krb5_auth_con_setaddrs(context, auth_context, &local_kaddr, NULL); if (ret) { close(sock); SAFE_FREE(ap_req.data); krb5_auth_con_free(context, auth_context); DEBUG(1,("krb5_auth_con_setaddrs failed (%s)\n", error_message(ret))); return ADS_ERROR_KRB5(ret); } ret = build_kpasswd_request(pversion, context, auth_context, &ap_req, princ, newpw, use_tcp, &chpw_req); if (ret) { close(sock); SAFE_FREE(ap_req.data); krb5_auth_con_free(context, auth_context); DEBUG(1,("build_setpw_request failed (%s)\n", error_message(ret))); return ADS_ERROR_KRB5(ret); } ret = write(sock, chpw_req.data, chpw_req.length); if (ret != chpw_req.length) { close(sock); SAFE_FREE(chpw_req.data); SAFE_FREE(ap_req.data); krb5_auth_con_free(context, auth_context); DEBUG(1,("send of chpw failed (%s)\n", strerror(errno))); return ADS_ERROR_SYSTEM(errno); } SAFE_FREE(chpw_req.data); chpw_rep.length = 1500; chpw_rep.data = (char *) SMB_MALLOC(chpw_rep.length); if (!chpw_rep.data) { close(sock); SAFE_FREE(ap_req.data); krb5_auth_con_free(context, auth_context); DEBUG(1,("send of chpw failed (%s)\n", strerror(errno))); errno = ENOMEM; return ADS_ERROR_SYSTEM(errno); } ret = read(sock, chpw_rep.data, chpw_rep.length); if (ret < 0) { close(sock); SAFE_FREE(chpw_rep.data); SAFE_FREE(ap_req.data); krb5_auth_con_free(context, auth_context); DEBUG(1,("recv of chpw reply failed (%s)\n", strerror(errno))); return ADS_ERROR_SYSTEM(errno); } close(sock); chpw_rep.length = ret; ret = krb5_auth_con_setaddrs(context, auth_context, NULL,&remote_kaddr); if (ret) { SAFE_FREE(chpw_rep.data); SAFE_FREE(ap_req.data); krb5_auth_con_free(context, auth_context); DEBUG(1,("krb5_auth_con_setaddrs on reply failed (%s)\n", error_message(ret))); return ADS_ERROR_KRB5(ret); } ret = parse_setpw_reply(context, use_tcp, auth_context, &chpw_rep); SAFE_FREE(chpw_rep.data); if (ret) { if (ret == KRB5KRB_ERR_RESPONSE_TOO_BIG && !use_tcp) { DEBUG(5, ("Trying setpw with TCP!!!\n")); use_tcp = True; continue; } SAFE_FREE(ap_req.data); krb5_auth_con_free(context, auth_context); DEBUG(1,("parse_setpw_reply failed (%s)\n", error_message(ret))); return ADS_ERROR_KRB5(ret); } SAFE_FREE(ap_req.data); krb5_auth_con_free(context, auth_context); } while ( ret ); return ADS_SUCCESS; }
/* ** The logic for setting and changing a password is mostly the same ** change_set_password handles both cases ** if set_password_for is NULL, then a password change is performed, ** otherwise, the password is set for the principal indicated in set_password_for */ static krb5_error_code change_set_password(krb5_context context, krb5_creds *creds, char *newpw, krb5_principal set_password_for, int *result_code, krb5_data *result_code_string, krb5_data *result_string) { krb5_data chpw_rep; krb5_address remote_kaddr; krb5_boolean use_tcp = 0; GETSOCKNAME_ARG3_TYPE addrlen; krb5_error_code code = 0; char *code_string; int local_result_code; struct sendto_callback_context callback_ctx; struct sendto_callback_info callback_info; struct sockaddr_storage remote_addr; struct serverlist sl = SERVERLIST_INIT; memset(&chpw_rep, 0, sizeof(krb5_data)); memset( &callback_ctx, 0, sizeof(struct sendto_callback_context)); callback_ctx.context = context; callback_ctx.newpw = newpw; callback_ctx.set_password_for = set_password_for; if ((code = krb5_auth_con_init(callback_ctx.context, &callback_ctx.auth_context))) goto cleanup; if ((code = krb5_mk_req_extended(callback_ctx.context, &callback_ctx.auth_context, AP_OPTS_USE_SUBKEY, NULL, creds, &callback_ctx.ap_req))) goto cleanup; callback_ctx.remote_seq_num = callback_ctx.auth_context->remote_seq_number; callback_ctx.local_seq_num = callback_ctx.auth_context->local_seq_number; do { int socktype = (use_tcp ? SOCK_STREAM : SOCK_DGRAM); code = locate_kpasswd(callback_ctx.context, &creds->server->realm, &sl, socktype); if (code) break; addrlen = sizeof(remote_addr); callback_info.data = &callback_ctx; callback_info.pfn_callback = kpasswd_sendto_msg_callback; callback_info.pfn_cleanup = kpasswd_sendto_msg_cleanup; krb5_free_data_contents(callback_ctx.context, &chpw_rep); code = k5_sendto(callback_ctx.context, NULL, &sl, socktype, 0, &callback_info, &chpw_rep, ss2sa(&remote_addr), &addrlen, NULL, NULL, NULL); if (code) { /* * Here we may want to switch to TCP on some errors. * right? */ break; } if (remote_addr.ss_family == AF_INET) { remote_kaddr.addrtype = ADDRTYPE_INET; remote_kaddr.length = sizeof(ss2sin(&remote_addr)->sin_addr); remote_kaddr.contents = (krb5_octet *) &ss2sin(&remote_addr)->sin_addr; } else if (remote_addr.ss_family == AF_INET6) { remote_kaddr.addrtype = ADDRTYPE_INET6; remote_kaddr.length = sizeof(ss2sin6(&remote_addr)->sin6_addr); remote_kaddr.contents = (krb5_octet *) &ss2sin6(&remote_addr)->sin6_addr; } else { break; } if ((code = krb5_auth_con_setaddrs(callback_ctx.context, callback_ctx.auth_context, NULL, &remote_kaddr))) break; code = krb5int_rd_chpw_rep(callback_ctx.context, callback_ctx.auth_context, &chpw_rep, &local_result_code, result_string); if (code) { if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG && !use_tcp) { k5_free_serverlist(&sl); use_tcp = 1; continue; } break; } if (result_code) *result_code = local_result_code; if (result_code_string) { code = krb5_chpw_result_code_string(callback_ctx.context, local_result_code, &code_string); if (code) goto cleanup; result_code_string->length = strlen(code_string); result_code_string->data = malloc(result_code_string->length); if (result_code_string->data == NULL) { code = ENOMEM; goto cleanup; } strncpy(result_code_string->data, code_string, result_code_string->length); } if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG && !use_tcp) { k5_free_serverlist(&sl); use_tcp = 1; } else { break; } } while (TRUE); cleanup: if (callback_ctx.auth_context != NULL) krb5_auth_con_free(callback_ctx.context, callback_ctx.auth_context); k5_free_serverlist(&sl); krb5_free_data_contents(callback_ctx.context, &callback_ctx.ap_req); krb5_free_data_contents(callback_ctx.context, &chpw_rep); return(code); }
int main(int argc, char **argv) { int log_level = 0; char *data_str; char *ap_req_str = NULL; unsigned char *data; size_t data_len; /* krb5 */ krb5_error_code ret; krb5_context context; krb5_auth_context auth_context; char *princ_str_tn = "kink/tn.example.com"; krb5_principal princ_tn; char *princ_str_nut = "kink/nut.example.com"; krb5_principal princ_nut; char *princ_str_krbtgt = "krbtgt/EXAMPLE.COM"; krb5_principal princ_krbtgt; krb5_ccache ccache; krb5_keytab keytab; krb5_creds creds_tgt; krb5_data ap_req; prog = (const char *) basename(argv[0]); if (prog == NULL) { fprintf(stderr, "basename: %s -- %s\n", strerror(errno), argv[0]); return(0); /* NOTREACHED */ } { int ch = 0; while ((ch = getopt(argc, argv, "dq:")) != -1) { switch (ch) { case 'd': log_level++; break; case 'q': ap_req_str = optarg; break; default: usage(); /* NOTREACHED */ break; } } argc -= optind; argv += optind; } if (!argc) { usage(); /* NOTREACHED */ } data_str = argv[0]; { printf("dbg: %s starts arg(%s)\n", prog, data_str); } { { /* stdout */ printf("std:data:%s\n", data_str); } data_len = strlen(data_str); data_len = data_len/2 + data_len%2; data = (unsigned char *)malloc(data_len); memset(data, 0, data_len); data = hex2data(data_str, data); } if (ap_req_str != NULL) { hex2krb5data(ap_req_str, &ap_req); if (log_level) { dump_krb5_data(&ap_req); } { /* stdout */ int i = 0; unsigned char *p; p = (unsigned char *)ap_req.data; printf("std:ap_req:"); for (i = 0; i < ap_req.length; i++) { printf("%02x", *p++); } printf("\n"); } } /* prepare krb5 context */ { /** init context */ ret = krb5_init_context(&context); if (ret != 0) { printf("ERR:krb5_init_context:%s\n", krb5_get_err_text(context, ret)); return(ret); } /** setup principals */ ret = krb5_parse_name(context, princ_str_tn, &princ_tn); if (ret != 0) { printf("ERR:krb5_parse_name:%s\n", krb5_get_err_text(context, ret)); return(ret); } ret = krb5_parse_name(context, princ_str_nut, &princ_nut); if (ret != 0) { printf("ERR:krb5_parse_name:%s\n", krb5_get_err_text(context, ret)); return(ret); } ret = krb5_parse_name(context, princ_str_krbtgt, &princ_krbtgt); if (ret != 0) { printf("ERR:krb5_parse_name:%s\n", krb5_get_err_text(context, ret)); return(ret); } /** prepare credential cache */ ret = krb5_cc_default(context, &ccache); if (ret != 0) { printf("ERR:krb5_cc_default:%s\n", krb5_get_err_text(context, ret)); return(ret); } /** prepare keytab */ /*ret = krb5_kt_resolve(context, "/usr/local/var/krb5kdc/kadm5.keytab", &keytab);*/ ret = krb5_kt_default(context, &keytab); if (ret != 0) { /* printf("ERR:krb5_kt_default:%s", krb5_get_err_text(context, ret)); */ printf("ERR:krb5_kt_resolve:%s", krb5_get_err_text(context, ret)); return(ret); } } /* get TGT */ { krb5_creds mcreds; memset(&mcreds, 0, sizeof(mcreds)); mcreds.client = princ_tn; mcreds.server = princ_krbtgt; ret = krb5_cc_retrieve_cred(context, ccache, 0, &mcreds, &creds_tgt); if (ret != 0) { printf("ERR:krb5_cc_retrieve_cred:%s\n", krb5_get_err_text(context, ret)); return(ret); } } /* prepare authentiation context */ { ret = krb5_auth_con_init(context, &auth_context); if (ret != 0) { printf("ERR:krb5_auth_con_init:%s\n", krb5_get_err_text(context, ret)); return(ret); } ret = krb5_auth_con_setflags(context, auth_context, KRB5_AUTH_CONTEXT_DO_SEQUENCE); if (ret != 0) { printf("ERR:krb5_auth_con_setflags:%s\n", krb5_get_err_text(context, ret)); return(ret); } /* if USE_SKEY */ /* ret = krb5_auth_con_setuserkey(context, auth_context, &creds_tgt.session); if (ret != 0) { printf("ERR:krb5_auth_con_setuseruserkey:%s\n", krb5_get_err_text(context, ret)); return(ret); } */ } /* set keyblock in auth_context */ if (ap_req_str != NULL) { krb5_ticket *ticket; krb5_flags ap_req_options; ap_req_options = AP_OPTS_MUTUAL_REQUIRED; ticket = NULL; ret = krb5_rd_req(context, &auth_context, &ap_req, NULL, keytab, &ap_req_options, &ticket); if (log_level) { printf("info: ticket.ticket.key is SKEYID_d\n"); /*dump_krb5_ticket(context, *ticket);*/ } if (log_level) { printf("ap_req_opt (%d)\n", ap_req_options); } if (ret != 0) { printf("ERR:krb5_rd_req:%s\n", krb5_get_err_text(context, ret)); return(ret); } if (log_level) { dump_krb5_keyblock(auth_context->keyblock); } krb5_free_ticket(context, ticket); } else { krb5_creds mcreds; krb5_creds *cred; krb5_creds cred_copy; memset(&mcreds, 0, sizeof(mcreds)); mcreds.client = princ_tn; mcreds.server = princ_nut; ret = krb5_get_credentials(context, KRB5_GC_CACHED, ccache, &mcreds, &cred); if (ret != 0) { printf("ERR:krb5_get_credentials:%s\n", krb5_get_err_text(context, ret)); return(ret); } /* mk_req_extends reallocate cred, so use a copy */ ret = krb5_copy_creds_contents(context, (const krb5_creds *)cred, &cred_copy); if (ret != 0) { printf("ERR:krb5_copy_creds_contents:%s\n", krb5_get_err_text(context, ret)); return(ret); } /* * If auth_con == NULL, one is allocated. * This is used later. (keyblock is used to decrypt AP_REP) */ ret = krb5_mk_req_extended(context, &auth_context, AP_OPTS_MUTUAL_REQUIRED, NULL /* in_data */, &cred_copy, &ap_req); if (ret != 0) { printf("ERR:krb5_mk_req_extended:%s\n", krb5_get_err_text(context, ret)); return(ret); } } /* create checksum */ { krb5_crypto crypto; krb5_checksum cksum; ret = krb5_crypto_init(context, auth_context->keyblock, auth_context->keyblock->keytype, &crypto); if (ret != 0) { printf("ERR:krb5_crypto_init:%s\n", krb5_get_err_text(context, ret)); return(ret); } if (0) { dump_krb5_keyblock(auth_context->keyblock); } ret = krb5_create_checksum(context, crypto, 40, 0 /* krb5_cksumtype type */, data, data_len, &cksum); if (ret != 0) { printf("ERR:krb5_create_checksum:%s\n", krb5_get_err_text(context, ret)); return(ret); } if (log_level) { dump_krb5_checksum(cksum); } { /* stdout */ int i = 0; unsigned char *p; p = (unsigned char *)cksum.checksum.data; printf("std:cksum:"); for (i = 0; i < cksum.checksum.length; i++) { printf("%02x", *p++); } printf("\n"); } krb5_crypto_destroy(context, crypto); } /* clenaup */ { /*free(data);*/ /*krb5_data_free(&ap_req);*/ krb5_free_cred_contents(context, &creds_tgt); ret = krb5_kt_close(context, keytab); if (ret != 0) { printf("ERR:krb5_kt_close:%s\n", krb5_get_err_text(context, ret)); return(ret); } ret = krb5_cc_close(context, ccache); if (ret != 0) { printf("ERR:krb5_cc_close:%s\n", krb5_get_err_text(context, ret)); return(ret); } krb5_free_principal(context, princ_krbtgt); krb5_free_principal(context, princ_nut); krb5_free_principal(context, princ_tn); krb5_free_context(context); } return(0); }
static krb5_error_code mod_authn_gssapi_verify_krb5_init_creds(server *srv, krb5_context context, krb5_creds *creds, krb5_principal ap_req_server, krb5_keytab ap_req_keytab) { krb5_error_code ret; krb5_data req; krb5_ccache local_ccache = NULL; krb5_creds *new_creds = NULL; krb5_auth_context auth_context = NULL; krb5_keytab keytab = NULL; char *server_name; memset(&req, 0, sizeof(req)); if (ap_req_keytab == NULL) { ret = krb5_kt_default(context, &keytab); if (ret) return ret; } else keytab = ap_req_keytab; ret = krb5_cc_resolve(context, "MEMORY:", &local_ccache); if (ret) { log_error_write(srv, __FILE__, __LINE__, "s", "krb5_cc_resolve() failed when verifying KDC"); /* return ret; */ goto end; } ret = krb5_cc_initialize(context, local_ccache, creds->client); if (ret) { log_error_write(srv, __FILE__, __LINE__, "s", "krb5_cc_initialize() failed when verifying KDC"); goto end; } ret = krb5_cc_store_cred(context, local_ccache, creds); if (ret) { log_error_write(srv, __FILE__, __LINE__, "s", "krb5_cc_store_cred() failed when verifying KDC"); goto end; } ret = krb5_unparse_name(context, ap_req_server, &server_name); if (ret) { log_error_write(srv, __FILE__, __LINE__, "s", "krb5_unparse_name() failed when verifying KDC"); goto end; } krb5_free_unparsed_name(context, server_name); if (!krb5_principal_compare(context, ap_req_server, creds->server)) { krb5_creds match_cred; memset(&match_cred, 0, sizeof(match_cred)); match_cred.client = creds->client; match_cred.server = ap_req_server; ret = krb5_get_credentials(context, 0, local_ccache, &match_cred, &new_creds); if (ret) { log_error_write(srv, __FILE__, __LINE__, "s", "krb5_get_credentials() failed when verifying KDC"); goto end; } creds = new_creds; } ret = krb5_mk_req_extended(context, &auth_context, 0, NULL, creds, &req); if (ret) { log_error_write(srv, __FILE__, __LINE__, "s", "krb5_mk_req_extended() failed when verifying KDC"); goto end; } krb5_auth_con_free(context, auth_context); auth_context = NULL; ret = krb5_auth_con_init(context, &auth_context); if (ret) { log_error_write(srv, __FILE__, __LINE__, "s", "krb5_auth_con_init() failed when verifying KDC"); goto end; } /* use KRB5_AUTH_CONTEXT_DO_SEQUENCE to skip replay cache checks */ krb5_auth_con_setflags(context, auth_context, KRB5_AUTH_CONTEXT_DO_SEQUENCE); ret = krb5_rd_req(context, &auth_context, &req, ap_req_server, keytab, 0, NULL); if (ret) { log_error_write(srv, __FILE__, __LINE__, "s", "krb5_rd_req() failed when verifying KDC"); goto end; } end: krb5_free_data_contents(context, &req); if (auth_context) krb5_auth_con_free(context, auth_context); if (new_creds) krb5_free_creds(context, new_creds); if (ap_req_keytab == NULL && keytab) krb5_kt_close(context, keytab); if (local_ccache) krb5_cc_destroy(context, local_ccache); return ret; }
krb5_error_code KRB5_CALLCONV krb5_verify_init_creds(krb5_context context, krb5_creds *creds, krb5_principal server_arg, krb5_keytab keytab_arg, krb5_ccache *ccache_arg, krb5_verify_init_creds_opt *options) { krb5_error_code ret; krb5_principal server; krb5_keytab keytab; krb5_ccache ccache; krb5_keytab_entry kte; krb5_creds in_creds, *out_creds; krb5_auth_context authcon; krb5_data ap_req; /* KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN */ server = NULL; keytab = NULL; ccache = NULL; out_creds = NULL; authcon = NULL; ap_req.data = NULL; if (server_arg) server = server_arg; else if (ret = krb5_sname_to_principal(context, NULL, NULL, KRB5_NT_SRV_HST, &server)) goto cleanup; /* first, check if the server is in the keytab. If not, there's no reason to continue. rd_req does all this, but there's no way to know that a given error is caused by a missing keytab or key, and not by some other problem. */ if (keytab_arg) { keytab = keytab_arg; } else { /* Solaris Kerberos: ignore errors here, deal with below */ ret = krb5_kt_default(context, &keytab); } /* Warning: be very, very careful when modifying the logic here */ if (keytab == NULL || (ret = krb5_kt_get_entry(context, keytab, server, 0, 0, &kte))) { /* this means there is no keying material. This is ok, as long as it is not prohibited by the configuration */ int nofail = 1; /* Solaris Kerberos: default return error if keytab problems */ if (options && (options->flags & KRB5_VERIFY_INIT_CREDS_OPT_AP_REQ_NOFAIL)) { /* first, if options are set then use the option value to set nofail */ nofail = options->ap_req_nofail; } else { /* * Check verify_ap_req_nofail if set in config file. Note this logic * assumes that krb5_libdefault_boolean will not set nofail to a * default value if verify_ap_req_nofail is not explictly set in * config file. Don't care about the return code. */ (void) krb5_libdefault_boolean(context, &creds->client->realm, "verify_ap_req_nofail", &nofail); } /* Solaris Kerberos: exit without an error ONLY if nofail is false */ if (!nofail) ret = 0; goto cleanup; } krb5_kt_free_entry(context, &kte); /* If the creds are for the server principal, we're set, just do a mk_req. Otherwise, do a get_credentials first. */ if (krb5_principal_compare(context, server, creds->server)) { /* make an ap_req */ if ((ret = krb5_mk_req_extended(context, &authcon, 0, NULL, creds, &ap_req))) goto cleanup; } else { /* this is unclean, but it's the easiest way without ripping the library into very small pieces. store the client's initial cred in a memory ccache, then call the library. Later, we'll copy everything except the initial cred into the ccache we return to the user. A clean implementation would involve library internals with a coherent idea of "in" and "out". */ /* insert the initial cred into the ccache */ if ((ret = krb5_cc_resolve(context, "MEMORY:rd_req", &ccache))) goto cleanup; if ((ret = krb5_cc_initialize(context, ccache, creds->client)) != NULL) goto cleanup; if ((ret = krb5_cc_store_cred(context, ccache, creds)) != NULL) goto cleanup; /* set up for get_creds */ memset(&in_creds, 0, sizeof(in_creds)); in_creds.client = creds->client; in_creds.server = server; if ((ret = krb5_timeofday(context, &in_creds.times.endtime))) goto cleanup; in_creds.times.endtime += 5*60; if ((ret = krb5_get_credentials(context, 0, ccache, &in_creds, &out_creds))) goto cleanup; /* make an ap_req */ if ((ret = krb5_mk_req_extended(context, &authcon, 0, NULL, out_creds, &ap_req))) goto cleanup; } /* wipe the auth context for mk_req */ if (authcon) { krb5_auth_con_free(context, authcon); authcon = NULL; } /* verify the ap_req */ if ((ret = krb5_rd_req(context, &authcon, &ap_req, server, keytab, NULL, NULL))) goto cleanup; /* if we get this far, then the verification succeeded. We can still fail if the library stuff here fails, but that's it */ if (ccache_arg && ccache) { if (*ccache_arg == NULL) { krb5_ccache retcc; retcc = NULL; if (((ret = krb5_cc_resolve(context, "MEMORY:rd_req2", &retcc)) != NULL) || ((ret = krb5_cc_initialize(context, retcc, creds->client)) != NULL) || ((ret = krb5_cc_copy_creds_except(context, ccache, retcc, creds->server)) != NULL)) { if (retcc) (void) krb5_cc_destroy(context, retcc); } else { *ccache_arg = retcc; } } else { ret = krb5_cc_copy_creds_except(context, ccache, *ccache_arg, server); } } /* if any of the above paths returned an errors, then ret is set accordingly. either that, or it's zero, which is fine, too */ cleanup: if (!server_arg && server) krb5_free_principal(context, server); if (!keytab_arg && keytab) (void) krb5_kt_close(context, keytab); if (ccache) (void) krb5_cc_destroy(context, ccache); if (out_creds) krb5_free_creds(context, out_creds); if (authcon) krb5_auth_con_free(context, authcon); if (ap_req.data) krb5_xfree(ap_req.data); return(ret); }
int kerberos5_send (TN_Authenticator * ap) { krb5_error_code r; krb5_ccache ccache; krb5_creds creds; krb5_creds *new_creds = 0; int ap_opts; char type_check[2]; krb5_data check_data; if (!UserNameRequested) { DEBUG (("telnet: Kerberos V5: no user name supplied\r\n")); return 0; } if ((r = krb5_cc_default (telnet_context, &ccache))) { DEBUG (("telnet: Kerberos V5: could not get default ccache\r\n")); return 0; } memset (&creds, 0, sizeof (creds)); if ((r = krb5_sname_to_principal (telnet_context, RemoteHostName, "host", KRB5_NT_SRV_HST, &creds.server))) { DEBUG (("telnet: Kerberos V5: error while constructing service name: %s\r\n", error_message (r))); return 0; } if (telnet_krb5_realm) { krb5_data rdata; rdata.length = strlen (telnet_krb5_realm); rdata.data = malloc (rdata.length + 1); assert (rdata.data); strcpy (rdata.data, telnet_krb5_realm); krb5_princ_set_realm (telnet_context, creds.server, &rdata); } if ((r = krb5_cc_get_principal (telnet_context, ccache, &creds.client))) { DEBUG (("telnet: Kerberos V5: failure on principal (%s)\r\n", error_message (r))); krb5_free_cred_contents (telnet_context, &creds); return 0; } creds.keyblock.enctype = ENCTYPE_DES_CBC_CRC; if ((r = krb5_get_credentials (telnet_context, 0, ccache, &creds, &new_creds))) { DEBUG (("telnet: Kerberos V5: failure on credentials(%s)\r\n", error_message (r))); krb5_free_cred_contents (telnet_context, &creds); return 0; } if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ap_opts = AP_OPTS_MUTUAL_REQUIRED; else ap_opts = 0; # ifdef ENCRYPTION ap_opts |= AP_OPTS_USE_SUBKEY; # endif if (auth_context) { krb5_auth_con_free (telnet_context, auth_context); auth_context = 0; } if ((r = krb5_auth_con_init (telnet_context, &auth_context))) { DEBUG (("Kerberos V5: failed to init auth_context (%s)\r\n", error_message (r))); return 0; } krb5_auth_con_setflags (telnet_context, auth_context, KRB5_AUTH_CONTEXT_RET_TIME); type_check[0] = ap->type; type_check[1] = ap->way; check_data.magic = KV5M_DATA; check_data.length = 2; check_data.data = (char *) &type_check; r = krb5_mk_req_extended (telnet_context, &auth_context, ap_opts, &check_data, new_creds, &auth); encryption_init (new_creds); krb5_free_cred_contents (telnet_context, &creds); krb5_free_creds (telnet_context, new_creds); if (r) { DEBUG (("telnet: Kerberos V5: mk_req failed (%s)\r\n", error_message (r))); return 0; } if (!auth_sendname (UserNameRequested, strlen (UserNameRequested))) { DEBUG (("telnet: Not enough room for user name\r\n")); return 0; } if (!Data (ap, KRB_AUTH, auth.data, auth.length)) { DEBUG (("telnet: Not enough room for authentication data\r\n")); return 0; } DEBUG (("telnet: Sent Kerberos V5 credentials to server\r\n")); return 1; }
/* * The username/password have been verified, now we must verify the * response came from a valid KDC by getting a ticket for our own * service that we can verify using our keytab. * * Based on mod_auth_kerb */ static gss_client_response *verify_krb5_kdc(krb5_context context, krb5_creds *creds, const char *service) { krb5_error_code problem; krb5_keytab keytab = NULL; krb5_ccache tmp_ccache = NULL; krb5_principal server_princ = NULL; krb5_creds *new_creds = NULL; krb5_auth_context auth_context = NULL; krb5_data req; gss_client_response *response = NULL; memset(&req, 0, sizeof(req)); problem = krb5_kt_default (context, &keytab); if (problem) { response = krb5_ctx_error(context, problem); goto out; } problem = krb5_cc_new_unique(context, "MEMORY", NULL, &tmp_ccache); if (problem) { response = krb5_ctx_error(context, problem); goto out; } problem = krb5_cc_initialize(context, tmp_ccache, creds->client); if (problem) { response = krb5_ctx_error(context, problem); goto out; } problem = krb5_cc_store_cred(context, tmp_ccache, creds); if (problem) { response = krb5_ctx_error(context, problem); goto out; } problem = krb5_parse_name(context, service, &server_princ); if (problem) { response = krb5_ctx_error(context, problem); goto out; } /* * creds->server is (almost always?) krbtgt service, and server_princ is not. * In which case retrieve a service ticket for server_princ service from KDC */ if (!krb5_principal_compare(context, server_princ, creds->server)) { krb5_creds match_cred; memset (&match_cred, 0, sizeof(match_cred)); match_cred.client = creds->client; match_cred.server = server_princ; problem = krb5_get_credentials(context, 0, tmp_ccache, &match_cred, &new_creds); if (problem) { response = krb5_ctx_error(context, problem); goto out; } creds = new_creds; } problem = krb5_mk_req_extended(context, &auth_context, 0, NULL, creds, &req); if (problem) { response = krb5_ctx_error(context, problem); goto out; } krb5_auth_con_free(context, auth_context); auth_context = NULL; problem = krb5_auth_con_init(context, &auth_context); if (problem) { response = krb5_ctx_error(context, problem); goto out; } /* disable replay cache checks */ krb5_auth_con_setflags(context, auth_context, KRB5_AUTH_CONTEXT_DO_SEQUENCE); problem = krb5_rd_req(context, &auth_context, &req, server_princ, keytab, 0, NULL); if (problem) { response = krb5_ctx_error(context, problem); goto out; } out: krb5_free_data_contents(context, &req); if (auth_context) { krb5_auth_con_free(context, auth_context); } if (new_creds) { krb5_free_creds(context, new_creds); } if (server_princ) { krb5_free_principal(context, server_princ); } if (tmp_ccache) { krb5_cc_destroy (context, tmp_ccache); } if (keytab) { krb5_kt_close (context, keytab); } return response; }
/* Given krb5 service (typically "kssl") and hostname in kssl_ctx, ** Return encrypted Kerberos ticket for service @ hostname. ** If authenp is non-NULL, also return encrypted authenticator, ** whose data should be freed by caller. ** (Originally was: Create Kerberos AP_REQ message for SSL Client.) ** ** 19990628 VRS Started; Returns Kerberos AP_REQ message. ** 20010409 VRS Modified for RFC2712; Returns enc tkt. ** 20010606 VRS May also return optional authenticator. */ krb5_error_code kssl_cget_tkt( /* UPDATE */ KSSL_CTX *kssl_ctx, /* OUT */ krb5_data **enc_ticketp, /* UPDATE */ krb5_data *authenp, /* OUT */ KSSL_ERR *kssl_err) { krb5_error_code krb5rc = KRB5KRB_ERR_GENERIC; krb5_context krb5context = NULL; krb5_auth_context krb5auth_context = NULL; krb5_ccache krb5ccdef = NULL; krb5_creds krb5creds, *krb5credsp = NULL; krb5_data krb5_app_req; kssl_err_set(kssl_err, 0, ""); memset((char *)&krb5creds, 0, sizeof(krb5creds)); if (!kssl_ctx) { kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT, "No kssl_ctx defined.\n"); goto err; } else if (!kssl_ctx->service_host) { kssl_err_set(kssl_err, SSL_R_KRB5_S_INIT, "kssl_ctx service_host undefined.\n"); goto err; } if ((krb5rc = krb5_init_context(&krb5context)) != 0) { (void) snprintf(kssl_err->text,KSSL_ERR_MAX, "krb5_init_context() fails: %d\n", krb5rc); kssl_err->reason = SSL_R_KRB5_C_INIT; goto err; } if ((krb5rc = krb5_sname_to_principal(krb5context, kssl_ctx->service_host, (kssl_ctx->service_name)? kssl_ctx->service_name: KRB5SVC, KRB5_NT_SRV_HST, &krb5creds.server)) != 0) { (void) snprintf(kssl_err->text,KSSL_ERR_MAX, "krb5_sname_to_principal() fails for %s/%s\n", kssl_ctx->service_host, (kssl_ctx->service_name)? kssl_ctx->service_name: KRB5SVC); kssl_err->reason = SSL_R_KRB5_C_INIT; goto err; } if ((krb5rc = krb5_cc_default(krb5context, &krb5ccdef)) != 0) { kssl_err_set(kssl_err, SSL_R_KRB5_C_CC_PRINC, "krb5_cc_default fails.\n"); goto err; } if ((krb5rc = krb5_cc_get_principal(krb5context, krb5ccdef, &krb5creds.client)) != 0) { kssl_err_set(kssl_err, SSL_R_KRB5_C_CC_PRINC, "krb5_cc_get_principal() fails.\n"); goto err; } if ((krb5rc = krb5_get_credentials(krb5context, 0, krb5ccdef, &krb5creds, &krb5credsp)) != 0) { kssl_err_set(kssl_err, SSL_R_KRB5_C_GET_CRED, "krb5_get_credentials() fails.\n"); goto err; } *enc_ticketp = &krb5credsp->ticket; #ifdef KRB5_HEIMDAL kssl_ctx->enctype = krb5credsp->session.keytype; #else kssl_ctx->enctype = krb5credsp->keyblock.enctype; #endif krb5rc = KRB5KRB_ERR_GENERIC; /* caller should free data of krb5_app_req */ /* 20010406 VRS deleted for real KerberosWrapper ** 20010605 VRS reinstated to offer Authenticator to KerberosWrapper */ krb5_app_req.length = 0; if (authenp) { krb5_data krb5in_data; const unsigned char *p; long arlen; KRB5_APREQBODY *ap_req; authenp->length = 0; krb5in_data.data = NULL; krb5in_data.length = 0; if ((krb5rc = krb5_mk_req_extended(krb5context, &krb5auth_context, 0, &krb5in_data, krb5credsp, &krb5_app_req)) != 0) { kssl_err_set(kssl_err, SSL_R_KRB5_C_MK_REQ, "krb5_mk_req_extended() fails.\n"); goto err; } arlen = krb5_app_req.length; p = (unsigned char *)krb5_app_req.data; ap_req = (KRB5_APREQBODY *) d2i_KRB5_APREQ(NULL, &p, arlen); if (ap_req) { authenp->length = i2d_KRB5_ENCDATA( ap_req->authenticator, NULL); if (authenp->length && (authenp->data = malloc(authenp->length))) { unsigned char *adp = (unsigned char *)authenp->data; authenp->length = i2d_KRB5_ENCDATA( ap_req->authenticator, &adp); } } if (ap_req) KRB5_APREQ_free((KRB5_APREQ *) ap_req); if (krb5_app_req.length) kssl_krb5_free_data_contents(krb5context, &krb5_app_req); } #ifdef KRB5_HEIMDAL if (kssl_ctx_setkey(kssl_ctx, &krb5credsp->session)) { kssl_err_set(kssl_err, SSL_R_KRB5_C_INIT, "kssl_ctx_setkey() fails.\n"); } #else if (kssl_ctx_setkey(kssl_ctx, &krb5credsp->keyblock)) { kssl_err_set(kssl_err, SSL_R_KRB5_C_INIT, "kssl_ctx_setkey() fails.\n"); } #endif else krb5rc = 0; err: #ifdef KSSL_DEBUG kssl_ctx_show(kssl_ctx); #endif /* KSSL_DEBUG */ if (krb5creds.client) krb5_free_principal(krb5context, krb5creds.client); if (krb5creds.server) krb5_free_principal(krb5context, krb5creds.server); if (krb5auth_context) krb5_auth_con_free(krb5context, krb5auth_context); if (krb5context) krb5_free_context(krb5context); return (krb5rc); }
static int k5_auth_send(kstream ks, int how) { krb5_error_code r; krb5_ccache ccache; krb5_creds creds; krb5_creds * new_creds; extern krb5_flags krb5_kdc_default_options; krb5_flags ap_opts; char type_check[2]; krb5_data check_data; int len; #ifdef ENCRYPTION krb5_keyblock *newkey = 0; #endif if (r = krb5_cc_default(k5_context, &ccache)) { com_err(NULL, r, "while authorizing."); return(0); } memset((char *)&creds, 0, sizeof(creds)); if (r = krb5_sname_to_principal(k5_context, szHostName, KRB_SERVICE_NAME, KRB5_NT_SRV_HST, &creds.server)) { com_err(NULL, r, "while authorizing."); return(0); } if (r = krb5_cc_get_principal(k5_context, ccache, &creds.client)) { com_err(NULL, r, "while authorizing."); krb5_free_cred_contents(k5_context, &creds); return(0); } if (szUserName[0] == '\0') { /* Get user name now */ len = krb5_princ_component(k5_context, creds.client, 0)->length; memcpy(szUserName, krb5_princ_component(k5_context, creds.client, 0)->data, len); szUserName[len] = '\0'; } if (r = krb5_get_credentials(k5_context, 0, ccache, &creds, &new_creds)) { com_err(NULL, r, "while authorizing."); krb5_free_cred_contents(k5_context, &creds); return(0); } ap_opts = 0; if ((how & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) ap_opts = AP_OPTS_MUTUAL_REQUIRED; #ifdef ENCRYPTION ap_opts |= AP_OPTS_USE_SUBKEY; #endif if (auth_context) { krb5_auth_con_free(k5_context, auth_context); auth_context = 0; } if ((r = krb5_auth_con_init(k5_context, &auth_context))) { com_err(NULL, r, "while initializing auth context"); return(0); } krb5_auth_con_setflags(k5_context, auth_context, KRB5_AUTH_CONTEXT_RET_TIME); type_check[0] = AUTHTYPE_KERBEROS_V5; type_check[1] = AUTH_WHO_CLIENT| (how & AUTH_HOW_MASK); #ifdef ENCRYPTION type_check[1] |= AUTH_ENCRYPT_ON; #endif check_data.magic = KV5M_DATA; check_data.length = 2; check_data.data = (char *)&type_check; r = krb5_mk_req_extended(k5_context, &auth_context, ap_opts, NULL, new_creds, &auth); #ifdef ENCRYPTION krb5_auth_con_getlocalsubkey(k5_context, auth_context, &newkey); if (session_key) { krb5_free_keyblock(k5_context, session_key); session_key = 0; } if (newkey) { /* * keep the key in our private storage, but don't use it * yet---see kerberos5_reply() below */ if ((newkey->enctype != ENCTYPE_DES_CBC_CRC) && (newkey-> enctype != ENCTYPE_DES_CBC_MD5)) { if ((new_creds->keyblock.enctype == ENCTYPE_DES_CBC_CRC) || (new_creds->keyblock.enctype == ENCTYPE_DES_CBC_MD5)) /* use the session key in credentials instead */ krb5_copy_keyblock(k5_context, &new_creds->keyblock, &session_key); else ; /* What goes here? XXX */ } else { krb5_copy_keyblock(k5_context, newkey, &session_key); } krb5_free_keyblock(k5_context, newkey); } #endif /* ENCRYPTION */ krb5_free_cred_contents(k5_context, &creds); krb5_free_creds(k5_context, new_creds); if (r) { com_err(NULL, r, "while authorizing."); return(0); } return(1); }
static krb5_error_code get_vfy_cred(krb5_context context, krb5_creds *creds, krb5_principal server, krb5_keytab keytab, krb5_ccache *ccache_arg) { krb5_error_code ret; krb5_ccache ccache = NULL, retcc = NULL; krb5_creds in_creds, *out_creds = NULL; krb5_auth_context authcon = NULL; krb5_data ap_req = empty_data(); /* If the creds are for the server principal, we're set, just do a mk_req. * Otherwise, do a get_credentials first. */ if (krb5_principal_compare(context, server, creds->server)) { /* Make an ap-req. */ ret = krb5_mk_req_extended(context, &authcon, 0, NULL, creds, &ap_req); if (ret) goto cleanup; } else { /* * This is unclean, but it's the easiest way without ripping the * library into very small pieces. store the client's initial cred * in a memory ccache, then call the library. Later, we'll copy * everything except the initial cred into the ccache we return to * the user. A clean implementation would involve library * internals with a coherent idea of "in" and "out". */ /* Insert the initial cred into the ccache. */ ret = krb5_cc_new_unique(context, "MEMORY", NULL, &ccache); if (ret) goto cleanup; ret = krb5_cc_initialize(context, ccache, creds->client); if (ret) goto cleanup; ret = krb5_cc_store_cred(context, ccache, creds); if (ret) goto cleanup; /* Get credentials with get_creds. */ memset(&in_creds, 0, sizeof(in_creds)); in_creds.client = creds->client; in_creds.server = server; ret = krb5_timeofday(context, &in_creds.times.endtime); if (ret) goto cleanup; in_creds.times.endtime += 5*60; ret = krb5_get_credentials(context, 0, ccache, &in_creds, &out_creds); if (ret) goto cleanup; /* Make an ap-req. */ ret = krb5_mk_req_extended(context, &authcon, 0, NULL, out_creds, &ap_req); if (ret) goto cleanup; } /* Wipe the auth context created by mk_req. */ if (authcon) { krb5_auth_con_free(context, authcon); authcon = NULL; } /* Verify the ap_req. */ ret = krb5_rd_req(context, &authcon, &ap_req, server, keytab, NULL, NULL); if (ret) goto cleanup; /* If we get this far, then the verification succeeded. We can * still fail if the library stuff here fails, but that's it. */ if (ccache_arg != NULL && ccache != NULL) { if (*ccache_arg == NULL) { ret = krb5_cc_resolve(context, "MEMORY:rd_req2", &retcc); if (ret) goto cleanup; ret = krb5_cc_initialize(context, retcc, creds->client); if (ret) goto cleanup; ret = copy_creds_except(context, ccache, retcc, creds->server); if (ret) goto cleanup; *ccache_arg = retcc; retcc = NULL; } else { ret = copy_creds_except(context, ccache, *ccache_arg, server); } } cleanup: if (retcc != NULL) krb5_cc_destroy(context, retcc); if (ccache != NULL) krb5_cc_destroy(context, ccache); krb5_free_creds(context, out_creds); krb5_auth_con_free(context, authcon); krb5_free_data_contents(context, &ap_req); return ret; }
krb5_error_code KRB5_LIB_FUNCTION krb5_sendauth(krb5_context context, krb5_auth_context *auth_context, krb5_pointer p_fd, const char *appl_version, krb5_principal client, krb5_principal server, krb5_flags ap_req_options, krb5_data *in_data, krb5_creds *in_creds, krb5_ccache ccache, krb5_error **ret_error, krb5_ap_rep_enc_part **rep_result, krb5_creds **out_creds) { krb5_error_code ret; uint32_t len, net_len; const char *version = KRB5_SENDAUTH_VERSION; u_char repl; krb5_data ap_req, error_data; krb5_creds this_cred; krb5_principal this_client = NULL; krb5_creds *creds; ssize_t sret; krb5_boolean my_ccache = FALSE; len = strlen(version) + 1; net_len = htonl(len); if (krb5_net_write (context, p_fd, &net_len, 4) != 4 || krb5_net_write (context, p_fd, version, len) != len) { ret = errno; krb5_set_error_string (context, "write: %s", strerror(ret)); return ret; } len = strlen(appl_version) + 1; net_len = htonl(len); if (krb5_net_write (context, p_fd, &net_len, 4) != 4 || krb5_net_write (context, p_fd, appl_version, len) != len) { ret = errno; krb5_set_error_string (context, "write: %s", strerror(ret)); return ret; } sret = krb5_net_read (context, p_fd, &repl, sizeof(repl)); if (sret < 0) { ret = errno; krb5_set_error_string (context, "read: %s", strerror(ret)); return ret; } else if (sret != sizeof(repl)) { krb5_clear_error_string (context); return KRB5_SENDAUTH_BADRESPONSE; } if (repl != 0) { krb5_clear_error_string (context); return KRB5_SENDAUTH_REJECTED; } if (in_creds == NULL) { if (ccache == NULL) { ret = krb5_cc_default (context, &ccache); if (ret) return ret; my_ccache = TRUE; } if (client == NULL) { ret = krb5_cc_get_principal (context, ccache, &this_client); if (ret) { if(my_ccache) krb5_cc_close(context, ccache); return ret; } client = this_client; } memset(&this_cred, 0, sizeof(this_cred)); this_cred.client = client; this_cred.server = server; this_cred.times.endtime = 0; this_cred.ticket.length = 0; in_creds = &this_cred; } if (in_creds->ticket.length == 0) { ret = krb5_get_credentials (context, 0, ccache, in_creds, &creds); if (ret) { if(my_ccache) krb5_cc_close(context, ccache); return ret; } } else { creds = in_creds; } if(my_ccache) krb5_cc_close(context, ccache); ret = krb5_mk_req_extended (context, auth_context, ap_req_options, in_data, creds, &ap_req); if (out_creds) *out_creds = creds; else krb5_free_creds(context, creds); if(this_client) krb5_free_principal(context, this_client); if (ret) return ret; ret = krb5_write_message (context, p_fd, &ap_req); if (ret) return ret; krb5_data_free (&ap_req); ret = krb5_read_message (context, p_fd, &error_data); if (ret) return ret; if (error_data.length != 0) { KRB_ERROR error; ret = krb5_rd_error (context, &error_data, &error); krb5_data_free (&error_data); if (ret == 0) { ret = krb5_error_from_rd_error(context, &error, NULL); if (ret_error != NULL) { *ret_error = malloc (sizeof(krb5_error)); if (*ret_error == NULL) { krb5_free_error_contents (context, &error); } else { **ret_error = error; } } else { krb5_free_error_contents (context, &error); } return ret; } else { krb5_clear_error_string(context); return ret; } } if (ap_req_options & AP_OPTS_MUTUAL_REQUIRED) { krb5_data ap_rep; krb5_ap_rep_enc_part *ignore; krb5_data_zero (&ap_rep); ret = krb5_read_message (context, p_fd, &ap_rep); if (ret) return ret; ret = krb5_rd_rep (context, *auth_context, &ap_rep, rep_result ? rep_result : &ignore); krb5_data_free (&ap_rep); if (ret) return ret; if (rep_result == NULL) krb5_free_ap_rep_enc_part (context, ignore); } return 0; }
int Condor_Auth_Kerberos :: authenticate_client_kerberos() { krb5_error_code code; krb5_flags flags; krb5_data request; int reply, rc = FALSE; request.data = 0; request.length = 0; //------------------------------------------ // Set up the flags //------------------------------------------ flags = AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY; //------------------------------------------ // Load local addresses //------------------------------------------ assert(creds_); if (creds_->addresses == NULL) { dprintf ( D_SECURITY, "KERBEROS: creds_->addresses == NULL\n"); if ((code = krb5_os_localaddr(krb_context_, &(creds_->addresses)))) { goto error; } } dprintf_krb5_principal ( D_FULLDEBUG, "KERBEROS: creds_->client is '%s'\n", creds_->client); dprintf_krb5_principal ( D_FULLDEBUG, "KERBEROS: creds_->server is '%s'\n", creds_->server); //------------------------------------------ // Let's create the KRB_AP_REQ message //------------------------------------------ if ((code = krb5_mk_req_extended(krb_context_, &auth_context_, flags, 0, creds_, &request))) { goto error; } // Send out the request if ((reply = send_request(&request)) != KERBEROS_MUTUAL) { dprintf( D_ALWAYS, "KERBEROS: Could not authenticate!\n" ); return FALSE; } //------------------------------------------ // Now, mutual authenticate //------------------------------------------ reply = client_mutual_authenticate(); switch (reply) { case KERBEROS_DENY: dprintf( D_ALWAYS, "KERBEROS: Authentication failed\n" ); return FALSE; break; // unreachable case KERBEROS_FORWARD: // We need to forward the credentials // We could do a fast forwarding (i.e stashing, if client/server // are located on the same machine. However, I want to keep the // forwarding mechanism clean, so, we use krb5_fwd_tgt_creds // regardless of where client/server are located // This is an implict GRANT //if (forward_tgt_creds(creds_, 0)) { // dprintf(D_ALWAYS,"KERBEROS: Unable to forward credentials\n"); //return FALSE; // } case KERBEROS_GRANT: break; default: dprintf( D_ALWAYS, "KERBEROS: Response is invalid\n" ); break; } //------------------------------------------ // Success, do some cleanup //------------------------------------------ setRemoteAddress(); //------------------------------------------ // Store the session key for encryption //------------------------------------------ if ((code = krb5_copy_keyblock(krb_context_, &(creds_->keyblock), &sessionKey_))) { goto error; } rc = TRUE; goto cleanup; error: dprintf( D_ALWAYS, "KERBEROS: %s\n", error_message(code) ); // Abort mySock_->encode(); reply = KERBEROS_ABORT; if (!mySock_->code(reply) || !mySock_->end_of_message()) { dprintf( D_ALWAYS, "KERBEROS: Failed to send ABORT message.\n"); } rc = FALSE; cleanup: if (creds_) { krb5_free_creds(krb_context_, creds_); } if (request.data) { free(request.data); } return rc; }
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_verify_init_creds(krb5_context context, krb5_creds *creds, krb5_principal ap_req_server, krb5_keytab ap_req_keytab, krb5_ccache *ccache, krb5_verify_init_creds_opt *options) { krb5_error_code ret; krb5_data req; krb5_ccache local_ccache = NULL; krb5_creds *new_creds = NULL; krb5_auth_context auth_context = NULL; krb5_principal server = NULL; krb5_keytab keytab = NULL; krb5_data_zero (&req); if (ap_req_server == NULL) { char local_hostname[MAXHOSTNAMELEN]; if (gethostname (local_hostname, sizeof(local_hostname)) < 0) { ret = errno; krb5_set_error_message (context, ret, "gethostname: %s", strerror(ret)); return ret; } ret = krb5_sname_to_principal (context, local_hostname, "host", KRB5_NT_SRV_HST, &server); if (ret) goto cleanup; } else server = ap_req_server; if (ap_req_keytab == NULL) { ret = krb5_kt_default (context, &keytab); if (ret) goto cleanup; } else keytab = ap_req_keytab; if (ccache && *ccache) local_ccache = *ccache; else { ret = krb5_cc_new_unique(context, krb5_cc_type_memory, NULL, &local_ccache); if (ret) goto cleanup; ret = krb5_cc_initialize (context, local_ccache, creds->client); if (ret) goto cleanup; ret = krb5_cc_store_cred (context, local_ccache, creds); if (ret) goto cleanup; } if (!krb5_principal_compare (context, server, creds->server)) { krb5_creds match_cred; memset (&match_cred, 0, sizeof(match_cred)); match_cred.client = creds->client; match_cred.server = server; ret = krb5_get_credentials (context, 0, local_ccache, &match_cred, &new_creds); if (ret) { if (fail_verify_is_ok (context, options)) ret = 0; goto cleanup; } creds = new_creds; } ret = krb5_mk_req_extended (context, &auth_context, 0, NULL, creds, &req); krb5_auth_con_free (context, auth_context); auth_context = NULL; if (ret) goto cleanup; ret = krb5_rd_req (context, &auth_context, &req, server, keytab, 0, NULL); if (ret == KRB5_KT_NOTFOUND && fail_verify_is_ok (context, options)) ret = 0; cleanup: if (auth_context) krb5_auth_con_free (context, auth_context); krb5_data_free (&req); if (new_creds != NULL) krb5_free_creds (context, new_creds); if (ap_req_server == NULL && server) krb5_free_principal (context, server); if (ap_req_keytab == NULL && keytab) krb5_kt_close (context, keytab); if (local_ccache != NULL && (ccache == NULL || (ret != 0 && *ccache == NULL))) krb5_cc_destroy (context, local_ccache); if (ret == 0 && ccache != NULL && *ccache == NULL) *ccache = local_ccache; return ret; }
static int cifs_krb5_get_req(const char *host, const char *ccname, DATA_BLOB * mechtoken, DATA_BLOB * sess_key) { krb5_error_code ret; krb5_keyblock *tokb; krb5_context context; krb5_ccache ccache; krb5_creds in_creds, *out_creds; krb5_data apreq_pkt, in_data; krb5_auth_context auth_context = NULL; #if defined(HAVE_KRB5_AUTH_CON_SETADDRS) && defined(HAVE_KRB5_AUTH_CON_SET_REQ_CKSUMTYPE) static const uint8_t gss_cksum[24] = { 0x10, 0x00, /* ... */}; #endif ret = krb5_init_context(&context); if (ret) { syslog(LOG_DEBUG, "%s: unable to init krb5 context", __func__); return ret; } ret = krb5_cc_resolve(context, ccname, &ccache); if (ret) { syslog(LOG_DEBUG, "%s: unable to resolve %s to ccache\n", __func__, ccname); goto out_free_context; } memset(&in_creds, 0, sizeof(in_creds)); ret = krb5_cc_get_principal(context, ccache, &in_creds.client); if (ret) { syslog(LOG_DEBUG, "%s: unable to get client principal name", __func__); goto out_free_ccache; } ret = krb5_sname_to_principal(context, host, "cifs", KRB5_NT_UNKNOWN, &in_creds.server); if (ret) { syslog(LOG_DEBUG, "%s: unable to convert sname to princ (%s).", __func__, host); goto out_free_principal; } ret = krb5_get_credentials(context, 0, ccache, &in_creds, &out_creds); krb5_free_principal(context, in_creds.server); if (ret) { syslog(LOG_DEBUG, "%s: unable to get credentials for %s", __func__, host); goto out_free_principal; } in_data.length = 0; in_data.data = NULL; ret = krb5_auth_con_init(context, &auth_context); if (ret) { syslog(LOG_DEBUG, "%s: unable to create auth_context: %d", __func__, ret); goto out_free_creds; } #if defined(HAVE_KRB5_AUTH_CON_SETADDRS) && defined(HAVE_KRB5_AUTH_CON_SET_REQ_CKSUMTYPE) /* Ensure we will get an addressless ticket. */ ret = krb5_auth_con_setaddrs(context, auth_context, NULL, NULL); if (ret) { syslog(LOG_DEBUG, "%s: unable to set NULL addrs: %d", __func__, ret); goto out_free_auth; } /* * Create a GSSAPI checksum (0x8003), see RFC 4121. * * The current layout is * * 0x10, 0x00, 0x00, 0x00 - length = 16 * 0x00, 0x00, 0x00, 0x00 - channel binding info - 16 zero bytes * 0x00, 0x00, 0x00, 0x00 * 0x00, 0x00, 0x00, 0x00 * 0x00, 0x00, 0x00, 0x00 * 0x00, 0x00, 0x00, 0x00 - flags * * GSS_C_NO_CHANNEL_BINDINGS means 16 zero bytes, * this is needed to work against some closed source * SMB servers. * * See https://bugzilla.samba.org/show_bug.cgi?id=7890 */ in_data.data = discard_const_p(char, gss_cksum); in_data.length = 24; /* MIT krb5 < 1.7 is missing the prototype, but still has the symbol */ #if !HAVE_DECL_KRB5_AUTH_CON_SET_REQ_CKSUMTYPE krb5_error_code krb5_auth_con_set_req_cksumtype( krb5_context context, krb5_auth_context auth_context, krb5_cksumtype cksumtype); #endif ret = krb5_auth_con_set_req_cksumtype(context, auth_context, 0x8003); if (ret) { syslog(LOG_DEBUG, "%s: unable to set 0x8003 checksum", __func__); goto out_free_auth; } #endif apreq_pkt.length = 0; apreq_pkt.data = NULL; ret = krb5_mk_req_extended(context, &auth_context, AP_OPTS_USE_SUBKEY, &in_data, out_creds, &apreq_pkt); if (ret) { syslog(LOG_DEBUG, "%s: unable to make AP-REQ for %s", __func__, host); goto out_free_auth; } ret = krb5_auth_con_getsendsubkey(context, auth_context, &tokb); if (ret) { syslog(LOG_DEBUG, "%s: unable to get session key for %s", __func__, host); goto out_free_auth; } *mechtoken = data_blob(apreq_pkt.data, apreq_pkt.length); *sess_key = data_blob(KRB5_KEY_DATA(tokb), KRB5_KEY_LENGTH(tokb)); krb5_free_keyblock(context, tokb); out_free_auth: krb5_auth_con_free(context, auth_context); out_free_creds: krb5_free_creds(context, out_creds); out_free_principal: krb5_free_principal(context, in_creds.client); out_free_ccache: #if defined(KRB5_TC_OPENCLOSE) krb5_cc_set_flags(context, ccache, KRB5_TC_OPENCLOSE); #endif krb5_cc_close(context, ccache); out_free_context: krb5_free_context(context); return ret; }
krb5_error_code KRB5_CALLCONV krb5_verify_init_creds(krb5_context context, krb5_creds *creds, krb5_principal server_arg, krb5_keytab keytab_arg, krb5_ccache *ccache_arg, krb5_verify_init_creds_opt *options) { krb5_error_code ret; krb5_principal server; krb5_keytab keytab; krb5_ccache ccache; krb5_keytab_entry kte; krb5_creds in_creds, *out_creds; krb5_auth_context authcon; krb5_data ap_req; /* KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN */ server = NULL; keytab = NULL; ccache = NULL; out_creds = NULL; authcon = NULL; ap_req.data = NULL; if (keytab_arg) { keytab = keytab_arg; } else { if ((ret = krb5_kt_default(context, &keytab))) goto cleanup; } if (server_arg) { ret = krb5_copy_principal(context, server_arg, &server); if (ret) goto cleanup; } else { /* Use a principal name from the keytab. */ ret = k5_kt_get_principal(context, keytab, &server); if (ret) { /* There's no keytab, or it's empty, or we can't read it. * Allow this unless configuration demands verification. */ if (!nofail(context, options, creds)) ret = 0; goto cleanup; } } /* first, check if the server is in the keytab. If not, there's no reason to continue. rd_req does all this, but there's no way to know that a given error is caused by a missing keytab or key, and not by some other problem. */ if (krb5_is_referral_realm(&server->realm)) { krb5_free_data_contents(context, &server->realm); ret = krb5_get_default_realm(context, &server->realm.data); if (ret) goto cleanup; server->realm.length = strlen(server->realm.data); } if ((ret = krb5_kt_get_entry(context, keytab, server, 0, 0, &kte))) { /* this means there is no keying material. This is ok, as long as it is not prohibited by the configuration */ if (!nofail(context, options, creds)) ret = 0; goto cleanup; } krb5_kt_free_entry(context, &kte); /* If the creds are for the server principal, we're set, just do a mk_req. * Otherwise, do a get_credentials first. */ if (krb5_principal_compare(context, server, creds->server)) { /* make an ap_req */ if ((ret = krb5_mk_req_extended(context, &authcon, 0, NULL, creds, &ap_req))) goto cleanup; } else { /* this is unclean, but it's the easiest way without ripping the library into very small pieces. store the client's initial cred in a memory ccache, then call the library. Later, we'll copy everything except the initial cred into the ccache we return to the user. A clean implementation would involve library internals with a coherent idea of "in" and "out". */ /* insert the initial cred into the ccache */ if ((ret = krb5_cc_new_unique(context, "MEMORY", NULL, &ccache))) { ccache = NULL; goto cleanup; } if ((ret = krb5_cc_initialize(context, ccache, creds->client))) goto cleanup; if ((ret = krb5_cc_store_cred(context, ccache, creds))) goto cleanup; /* set up for get_creds */ memset(&in_creds, 0, sizeof(in_creds)); in_creds.client = creds->client; in_creds.server = server; if ((ret = krb5_timeofday(context, &in_creds.times.endtime))) goto cleanup; in_creds.times.endtime += 5*60; if ((ret = krb5_get_credentials(context, 0, ccache, &in_creds, &out_creds))) goto cleanup; /* make an ap_req */ if ((ret = krb5_mk_req_extended(context, &authcon, 0, NULL, out_creds, &ap_req))) goto cleanup; } /* wipe the auth context for mk_req */ if (authcon) { krb5_auth_con_free(context, authcon); authcon = NULL; } /* verify the ap_req */ if ((ret = krb5_rd_req(context, &authcon, &ap_req, server, keytab, NULL, NULL))) goto cleanup; /* if we get this far, then the verification succeeded. We can still fail if the library stuff here fails, but that's it */ if (ccache_arg && ccache) { if (*ccache_arg == NULL) { krb5_ccache retcc; retcc = NULL; if ((ret = krb5_cc_resolve(context, "MEMORY:rd_req2", &retcc)) || (ret = krb5_cc_initialize(context, retcc, creds->client)) || (ret = copy_creds_except(context, ccache, retcc, creds->server))) { if (retcc) krb5_cc_destroy(context, retcc); } else { *ccache_arg = retcc; } } else { ret = copy_creds_except(context, ccache, *ccache_arg, server); } } /* if any of the above paths returned an errors, then ret is set accordingly. * Either that, or it's zero, which is fine, too */ cleanup: if ( server) krb5_free_principal(context, server); if (!keytab_arg && keytab) krb5_kt_close(context, keytab); if (ccache) krb5_cc_destroy(context, ccache); if (out_creds) krb5_free_creds(context, out_creds); if (authcon) krb5_auth_con_free(context, authcon); if (ap_req.data) free(ap_req.data); return(ret); }
/* we can't use krb5_mk_req because w2k wants the service to be in a particular format */ static krb5_error_code ads_krb5_mk_req(krb5_context context, krb5_auth_context *auth_context, const krb5_flags ap_req_options, const char *principal, krb5_ccache ccache, krb5_data *outbuf, time_t *expire_time) { krb5_error_code retval; krb5_principal server; krb5_creds * credsp; krb5_creds creds; krb5_data in_data; BOOL creds_ready = False; int i = 0, maxtries = 3; retval = smb_krb5_parse_name(context, principal, &server); if (retval) { DEBUG(1,("ads_krb5_mk_req: Failed to parse principal %s\n", principal)); return retval; } /* obtain ticket & session key */ ZERO_STRUCT(creds); if ((retval = krb5_copy_principal(context, server, &creds.server))) { DEBUG(1,("ads_krb5_mk_req: krb5_copy_principal failed (%s)\n", error_message(retval))); goto cleanup_princ; } if ((retval = krb5_cc_get_principal(context, ccache, &creds.client))) { /* This can commonly fail on smbd startup with no ticket in the cache. * Report at higher level than 1. */ DEBUG(3,("ads_krb5_mk_req: krb5_cc_get_principal failed (%s)\n", error_message(retval))); goto cleanup_creds; } while (!creds_ready && (i < maxtries)) { if ((retval = krb5_get_credentials(context, 0, ccache, &creds, &credsp))) { DEBUG(1,("ads_krb5_mk_req: krb5_get_credentials failed for %s (%s)\n", principal, error_message(retval))); goto cleanup_creds; } /* cope with ticket being in the future due to clock skew */ if ((unsigned)credsp->times.starttime > time(NULL)) { time_t t = time(NULL); int time_offset =(int)((unsigned)credsp->times.starttime-t); DEBUG(4,("ads_krb5_mk_req: Advancing clock by %d seconds to cope with clock skew\n", time_offset)); krb5_set_real_time(context, t + time_offset + 1, 0); } if (!ads_cleanup_expired_creds(context, ccache, credsp)) { creds_ready = True; } i++; } DEBUG(10,("ads_krb5_mk_req: Ticket (%s) in ccache (%s:%s) is valid until: (%s - %u)\n", principal, krb5_cc_get_type(context, ccache), krb5_cc_get_name(context, ccache), http_timestring((unsigned)credsp->times.endtime), (unsigned)credsp->times.endtime)); if (expire_time) { *expire_time = (time_t)credsp->times.endtime; } in_data.length = 0; retval = krb5_mk_req_extended(context, auth_context, ap_req_options, &in_data, credsp, outbuf); if (retval) { DEBUG(1,("ads_krb5_mk_req: krb5_mk_req_extended failed (%s)\n", error_message(retval))); } krb5_free_creds(context, credsp); cleanup_creds: krb5_free_cred_contents(context, &creds); cleanup_princ: krb5_free_principal(context, server); return retval; }
static krb5_error_code chgpw_send_request (krb5_context context, krb5_auth_context *auth_context, krb5_creds *creds, krb5_principal targprinc, int is_stream, int sock, char *passwd, const char *host) { krb5_error_code ret; krb5_data ap_req_data; krb5_data krb_priv_data; krb5_data passwd_data; size_t len; u_char header[6]; u_char *p; struct iovec iov[3]; struct msghdr msghdr; if (is_stream) return KRB5_KPASSWD_MALFORMED; if (targprinc && krb5_principal_compare(context, creds->client, targprinc) != TRUE) return KRB5_KPASSWD_MALFORMED; krb5_data_zero (&ap_req_data); ret = krb5_mk_req_extended (context, auth_context, AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY, NULL, /* in_data */ creds, &ap_req_data); if (ret) return ret; passwd_data.data = passwd; passwd_data.length = strlen(passwd); krb5_data_zero (&krb_priv_data); ret = krb5_mk_priv (context, *auth_context, &passwd_data, &krb_priv_data, NULL); if (ret) goto out2; len = 6 + ap_req_data.length + krb_priv_data.length; p = header; *p++ = (len >> 8) & 0xFF; *p++ = (len >> 0) & 0xFF; *p++ = 0; *p++ = 1; *p++ = (ap_req_data.length >> 8) & 0xFF; *p++ = (ap_req_data.length >> 0) & 0xFF; memset(&msghdr, 0, sizeof(msghdr)); msghdr.msg_name = NULL; msghdr.msg_namelen = 0; msghdr.msg_iov = iov; msghdr.msg_iovlen = sizeof(iov)/sizeof(*iov); #if 0 msghdr.msg_control = NULL; msghdr.msg_controllen = 0; #endif iov[0].iov_base = (void*)header; iov[0].iov_len = 6; iov[1].iov_base = ap_req_data.data; iov[1].iov_len = ap_req_data.length; iov[2].iov_base = krb_priv_data.data; iov[2].iov_len = krb_priv_data.length; if (sendmsg (sock, &msghdr, 0) < 0) { ret = errno; krb5_set_error_string(context, "sendmsg %s: %s", host, strerror(ret)); } krb5_data_free (&krb_priv_data); out2: krb5_data_free (&ap_req_data); return ret; }
DWORD LwKrb5InitializeUserLoginCredentials( IN PCSTR pszUserPrincipalName, IN PCSTR pszPassword, IN uid_t uid, IN gid_t gid, IN LW_KRB5_LOGIN_FLAGS Flags, IN PCSTR pszServicePrincipal, IN PCSTR pszServiceRealm, IN PCSTR pszServicePassword, OUT PVOID* ppNdrPacInfo, OUT size_t* pNdrPacInfoSize, OUT PDWORD pdwGoodUntilTime ) { DWORD dwError = 0; krb5_error_code ret = 0; krb5_context ctx = NULL; krb5_ccache cc = NULL; // Free with krb5_free_cred_contents krb5_creds credsRequest = {0}; krb5_creds *pTgsCreds = NULL; krb5_ticket *pTgsTicket = NULL; krb5_ticket *pDecryptedTgs = NULL; krb5_auth_context authContext = NULL; krb5_data apReqPacket = {0}; krb5_keyblock serviceKey = {0}; krb5_data salt = {0}; // Do not free krb5_data machinePassword = {0}; krb5_flags flags = 0; krb5_int32 authcon_flags = 0; BOOLEAN bInLock = FALSE; PCSTR pszTempCacheName = NULL; PSTR pszTempCachePath = NULL; PVOID pNdrPacInfo = NULL; size_t ndrPacInfoSize = 0; DWORD dwGoodUntilTime = 0; ret = krb5_init_context(&ctx); BAIL_ON_KRB_ERROR(ctx, ret); /* Generates a new filed based credentials cache in /tmp. The file will * be owned by root and only accessible by root. */ ret = krb5_cc_new_unique( ctx, "FILE", "hint", &cc); BAIL_ON_KRB_ERROR(ctx, ret); if (Flags & LW_KRB5_LOGIN_FLAG_SMART_CARD) { dwError = LwKrb5GetTgtWithSmartCard( pszUserPrincipalName, pszPassword, krb5_cc_get_name(ctx, cc), &dwGoodUntilTime); } else { dwError = LwKrb5GetTgt( pszUserPrincipalName, pszPassword, krb5_cc_get_name(ctx, cc), &dwGoodUntilTime); } BAIL_ON_LW_ERROR(dwError); ret = krb5_parse_name(ctx, pszServicePrincipal, &credsRequest.server); BAIL_ON_KRB_ERROR(ctx, ret); ret = krb5_cc_get_principal(ctx, cc, &credsRequest.client); BAIL_ON_KRB_ERROR(ctx, ret); /* Get a TGS for our service using the tgt in the cache */ ret = krb5_get_credentials( ctx, 0, /*no options (not user to user encryption, and not only cached) */ cc, &credsRequest, &pTgsCreds); // Don't trust pTgsCreds on an unsuccessful return // This may be non-zero due to the krb5 libs following referrals // but has been freed in the krb5 libs themselves and any useful // tickets have already been cached. if (ret != 0) { pTgsCreds = NULL; } BAIL_ON_KRB_ERROR(ctx, ret); //No need to store the tgs in the cc. Kerberos does that automatically /* Generate an ap_req message, but don't send it anywhere. Just decode it * immediately. This is the only way to get kerberos to decrypt the tgs * using public APIs */ ret = krb5_mk_req_extended( ctx, &authContext, 0, /* no options necessary */ NULL, /* since this isn't a real ap_req, we don't have any supplemental data to send with it. */ pTgsCreds, &apReqPacket); BAIL_ON_KRB_ERROR(ctx, ret); /* Decode (but not decrypt) the tgs ticket so that we can figure out * which encryption type was used in it. */ ret = krb5_decode_ticket(&pTgsCreds->ticket, &pTgsTicket); /* The TGS ticket is encrypted with the machine password and salted with * the service principal. pszServicePrincipal could probably be used * directly, but it's safer to unparse pTgsCreds->server, because the KDC * sent that to us. */ salt.magic = KV5M_DATA; ret = krb5_unparse_name( ctx, pTgsCreds->server, &salt.data); BAIL_ON_KRB_ERROR(ctx, ret); salt.length = strlen(salt.data); machinePassword.magic = KV5M_DATA; machinePassword.data = (PSTR)pszServicePassword, machinePassword.length = strlen(pszServicePassword), /* Generate a key to decrypt the TGS */ ret = krb5_c_string_to_key( ctx, pTgsTicket->enc_part.enctype, &machinePassword, &salt, &serviceKey); BAIL_ON_KRB_ERROR(ctx, ret); /* Typically krb5_rd_req would decode the AP_REQ using the keytab, but * we don't want to depend on the keytab. As a side effect of kerberos' * user to user authentication support, if a key is explictly set on the * auth context, that key will be used to decrypt the TGS instead of the * keytab. * * By manually generating the key and setting it, we don't require * a keytab. */ if (authContext != NULL) { ret = krb5_auth_con_free(ctx, authContext); BAIL_ON_KRB_ERROR(ctx, ret); } ret = krb5_auth_con_init(ctx, &authContext); BAIL_ON_KRB_ERROR(ctx, ret); ret = krb5_auth_con_setuseruserkey( ctx, authContext, &serviceKey); BAIL_ON_KRB_ERROR(ctx, ret); /* Disable replay detection which is unnecessary and * can fail when authenticating large numbers of users. */ krb5_auth_con_getflags(ctx, authContext, &authcon_flags); krb5_auth_con_setflags(ctx, authContext, authcon_flags & ~KRB5_AUTH_CONTEXT_DO_TIME); if (pszServiceRealm) { ret = krb5_set_default_realm(ctx, pszServiceRealm); BAIL_ON_KRB_ERROR(ctx, ret); } /* This decrypts the TGS. As a side effect it ensures that the KDC that * the user's TGT came from is in the same realm that the machine was * joined to (this prevents users from spoofing the KDC). */ ret = krb5_rd_req( ctx, &authContext, &apReqPacket, pTgsCreds->server, NULL, /* we're not using the keytab */ &flags, &pDecryptedTgs); BAIL_ON_KRB_ERROR(ctx, ret); dwError = LwKrb5FindPac( ctx, pDecryptedTgs, &serviceKey, &pNdrPacInfo, &ndrPacInfoSize); BAIL_ON_LW_ERROR(dwError); if (Flags & LW_KRB5_LOGIN_FLAG_UPDATE_CACHE) { /* 1. Copy old credentials from the existing user creds cache to * the temporary cache. * 2. Delete the existing creds cache. * 3. Move the temporary cache file into the final path. */ dwError = pthread_mutex_lock(&gLwKrb5State.UserCacheMutex); BAIL_ON_LW_ERROR(dwError); bInLock = TRUE; dwError = LwKrb5CopyFromUserCache( ctx, cc, uid ); BAIL_ON_LW_ERROR(dwError); pszTempCacheName = krb5_cc_get_name(ctx, cc); if (!strncasecmp(pszTempCacheName, "FILE:", sizeof("FILE:")-1)) { pszTempCacheName += sizeof("FILE:") - 1; } dwError = LwAllocateString(pszTempCacheName, &pszTempCachePath); BAIL_ON_LW_ERROR(dwError); krb5_cc_close(ctx, cc); // Just to make sure no one accesses this now invalid pointer cc = NULL; dwError = LwKrb5MoveCCacheToUserPath( ctx, pszTempCachePath, uid, gid); if (dwError != LW_ERROR_SUCCESS) { /* Let the user login, even if we couldn't create the ccache for * them. Possible causes are: * 1. /tmp is readonly * 2. Another user maliciously setup a weird file (such as a * directory) where the ccache would go. * 3. Someone created a ccache in the small window after we delete * the old one and before we move in the new one. */ LW_LOG_WARNING("Unable to set up credentials cache with tgt for uid %ld", (long)uid); dwError = LwRemoveFile(pszTempCachePath); BAIL_ON_LW_ERROR(dwError); } } error: if (dwError) { LW_SAFE_FREE_MEMORY(pNdrPacInfo); ndrPacInfoSize = 0; dwGoodUntilTime = 0; } if (ctx) { // This function skips fields which are NULL krb5_free_cred_contents(ctx, &credsRequest); if (pTgsCreds != NULL) { krb5_free_creds(ctx, pTgsCreds); } if (pTgsTicket != NULL) { krb5_free_ticket(ctx, pTgsTicket); } if (pDecryptedTgs != NULL) { krb5_free_ticket(ctx, pDecryptedTgs); } if (authContext != NULL) { krb5_auth_con_free(ctx, authContext); } krb5_free_data_contents(ctx, &apReqPacket); krb5_free_data_contents(ctx, &salt); krb5_free_keyblock_contents(ctx, &serviceKey); if (cc != NULL) { krb5_cc_destroy(ctx, cc); } krb5_free_context(ctx); } if (bInLock) { pthread_mutex_unlock(&gLwKrb5State.UserCacheMutex); } LW_SAFE_FREE_STRING(pszTempCachePath); *ppNdrPacInfo = pNdrPacInfo; *pNdrPacInfoSize = ndrPacInfoSize; *pdwGoodUntilTime = dwGoodUntilTime; return dwError; }
static krb5_error_code setpw_prexmit(krb5_context context, int proto, void *ctx, int fd, krb5_data *data) { struct request *request = ctx; krb5_data ap_req_data, krb_priv_data, pwd_data; krb5_error_code ret; ChangePasswdDataMS chpw; krb5_storage *sp = NULL; ssize_t slen; size_t len; krb5_data_zero(&ap_req_data); krb5_data_zero(&krb_priv_data); krb5_data_zero(&pwd_data); ret = krb5_auth_con_genaddrs(context, request->ac, fd, KRB5_AUTH_CONTEXT_GENERATE_LOCAL_ADDR); if (ret) goto out; ret = krb5_mk_req_extended(context, &request->ac, AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY, NULL, request->creds, &ap_req_data); if (ret) goto out; chpw.newpasswd.length = strlen(request->password); chpw.newpasswd.data = rk_UNCONST(request->password); if (request->target) { chpw.targname = &request->target->name; chpw.targrealm = &request->target->realm; } else { chpw.targname = NULL; chpw.targrealm = NULL; } ASN1_MALLOC_ENCODE(ChangePasswdDataMS, pwd_data.data, pwd_data.length, &chpw, &len, ret); if (ret) goto out; if(pwd_data.length != len) krb5_abortx(context, "internal error in ASN.1 encoder"); ret = krb5_mk_priv (context, request->ac, &pwd_data, &krb_priv_data, NULL); if (ret) goto out; sp = krb5_storage_emem(); if (sp == NULL) { ret = ENOMEM; goto out; } len = 6 + ap_req_data.length + krb_priv_data.length; ret = krb5_store_uint16(sp, len); if (ret) goto out; ret = krb5_store_uint16(sp, 0xff80); if (ret) goto out; ret = krb5_store_uint16(sp, ap_req_data.length); if (ret) goto out; slen = krb5_storage_write(sp, ap_req_data.data, ap_req_data.length); if (slen != ap_req_data.length) { ret = EINVAL; goto out; } slen = krb5_storage_write(sp, krb_priv_data.data, krb_priv_data.length); if (slen != krb_priv_data.length) { ret = EINVAL; goto out; } ret = krb5_storage_to_data(sp, data); out: if (ret) _krb5_debugx(context, 10, "setpw_prexmit failed with %d", ret); if (sp) krb5_storage_free(sp); krb5_data_free(&krb_priv_data); krb5_data_free(&ap_req_data); krb5_data_free(&pwd_data); return ret; }
Code_t Z_MakeZcodeAuthentication(register ZNotice_t *notice, char *buffer, int buffer_len, int *phdr_len, krb5_creds *creds) { krb5_error_code result = 0; krb5_keyblock *keyblock; krb5_auth_context authctx; krb5_data *authent; char *cksum_start, *cstart, *cend; int cksum_len, zcode_len = 0, phdr_adj = 0; notice->z_ascii_authent = NULL; keyblock = Z_credskey(creds); authent = (krb5_data *)malloc(sizeof(krb5_data)); if (authent == NULL) result = ENOMEM; authent->data = NULL; /* so that we can blithely krb5_fre_data_contents on the way out */ if (!result) result = krb5_auth_con_init(Z_krb5_ctx, &authctx); if (!result) { result = krb5_mk_req_extended(Z_krb5_ctx, &authctx, 0 /* options */, 0 /* in_data */, creds, authent); krb5_auth_con_free(Z_krb5_ctx, authctx); } if (!result || result == KRB5KRB_AP_ERR_TKT_EXPIRED) { notice->z_auth = 1; if (result == 0) { notice->z_authent_len = authent->length; } else { notice->z_authent_len = 0; result = 0; } zcode_len = notice->z_authent_len * 2 + 2; /* 2x growth plus Z and null */ notice->z_ascii_authent = (char *)malloc(zcode_len); if (notice->z_ascii_authent == NULL) result = ENOMEM; } if (!result) result = ZMakeZcode(notice->z_ascii_authent, zcode_len, (unsigned char *)authent->data, notice->z_authent_len); /* format the notice header, with a zero checksum */ if (!result) result = Z_NewFormatRawHeader(notice, buffer, buffer_len, phdr_len, &cksum_start, &cksum_len, &cstart, &cend); notice->z_authent_len = 0; if (!result) result = Z_InsertZcodeChecksum(keyblock, notice, buffer, cksum_start, cksum_len, cstart, cend, buffer_len, &phdr_adj, 0); if (!result) *phdr_len += phdr_adj; if (notice->z_ascii_authent != NULL) free(notice->z_ascii_authent); krb5_free_data_contents(Z_krb5_ctx, authent); if (authent != NULL) free(authent); return result; }