/** * \brief Establishing Kerberos connection, verifies clients credentials * \param[in] vContextc *C The context of verse server * \param[out] char **u_name user name of client * \return returns 1 if Kerberos authentication was OK 0 if something went wrong */ int vs_kerberos_auth(struct vContext *C, char **u_name){ struct VS_CTX *vs_ctx = CTX_server_ctx(C); struct IO_CTX *io_ctx = CTX_io_ctx(C); struct VStreamConn *stream_conn = CTX_current_stream_conn(C); int flags = 0; int len = MAX_PACKET_SIZE; unsigned char buffer[MAX_PACKET_SIZE]; char *client_principal; krb5_data packet; krb5_error_code krb5err; int flag; /* Make sure socket is blocking */ flag = fcntl(stream_conn->io_ctx.sockfd, F_GETFL, 0); if ((fcntl(stream_conn->io_ctx.sockfd, F_SETFL, flag & ~O_NONBLOCK)) == -1) { if (is_log_level(VRS_PRINT_ERROR)) v_print_log(VRS_PRINT_ERROR, "fcntl(): %s\n", strerror(errno)); return 0; } /* Get KRB_AP_REQ message */ if ((len = recvfrom(stream_conn->io_ctx.sockfd, (char *) buffer, MAX_PACKET_SIZE, flags, NULL, NULL)) < 0) { v_print_log(VRS_PRINT_ERROR, "recvfrom failed.\n"); return 0; } packet.length = len; packet.data = (krb5_pointer) buffer; io_ctx->krb5_auth_ctx = vs_ctx->tcp_io_ctx.krb5_auth_ctx; io_ctx->krb5_ctx = vs_ctx->tcp_io_ctx.krb5_ctx; io_ctx->krb5_ticket = vs_ctx->tcp_io_ctx.krb5_ticket; io_ctx->krb5_keytab = vs_ctx->tcp_io_ctx.krb5_keytab; /* Check authentication info */ if ((krb5err = krb5_rd_req(io_ctx->krb5_ctx, (krb5_auth_context *) &io_ctx->krb5_auth_ctx, &packet, io_ctx->krb5_principal, io_ctx->krb5_keytab, NULL, (krb5_ticket **) &io_ctx->krb5_ticket))) { v_print_log(VRS_PRINT_ERROR, "krb5_rd_req %d: %s\n", (int) krb5err, krb5_get_error_message(io_ctx->krb5_ctx, krb5err)); return 0; } /* Get user name */ if ((krb5err = krb5_unparse_name(io_ctx->krb5_ctx, io_ctx->krb5_ticket->enc_part2->client, &client_principal))) { v_print_log(VRS_PRINT_ERROR, "krb5_unparse_name %d: %s\n", (int) krb5err, krb5_get_error_message(io_ctx->krb5_ctx, krb5err)); return 0; } v_print_log(VRS_PRINT_DEBUG_MSG, "Got authentication info from %s\n", client_principal); *u_name = client_principal; return 1; }
static krb5_error_code armor_ap_request (struct kdc_request_state *state, krb5_fast_armor *armor) { krb5_error_code retval = 0; krb5_auth_context authcontext = NULL; krb5_ticket *ticket = NULL; krb5_keyblock *subkey = NULL; kdc_realm_t *kdc_active_realm = state->realm_data; assert(armor->armor_type == KRB5_FAST_ARMOR_AP_REQUEST); krb5_clear_error_message(kdc_context); retval = krb5_auth_con_init(kdc_context, &authcontext); if (retval == 0) retval = krb5_auth_con_setflags(kdc_context, authcontext, 0); /*disable replay cache*/ retval = krb5_rd_req(kdc_context, &authcontext, &armor->armor_value, NULL /*server*/, kdc_active_realm->realm_keytab, NULL, &ticket); if (retval != 0) { const char * errmsg = krb5_get_error_message(kdc_context, retval); krb5_set_error_message(kdc_context, retval, _("%s while handling ap-request armor"), errmsg); krb5_free_error_message(kdc_context, errmsg); } if (retval == 0) { if (!krb5_principal_compare_any_realm(kdc_context, tgs_server, ticket->server)) { krb5_set_error_message(kdc_context, KRB5KDC_ERR_SERVER_NOMATCH, _("ap-request armor for something other " "than the local TGS")); retval = KRB5KDC_ERR_SERVER_NOMATCH; } } if (retval == 0) { retval = krb5_auth_con_getrecvsubkey(kdc_context, authcontext, &subkey); if (retval != 0 || subkey == NULL) { krb5_set_error_message(kdc_context, KRB5KDC_ERR_POLICY, _("ap-request armor without subkey")); retval = KRB5KDC_ERR_POLICY; } } if (retval == 0) retval = krb5_c_fx_cf2_simple(kdc_context, subkey, "subkeyarmor", ticket->enc_part2->session, "ticketarmor", &state->armor_key); if (ticket) krb5_free_ticket(kdc_context, ticket); if (subkey) krb5_free_keyblock(kdc_context, subkey); if (authcontext) krb5_auth_con_free(kdc_context, authcontext); return retval; }
krb5_error_code krb5_rd_req_return_keyblock_from_keytab(krb5_context context, krb5_auth_context *auth_context, const krb5_data *inbuf, krb5_const_principal server, krb5_keytab keytab, krb5_flags *ap_req_options, krb5_ticket **ticket, krb5_keyblock **keyblock) { krb5_error_code ret; krb5_kvno kvno; krb5_enctype enctype; krb5_keyblock *local_keyblock; ret = krb5_rd_req(context, auth_context, inbuf, server, keytab, ap_req_options, ticket); if (ret) { return ret; } #ifdef KRB5_TICKET_HAS_KEYINFO enctype = (*ticket)->enc_part.enctype; kvno = (*ticket)->enc_part.kvno; #else ret = smb_krb5_get_keyinfo_from_ap_req(context, inbuf, &kvno, &enctype); if (ret) { return ret; } #endif ret = get_key_from_keytab(context, server, enctype, kvno, &local_keyblock); if (ret) { DEBUG(0,("krb5_rd_req_return_keyblock_from_keytab: failed to call get_key_from_keytab\n")); goto out; } out: if (ret && local_keyblock != NULL) { krb5_free_keyblock(context, local_keyblock); } else { *keyblock = local_keyblock; } return ret; }
krb5_error_code KRB5_LIB_FUNCTION krb5_recvauth_match_version(krb5_context context, krb5_auth_context *auth_context, krb5_pointer p_fd, krb5_boolean (*match_appl_version)(const void *, const char*), const void *match_data, krb5_principal server, int32_t flags, krb5_keytab keytab, krb5_ticket **ticket) { krb5_error_code ret; const char *version = KRB5_SENDAUTH_VERSION; char her_version[sizeof(KRB5_SENDAUTH_VERSION)]; char *her_appl_version; u_int32_t len; u_char repl; krb5_data data; krb5_flags ap_options; ssize_t n; /* * If there are no addresses in auth_context, get them from `fd'. */ if (*auth_context == NULL) { ret = krb5_auth_con_init (context, auth_context); if (ret) return ret; } ret = krb5_auth_con_setaddrs_from_fd (context, *auth_context, p_fd); if (ret) return ret; if(!(flags & KRB5_RECVAUTH_IGNORE_VERSION)) { n = krb5_net_read (context, p_fd, &len, 4); if (n < 0) { ret = errno; krb5_set_error_string (context, "read: %s", strerror(errno)); return ret; } if (n == 0) { krb5_clear_error_string (context); return KRB5_SENDAUTH_BADAUTHVERS; } len = ntohl(len); if (len != sizeof(her_version) || krb5_net_read (context, p_fd, her_version, len) != len || strncmp (version, her_version, len)) { repl = 1; krb5_net_write (context, p_fd, &repl, 1); krb5_clear_error_string (context); return KRB5_SENDAUTH_BADAUTHVERS; } } n = krb5_net_read (context, p_fd, &len, 4); if (n < 0) { ret = errno; krb5_set_error_string (context, "read: %s", strerror(errno)); return ret; } if (n == 0) { krb5_clear_error_string (context); return KRB5_SENDAUTH_BADAPPLVERS; } len = ntohl(len); her_appl_version = malloc (len); if (her_appl_version == NULL) { repl = 2; krb5_net_write (context, p_fd, &repl, 1); krb5_set_error_string (context, "malloc: out of memory"); return ENOMEM; } if (krb5_net_read (context, p_fd, her_appl_version, len) != len || !(*match_appl_version)(match_data, her_appl_version)) { repl = 2; krb5_net_write (context, p_fd, &repl, 1); krb5_set_error_string (context, "wrong sendauth version (%s)", her_appl_version); free (her_appl_version); return KRB5_SENDAUTH_BADAPPLVERS; } free (her_appl_version); repl = 0; if (krb5_net_write (context, p_fd, &repl, 1) != 1) { ret = errno; krb5_set_error_string (context, "write: %s", strerror(errno)); return ret; } krb5_data_zero (&data); ret = krb5_read_message (context, p_fd, &data); if (ret) return ret; ret = krb5_rd_req (context, auth_context, &data, server, keytab, &ap_options, ticket); krb5_data_free (&data); if (ret) { krb5_data error_data; krb5_error_code ret2; ret2 = krb5_mk_error (context, ret, NULL, NULL, NULL, server, NULL, NULL, &error_data); if (ret2 == 0) { krb5_write_message (context, p_fd, &error_data); krb5_data_free (&error_data); } return ret; } len = 0; if (krb5_net_write (context, p_fd, &len, 4) != 4) { ret = errno; krb5_set_error_string (context, "write: %s", strerror(errno)); return ret; } if (ap_options & AP_OPTS_MUTUAL_REQUIRED) { ret = krb5_mk_rep (context, *auth_context, &data); if (ret) return ret; ret = krb5_write_message (context, p_fd, &data); if (ret) return ret; krb5_data_free (&data); } return 0; }
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); }
static krb5_error_code ads_secrets_verify_ticket(krb5_context context, krb5_auth_context auth_context, krb5_principal host_princ, const DATA_BLOB *ticket, krb5_ticket **pp_tkt, krb5_keyblock **keyblock, krb5_error_code *perr) { krb5_error_code ret = 0; bool auth_ok = False; char *password_s = NULL; krb5_data password; krb5_enctype enctypes[] = { #if defined(ENCTYPE_ARCFOUR_HMAC) ENCTYPE_ARCFOUR_HMAC, #endif ENCTYPE_DES_CBC_CRC, ENCTYPE_DES_CBC_MD5, ENCTYPE_NULL }; krb5_data packet; int i; *pp_tkt = NULL; *keyblock = NULL; *perr = 0; if (!secrets_init()) { DEBUG(1,("ads_secrets_verify_ticket: secrets_init failed\n")); *perr = KRB5_CONFIG_CANTOPEN; return False; } password_s = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL); if (!password_s) { DEBUG(1,("ads_secrets_verify_ticket: failed to fetch machine password\n")); *perr = KRB5_LIBOS_CANTREADPWD; return False; } password.data = password_s; password.length = strlen(password_s); /* CIFS doesn't use addresses in tickets. This would break NAT. JRA */ packet.length = ticket->length; packet.data = (char *)ticket->data; /* We need to setup a auth context with each possible encoding type in turn. */ for (i=0;enctypes[i];i++) { krb5_keyblock *key = NULL; if (!(key = SMB_MALLOC_P(krb5_keyblock))) { ret = ENOMEM; goto out; } if (create_kerberos_key_from_string(context, host_princ, &password, key, enctypes[i], false)) { SAFE_FREE(key); continue; } krb5_auth_con_setuseruserkey(context, auth_context, key); if (!(ret = krb5_rd_req(context, &auth_context, &packet, NULL, NULL, NULL, pp_tkt))) { DEBUG(10,("ads_secrets_verify_ticket: enc type [%u] decrypted message !\n", (unsigned int)enctypes[i] )); auth_ok = True; krb5_copy_keyblock(context, key, keyblock); krb5_free_keyblock(context, key); break; } DEBUG((ret != KRB5_BAD_ENCTYPE) ? 3 : 10, ("ads_secrets_verify_ticket: enc type [%u] failed to decrypt with error %s\n", (unsigned int)enctypes[i], error_message(ret))); /* successfully decrypted but ticket is just not valid at the moment */ if (ret == KRB5KRB_AP_ERR_TKT_NYV || ret == KRB5KRB_AP_ERR_TKT_EXPIRED || ret == KRB5KRB_AP_ERR_SKEW) { krb5_free_keyblock(context, key); break; } krb5_free_keyblock(context, key); } out: SAFE_FREE(password_s); *perr = ret; return auth_ok; }
static int verify (krb5_auth_context *auth_context, krb5_realm *realms, krb5_keytab keytab, krb5_ticket **ticket, krb5_data *out_data, uint16_t *version, int s, struct sockaddr *sa, int sa_size, u_char *msg, size_t len, krb5_address *client_addr) { krb5_error_code ret; uint16_t pkt_len, pkt_ver, ap_req_len; krb5_data ap_req_data; krb5_data krb_priv_data; krb5_realm *r; /* * Only send an error reply if the request passes basic length * verification. Otherwise, kpasswdd would reply to every UDP packet, * allowing an attacker to set up a ping-pong DoS attack via a spoofed UDP * packet with a source address of another UDP service that also replies * to every packet. * * Also suppress the error reply if ap_req_len is 0, which indicates * either an invalid request or an error packet. An error packet may be * the result of a ping-pong attacker pointing us at another kpasswdd. */ pkt_len = (msg[0] << 8) | (msg[1]); pkt_ver = (msg[2] << 8) | (msg[3]); ap_req_len = (msg[4] << 8) | (msg[5]); if (pkt_len != len) { krb5_warnx (context, "Strange len: %ld != %ld", (long)pkt_len, (long)len); return 1; } if (ap_req_len == 0) { krb5_warnx (context, "Request is error packet (ap_req_len == 0)"); return 1; } if (pkt_ver != KRB5_KPASSWD_VERS_CHANGEPW && pkt_ver != KRB5_KPASSWD_VERS_SETPW) { krb5_warnx (context, "Bad version (%d)", pkt_ver); reply_error (NULL, s, sa, sa_size, 0, 1, "Wrong program version"); return 1; } *version = pkt_ver; ap_req_data.data = msg + 6; ap_req_data.length = ap_req_len; ret = krb5_rd_req (context, auth_context, &ap_req_data, NULL, keytab, NULL, ticket); if (ret) { krb5_warn (context, ret, "krb5_rd_req"); reply_error (NULL, s, sa, sa_size, ret, 3, "Authentication failed"); return 1; } /* verify realm and principal */ for (r = realms; *r != NULL; r++) { krb5_principal principal; krb5_boolean same; ret = krb5_make_principal (context, &principal, *r, "kadmin", "changepw", NULL); if (ret) krb5_err (context, 1, ret, "krb5_make_principal"); same = krb5_principal_compare(context, principal, (*ticket)->server); krb5_free_principal(context, principal); if (same == TRUE) break; } if (*r == NULL) { char *str; krb5_unparse_name(context, (*ticket)->server, &str); krb5_warnx (context, "client used not valid principal %s", str); free(str); reply_error (NULL, s, sa, sa_size, ret, 1, "Bad request"); goto out; } if (strcmp((*ticket)->server->realm, (*ticket)->client->realm) != 0) { krb5_warnx (context, "server realm (%s) not same a client realm (%s)", (*ticket)->server->realm, (*ticket)->client->realm); reply_error ((*ticket)->server->realm, s, sa, sa_size, ret, 1, "Bad request"); goto out; } if (!(*ticket)->ticket.flags.initial) { krb5_warnx (context, "initial flag not set"); reply_error ((*ticket)->server->realm, s, sa, sa_size, ret, 1, "Bad request"); goto out; } krb_priv_data.data = msg + 6 + ap_req_len; krb_priv_data.length = len - 6 - ap_req_len; /* * Only enforce client addresses on on tickets with addresses. If * its addressless, we are guessing its behind NAT and really * can't know this information. */ if ((*ticket)->ticket.caddr && (*ticket)->ticket.caddr->len > 0) { ret = krb5_auth_con_setaddrs (context, *auth_context, NULL, client_addr); if (ret) { krb5_warn (context, ret, "krb5_auth_con_setaddr(this)"); goto out; } } ret = krb5_rd_priv (context, *auth_context, &krb_priv_data, out_data, NULL); if (ret) { krb5_warn (context, ret, "krb5_rd_priv"); reply_error ((*ticket)->server->realm, s, sa, sa_size, ret, 3, "Bad request"); goto out; } return 0; out: krb5_free_ticket (context, *ticket); ticket = NULL; return 1; }
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; }
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); }
/* * Try krb5 authentication. server_user is passed for logging purposes * only, in auth is received ticket, in client is returned principal * from the ticket */ int auth_krb5(Authctxt *authctxt, krb5_data *auth, char **client) { krb5_error_code problem; krb5_principal server; krb5_data reply; krb5_ticket *ticket; int fd, ret; ret = 0; server = NULL; ticket = NULL; reply.length = 0; problem = krb5_init(authctxt); if (problem) goto err; problem = krb5_auth_con_init(authctxt->krb5_ctx, &authctxt->krb5_auth_ctx); if (problem) goto err; fd = packet_get_connection_in(); #ifdef HEIMDAL problem = krb5_auth_con_setaddrs_from_fd(authctxt->krb5_ctx, authctxt->krb5_auth_ctx, &fd); #else problem = krb5_auth_con_genaddrs(authctxt->krb5_ctx, authctxt->krb5_auth_ctx,fd, KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR | KRB5_AUTH_CONTEXT_GENERATE_LOCAL_FULL_ADDR); #endif if (problem) goto err; problem = krb5_sname_to_principal(authctxt->krb5_ctx, NULL, NULL , KRB5_NT_SRV_HST, &server); if (problem) goto err; problem = krb5_rd_req(authctxt->krb5_ctx, &authctxt->krb5_auth_ctx, auth, server, NULL, NULL, &ticket); if (problem) goto err; #ifdef HEIMDAL problem = krb5_copy_principal(authctxt->krb5_ctx, ticket->client, &authctxt->krb5_user); #else problem = krb5_copy_principal(authctxt->krb5_ctx, ticket->enc_part2->client, &authctxt->krb5_user); #endif if (problem) goto err; /* if client wants mutual auth */ problem = krb5_mk_rep(authctxt->krb5_ctx, authctxt->krb5_auth_ctx, &reply); if (problem) goto err; /* Check .k5login authorization now. */ if (!krb5_kuserok(authctxt->krb5_ctx, authctxt->krb5_user, authctxt->pw->pw_name)) goto err; if (client) krb5_unparse_name(authctxt->krb5_ctx, authctxt->krb5_user, client); packet_start(SSH_SMSG_AUTH_KERBEROS_RESPONSE); packet_put_string((char *) reply.data, reply.length); packet_send(); packet_write_wait(); ret = 1; err: if (server) krb5_free_principal(authctxt->krb5_ctx, server); if (ticket) krb5_free_ticket(authctxt->krb5_ctx, ticket); if (reply.length) xfree(reply.data); if (problem) { if (authctxt->krb5_ctx != NULL) debug("Kerberos v5 authentication failed: %s", krb5_get_err_text(authctxt->krb5_ctx, problem)); else debug("Kerberos v5 authentication failed: %d", problem); } return (ret); }
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; }
int ksm_process_in_msg(struct snmp_secmod_incoming_params *parms) { long temp; krb5_cksumtype cksumtype; krb5_auth_context auth_context = NULL; krb5_error_code retcode; krb5_checksum checksum; krb5_data ap_req, ivector; krb5_flags flags; krb5_keyblock *subkey = NULL; #ifdef MIT_NEW_CRYPTO krb5_data input, output; krb5_boolean valid; krb5_enc_data in_crypt; #else /* MIT_NEW_CRYPTO */ krb5_encrypt_block eblock; #endif /* MIT_NEW_CRYPTO */ krb5_ticket *ticket = NULL; int retval = SNMPERR_SUCCESS, response = 0; size_t length = parms->wholeMsgLen - (u_int) (parms->secParams - parms->wholeMsg); u_char *current = parms->secParams, type; size_t cksumlength, blocksize; long hint; char *cname; struct ksm_secStateRef *ksm_state; struct ksm_cache_entry *entry; DEBUGMSGTL(("ksm", "Processing has begun\n")); checksum.contents = NULL; ap_req.data = NULL; ivector.length = 0; ivector.data = NULL; /* * First, parse the security parameters (because we need the subkey inside * of the ticket to do anything */ if ((current = asn_parse_sequence(current, &length, &type, (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), "ksm first octet")) == NULL) { DEBUGMSGTL(("ksm", "Initial security paramter parsing failed\n")); retval = SNMPERR_ASN_PARSE_ERR; goto error; } if ((current = asn_parse_sequence(current, &length, &type, (ASN_SEQUENCE | ASN_CONSTRUCTOR), "ksm sequence")) == NULL) { DEBUGMSGTL(("ksm", "Security parameter sequence parsing failed\n")); retval = SNMPERR_ASN_PARSE_ERR; goto error; } if ((current = asn_parse_int(current, &length, &type, &temp, sizeof(temp))) == NULL) { DEBUGMSGTL(("ksm", "Security parameter checksum type parsing" "failed\n")); retval = SNMPERR_ASN_PARSE_ERR; goto error; } cksumtype = temp; #ifdef MIT_NEW_CRYPTO if (!krb5_c_valid_cksumtype(cksumtype)) { DEBUGMSGTL(("ksm", "Invalid checksum type (%d)\n", cksumtype)); retval = SNMPERR_KRB5; snmp_set_detail("Invalid checksum type"); goto error; } if (!krb5_c_is_keyed_cksum(cksumtype)) { DEBUGMSGTL(("ksm", "Checksum type %d is not a keyed checksum\n", cksumtype)); snmp_set_detail("Checksum is not a keyed checksum"); retval = SNMPERR_KRB5; goto error; } if (!krb5_c_is_coll_proof_cksum(cksumtype)) { DEBUGMSGTL(("ksm", "Checksum type %d is not a collision-proof " "checksum\n", cksumtype)); snmp_set_detail("Checksum is not a collision-proof checksum"); retval = SNMPERR_KRB5; goto error; } #else /* ! MIT_NEW_CRYPTO */ if (!valid_cksumtype(cksumtype)) { DEBUGMSGTL(("ksm", "Invalid checksum type (%d)\n", cksumtype)); retval = SNMPERR_KRB5; snmp_set_detail("Invalid checksum type"); goto error; } if (!is_keyed_cksum(cksumtype)) { DEBUGMSGTL(("ksm", "Checksum type %d is not a keyed checksum\n", cksumtype)); snmp_set_detail("Checksum is not a keyed checksum"); retval = SNMPERR_KRB5; goto error; } if (!is_coll_proof_cksum(cksumtype)) { DEBUGMSGTL(("ksm", "Checksum type %d is not a collision-proof " "checksum\n", cksumtype)); snmp_set_detail("Checksum is not a collision-proof checksum"); retval = SNMPERR_KRB5; goto error; } #endif /* MIT_NEW_CRYPTO */ checksum.checksum_type = cksumtype; cksumlength = length; if ((current = asn_parse_sequence(current, &cksumlength, &type, (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), "ksm checksum")) == NULL) { DEBUGMSGTL(("ksm", "Security parameter checksum parsing failed\n")); retval = SNMPERR_ASN_PARSE_ERR; goto error; } checksum.contents = malloc(cksumlength); if (!checksum.contents) { DEBUGMSGTL(("ksm", "Unable to malloc %d bytes for checksum.\n", cksumlength)); retval = SNMPERR_MALLOC; goto error; } memcpy(checksum.contents, current, cksumlength); checksum.length = cksumlength; checksum.checksum_type = cksumtype; /* * Zero out the checksum so the validation works correctly */ memset(current, 0, cksumlength); current += cksumlength; length = parms->wholeMsgLen - (u_int) (current - parms->wholeMsg); if ((current = asn_parse_sequence(current, &length, &type, (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), "ksm ap_req")) == NULL) { DEBUGMSGTL(("ksm", "KSM security parameter AP_REQ/REP parsing " "failed\n")); retval = SNMPERR_ASN_PARSE_ERR; goto error; } ap_req.length = length; ap_req.data = malloc(length); if (!ap_req.data) { DEBUGMSGTL(("ksm", "KSM unable to malloc %d bytes for AP_REQ/REP.\n", length)); retval = SNMPERR_MALLOC; goto error; } memcpy(ap_req.data, current, length); current += length; length = parms->wholeMsgLen - (u_int) (current - parms->wholeMsg); if ((current = asn_parse_int(current, &length, &type, &hint, sizeof(hint))) == NULL) { DEBUGMSGTL(("ksm", "KSM security parameter hint parsing failed\n")); retval = SNMPERR_ASN_PARSE_ERR; goto error; } /* * Okay! We've got it all! Now try decoding the damn ticket. * * But of course there's a WRINKLE! We need to figure out if we're * processing a AP_REQ or an AP_REP. How do we do that? We're going * to cheat, and look at the first couple of bytes (which is what * the Kerberos library routines do anyway). * * If there are ever new Kerberos message formats, we'll need to fix * this here. * * If it's a _response_, then we need to get the auth_context * from our cache. */ if (ap_req.length && (ap_req.data[0] == 0x6e || ap_req.data[0] == 0x4e)) { /* * We need to initalize the authorization context, and set the * replay cache in it (and initialize the replay cache if we * haven't already */ retcode = krb5_auth_con_init(kcontext, &auth_context); if (retcode) { DEBUGMSGTL(("ksm", "krb5_auth_con_init failed: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; snmp_set_detail(error_message(retcode)); goto error; } if (!rcache) { krb5_data server; server.data = "host"; server.length = strlen(server.data); retcode = krb5_get_server_rcache(kcontext, &server, &rcache); if (retcode) { DEBUGMSGTL(("ksm", "krb5_get_server_rcache failed: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; snmp_set_detail(error_message(retcode)); goto error; } } retcode = krb5_auth_con_setrcache(kcontext, auth_context, rcache); if (retcode) { DEBUGMSGTL(("ksm", "krb5_auth_con_setrcache failed: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; snmp_set_detail(error_message(retcode)); goto error; } retcode = krb5_rd_req(kcontext, &auth_context, &ap_req, NULL, keytab, &flags, &ticket); krb5_auth_con_setrcache(kcontext, auth_context, NULL); if (retcode) { DEBUGMSGTL(("ksm", "krb5_rd_req() failed: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; snmp_set_detail(error_message(retcode)); goto error; } retcode = krb5_unparse_name(kcontext, ticket->enc_part2->client, &cname); if (retcode == 0) { DEBUGMSGTL(("ksm", "KSM authenticated principal name: %s\n", cname)); free(cname); } /* * Check to make sure AP_OPTS_MUTUAL_REQUIRED was set */ if (!(flags & AP_OPTS_MUTUAL_REQUIRED)) { DEBUGMSGTL(("ksm", "KSM MUTUAL_REQUIRED not set in request!\n")); retval = SNMPERR_KRB5; snmp_set_detail("MUTUAL_REQUIRED not set in message"); goto error; } retcode = krb5_auth_con_getremotesubkey(kcontext, auth_context, &subkey); if (retcode) { DEBUGMSGTL(("ksm", "KSM remote subkey retrieval failed: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; snmp_set_detail(error_message(retcode)); goto error; } } else if (ap_req.length && (ap_req.data[0] == 0x6f || ap_req.data[0] == 0x4f)) { /* * Looks like a response; let's see if we've got that auth_context * in our cache. */ krb5_ap_rep_enc_part *repl = NULL; response = 1; entry = ksm_get_cache(parms->pdu->msgid); if (!entry) { DEBUGMSGTL(("ksm", "KSM: Unable to find auth_context for PDU with " "message ID of %ld\n", parms->pdu->msgid)); retval = SNMPERR_KRB5; goto error; } auth_context = entry->auth_context; /* * In that case, let's call the rd_rep function */ retcode = krb5_rd_rep(kcontext, auth_context, &ap_req, &repl); if (repl) krb5_free_ap_rep_enc_part(kcontext, repl); if (retcode) { DEBUGMSGTL(("ksm", "KSM: krb5_rd_rep() failed: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; goto error; } DEBUGMSGTL(("ksm", "KSM: krb5_rd_rep() decoded successfully.\n")); retcode = krb5_auth_con_getlocalsubkey(kcontext, auth_context, &subkey); if (retcode) { DEBUGMSGTL(("ksm", "Unable to retrieve local subkey: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; snmp_set_detail("Unable to retrieve local subkey"); goto error; } } else { DEBUGMSGTL(("ksm", "Unknown Kerberos message type (%02x)\n", ap_req.data[0])); retval = SNMPERR_KRB5; snmp_set_detail("Unknown Kerberos message type"); goto error; } #ifdef MIT_NEW_CRYPTO input.data = (char *) parms->wholeMsg; input.length = parms->wholeMsgLen; retcode = krb5_c_verify_checksum(kcontext, subkey, KSM_KEY_USAGE_CHECKSUM, &input, &checksum, &valid); #else /* MIT_NEW_CRYPTO */ retcode = krb5_verify_checksum(kcontext, cksumtype, &checksum, parms->wholeMsg, parms->wholeMsgLen, (krb5_pointer) subkey->contents, subkey->length); #endif /* MIT_NEW_CRYPTO */ if (retcode) { DEBUGMSGTL(("ksm", "KSM checksum verification failed: %s\n", error_message(retcode))); retval = SNMPERR_KRB5; snmp_set_detail(error_message(retcode)); goto error; } /* * Don't ask me why they didn't simply return an error, but we have * to check to see if "valid" is false. */ #ifdef MIT_NEW_CRYPTO if (!valid) { DEBUGMSGTL(("ksm", "Computed checksum did not match supplied " "checksum!\n")); retval = SNMPERR_KRB5; snmp_set_detail ("Computed checksum did not match supplied checksum"); goto error; } #endif /* MIT_NEW_CRYPTO */ /* * Handle an encrypted PDU. Note that it's an OCTET_STRING of the * output of whatever Kerberos cryptosystem you're using (defined by * the encryption type). Note that this is NOT the EncryptedData * sequence - it's what goes in the "cipher" field of EncryptedData. */ if (parms->secLevel == SNMP_SEC_LEVEL_AUTHPRIV) { if ((current = asn_parse_sequence(current, &length, &type, (ASN_UNIVERSAL | ASN_PRIMITIVE | ASN_OCTET_STR), "ksm pdu")) == NULL) { DEBUGMSGTL(("ksm", "KSM sPDU octet decoding failed\n")); retval = SNMPERR_ASN_PARSE_ERR; goto error; } /* * The PDU is now pointed at by "current", and the length is in * "length". */ DEBUGMSGTL(("ksm", "KSM starting sPDU decode\n")); /* * We need to set up a blank initialization vector for the decryption. * Use a block of all zero's (which is dependent on the block size * of the encryption method). */ #ifdef MIT_NEW_CRYPTO retcode = krb5_c_block_size(kcontext, subkey->enctype, &blocksize); if (retcode) { DEBUGMSGTL(("ksm", "Unable to determine crypto block size: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } #else /* MIT_NEW_CRYPTO */ blocksize = krb5_enctype_array[subkey->enctype]->system->block_length; #endif /* MIT_NEW_CRYPTO */ ivector.data = malloc(blocksize); if (!ivector.data) { DEBUGMSGTL(("ksm", "Unable to allocate %d bytes for ivector\n", blocksize)); retval = SNMPERR_MALLOC; goto error; } ivector.length = blocksize; memset(ivector.data, 0, blocksize); #ifndef MIT_NEW_CRYPTO krb5_use_enctype(kcontext, &eblock, subkey->enctype); retcode = krb5_process_key(kcontext, &eblock, subkey); if (retcode) { DEBUGMSGTL(("ksm", "KSM key post-processing failed: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } #endif /* !MIT_NEW_CRYPTO */ if (length > *parms->scopedPduLen) { DEBUGMSGTL(("ksm", "KSM not enough room - have %d bytes to " "decrypt but only %d bytes available\n", length, *parms->scopedPduLen)); retval = SNMPERR_TOO_LONG; #ifndef MIT_NEW_CRYPTO krb5_finish_key(kcontext, &eblock); #endif /* ! MIT_NEW_CRYPTO */ goto error; } #ifdef MIT_NEW_CRYPTO in_crypt.ciphertext.data = (char *) current; in_crypt.ciphertext.length = length; in_crypt.enctype = subkey->enctype; output.data = (char *) *parms->scopedPdu; output.length = *parms->scopedPduLen; retcode = krb5_c_decrypt(kcontext, subkey, KSM_KEY_USAGE_ENCRYPTION, &ivector, &in_crypt, &output); #else /* MIT_NEW_CRYPTO */ retcode = krb5_decrypt(kcontext, (krb5_pointer) current, *parms->scopedPdu, length, &eblock, ivector.data); krb5_finish_key(kcontext, &eblock); #endif /* MIT_NEW_CRYPTO */ if (retcode) { DEBUGMSGTL(("ksm", "Decryption failed: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } *parms->scopedPduLen = length; } else { /* * Clear PDU */ *parms->scopedPdu = current; *parms->scopedPduLen = parms->wholeMsgLen - (current - parms->wholeMsg); } /* * A HUGE GROSS HACK */ *parms->maxSizeResponse = parms->maxMsgSize - 200; DEBUGMSGTL(("ksm", "KSM processing complete\n")); /* * Set the secName to the right value (a hack for now). But that's * only used for when we're processing a request, not a response. */ if (!response) { retcode = krb5_unparse_name(kcontext, ticket->enc_part2->client, &cname); if (retcode) { DEBUGMSGTL(("ksm", "KSM krb5_unparse_name failed: %s\n", error_message(retcode))); snmp_set_detail(error_message(retcode)); retval = SNMPERR_KRB5; goto error; } if (strlen(cname) > *parms->secNameLen + 1) { DEBUGMSGTL(("ksm", "KSM: Principal length (%d) is too long (%d)\n", strlen(cname), parms->secNameLen)); retval = SNMPERR_TOO_LONG; free(cname); goto error; } strcpy(parms->secName, cname); *parms->secNameLen = strlen(cname); free(cname); /* * Also, if we're not a response, keep around our auth_context so we * can encode the reply message correctly */ ksm_state = SNMP_MALLOC_STRUCT(ksm_secStateRef); if (!ksm_state) { DEBUGMSGTL(("ksm", "KSM unable to malloc memory for " "ksm_secStateRef\n")); retval = SNMPERR_MALLOC; goto error; } ksm_state->auth_context = auth_context; auth_context = NULL; ksm_state->cksumtype = cksumtype; *parms->secStateRef = ksm_state; } else { /* * We _still_ have to set the secName in process_in_msg(). Do * that now with what we were passed in before (we cached it, * remember?) */ memcpy(parms->secName, entry->secName, entry->secNameLen); *parms->secNameLen = entry->secNameLen; } /* * Just in case */ parms->secEngineID = (u_char *) ""; *parms->secEngineIDLen = 0; auth_context = NULL; /* So we don't try to free it on success */ error: if (retval == SNMPERR_ASN_PARSE_ERR && snmp_increment_statistic(STAT_SNMPINASNPARSEERRS) == 0) DEBUGMSGTL(("ksm", "Failed to increment statistics.\n")); if (subkey) krb5_free_keyblock(kcontext, subkey); if (checksum.contents) free(checksum.contents); if (ivector.data) free(ivector.data); if (ticket) krb5_free_ticket(kcontext, ticket); if (!response && auth_context) krb5_auth_con_free(kcontext, auth_context); if (ap_req.data) free(ap_req.data); return retval; }
/* * 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; }
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; }
/* * Try krb5 authentication. server_user is passed for logging purposes * only, in auth is received ticket, in client is returned principal * from the ticket */ int auth_krb5(Authctxt *authctxt, krb5_data *auth, char **client, krb5_data *reply) { krb5_error_code problem; krb5_principal server; krb5_ticket *ticket; int fd, ret; const char *errtxt; ret = 0; server = NULL; ticket = NULL; reply->length = 0; problem = krb5_init(authctxt); if (problem) goto err; problem = krb5_auth_con_init(authctxt->krb5_ctx, &authctxt->krb5_auth_ctx); if (problem) goto err; fd = packet_get_connection_in(); problem = krb5_auth_con_setaddrs_from_fd(authctxt->krb5_ctx, authctxt->krb5_auth_ctx, &fd); if (problem) goto err; problem = krb5_sname_to_principal(authctxt->krb5_ctx, NULL, NULL, KRB5_NT_SRV_HST, &server); if (problem) goto err; problem = krb5_rd_req(authctxt->krb5_ctx, &authctxt->krb5_auth_ctx, auth, server, NULL, NULL, &ticket); if (problem) goto err; problem = krb5_copy_principal(authctxt->krb5_ctx, ticket->client, &authctxt->krb5_user); if (problem) goto err; /* if client wants mutual auth */ problem = krb5_mk_rep(authctxt->krb5_ctx, authctxt->krb5_auth_ctx, reply); if (problem) goto err; /* Check .k5login authorization now. */ if (!krb5_kuserok(authctxt->krb5_ctx, authctxt->krb5_user, authctxt->pw->pw_name)) goto err; if (client) krb5_unparse_name(authctxt->krb5_ctx, authctxt->krb5_user, client); ret = 1; err: if (server) krb5_free_principal(authctxt->krb5_ctx, server); if (ticket) krb5_free_ticket(authctxt->krb5_ctx, ticket); if (!ret && reply->length) { free(reply->data); memset(reply, 0, sizeof(*reply)); } if (problem) { errtxt = NULL; if (authctxt->krb5_ctx != NULL) errtxt = krb5_get_error_message(authctxt->krb5_ctx, problem); if (errtxt != NULL) { debug("Kerberos v5 authentication failed: %s", errtxt); krb5_free_error_message(authctxt->krb5_ctx, errtxt); } else debug("Kerberos v5 authentication failed: %d", problem); } return (ret); }
static int verify_krb5_tgt(krb5_context context, rlm_krb5_t *inst, const char *user, krb5_ccache ccache) { int rcode; int ret; char phost[BUFSIZ]; krb5_principal princ; krb5_keyblock *keyblock = 0; krb5_data packet, *server; krb5_auth_context auth_context = NULL; krb5_keytab keytab; char service[SERVICE_NAME_LEN] = "host"; char *server_name = NULL; char *keytab_name; /* krb5_kt_read_service_key lacks const qualifier */ memcpy(&keytab_name, &inst->keytab, sizeof(keytab_name)); if (inst->service_princ != NULL) { server_name = strchr(inst->service_princ, '/'); if (server_name != NULL) { *server_name = '\0'; } strlcpy(service, inst->service_princ, sizeof(service)); if (server_name != NULL) { *server_name = '/'; server_name++; } } memset(&packet, 0, sizeof packet); ret = krb5_sname_to_principal(context, server_name, service, KRB5_NT_SRV_HST, &princ); if (ret) { radlog(L_DBG, "rlm_krb5: [%s] krb5_sname_to_principal failed: %s", user, error_message(ret)); return RLM_MODULE_REJECT; } server = krb5_princ_component(c, princ, 1); if (!server) { radlog(L_DBG, "rlm_krb5: [%s] krb5_princ_component failed.", user); return RLM_MODULE_REJECT; } strlcpy(phost, server->data, sizeof(phost)); /* * Do we have host/<host> keys? * (use default/configured keytab, kvno IGNORE_VNO to get the * first match, and enctype is currently ignored anyhow.) */ ret = krb5_kt_read_service_key(context, keytab_name, princ, 0, ENCTYPE_DES_CBC_MD5, &keyblock); if (ret) { /* Keytab or service key does not exist */ radlog(L_DBG, "rlm_krb5: verify_krb_v5_tgt: host key not found : %s", error_message(ret)); return RLM_MODULE_OK; } if (keyblock) krb5_free_keyblock(context, keyblock); /* * Talk to the kdc and construct the ticket. */ ret = krb5_build_auth_context(inst, context, &auth_context); if (ret) { radlog(L_DBG, "rlm_krb5: [%s] krb5_build_auth_context() failed: %s", user, error_message(ret)); rcode = RLM_MODULE_REJECT; goto cleanup; } ret = krb5_mk_req(context, &auth_context, 0, service, phost, NULL, ccache, &packet); if (auth_context) { krb5_auth_con_free(context, auth_context); auth_context = NULL; /* setup for rd_req */ } if (ret) { radlog(L_DBG, "rlm_krb5: [%s] krb5_mk_req() failed: %s", user, error_message(ret)); rcode = RLM_MODULE_REJECT; goto cleanup; } if (keytab_name != NULL) { ret = krb5_kt_resolve(context, keytab_name, &keytab); } if (keytab_name == NULL || ret) { ret = krb5_kt_default(context, &keytab); } /* Hmm? The keytab was just fine a second ago! */ if (ret) { radlog(L_AUTH, "rlm_krb5: [%s] krb5_kt_resolve failed: %s", user, error_message(ret)); rcode = RLM_MODULE_REJECT; goto cleanup; } /* Try to use the ticket. */ ret = krb5_build_auth_context(inst, context, &auth_context); if (ret) { radlog(L_DBG, "rlm_krb5: [%s] krb5_build_auth_context() failed: %s", user, error_message(ret)); rcode = RLM_MODULE_REJECT; goto cleanup; } ret = krb5_rd_req(context, &auth_context, &packet, princ, keytab, NULL, NULL); if (auth_context) krb5_auth_con_free(context, auth_context); krb5_kt_close(context, keytab); if (ret) { radlog(L_AUTH, "rlm_krb5: [%s] krb5_rd_req() failed: %s", user, error_message(ret)); rcode = RLM_MODULE_REJECT; } else { rcode = RLM_MODULE_OK; } cleanup: if (packet.data) { krb5_free_data_contents(context, &packet); } return rcode; }
/** * Perform the server side of the sendauth protocol like krb5_recvauth(), but support * a user-specified callback, \a match_appl_version, to perform the match of the application * version \a match_data. */ KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_recvauth_match_version(krb5_context context, krb5_auth_context *auth_context, krb5_pointer p_fd, krb5_boolean (*match_appl_version)(const void *, const char*), const void *match_data, krb5_principal server, int32_t flags, krb5_keytab keytab, krb5_ticket **ticket) { krb5_error_code ret; const char *version = KRB5_SENDAUTH_VERSION; char her_version[sizeof(KRB5_SENDAUTH_VERSION)]; char *her_appl_version; uint32_t len; u_char repl; krb5_data data; krb5_flags ap_options; ssize_t n; /* * If there are no addresses in auth_context, get them from `fd'. */ if (*auth_context == NULL) { ret = krb5_auth_con_init (context, auth_context); if (ret) return ret; } ret = krb5_auth_con_setaddrs_from_fd (context, *auth_context, p_fd); if (ret) return ret; /* * Expect SENDAUTH protocol version. */ if(!(flags & KRB5_RECVAUTH_IGNORE_VERSION)) { n = krb5_net_read (context, p_fd, &len, 4); if (n < 0) { ret = errno; krb5_set_error_message(context, ret, "read: %s", strerror(ret)); return ret; } if (n == 0) { krb5_set_error_message(context, KRB5_SENDAUTH_BADAUTHVERS, N_("Failed to receive sendauth data", "")); return KRB5_SENDAUTH_BADAUTHVERS; } len = ntohl(len); if (len != sizeof(her_version) || krb5_net_read (context, p_fd, her_version, len) != len || strncmp (version, her_version, len)) { repl = 1; krb5_net_write (context, p_fd, &repl, 1); krb5_clear_error_message (context); return KRB5_SENDAUTH_BADAUTHVERS; } } /* * Expect application protocol version. */ n = krb5_net_read (context, p_fd, &len, 4); if (n < 0) { ret = errno; krb5_set_error_message(context, ret, "read: %s", strerror(ret)); return ret; } if (n == 0) { krb5_clear_error_message (context); return KRB5_SENDAUTH_BADAPPLVERS; } len = ntohl(len); her_appl_version = malloc (len); if (her_appl_version == NULL) { repl = 2; krb5_net_write (context, p_fd, &repl, 1); krb5_set_error_message(context, ENOMEM, N_("malloc: out of memory", "")); return ENOMEM; } if (krb5_net_read (context, p_fd, her_appl_version, len) != len || !(*match_appl_version)(match_data, her_appl_version)) { repl = 2; krb5_net_write (context, p_fd, &repl, 1); krb5_set_error_message(context, KRB5_SENDAUTH_BADAPPLVERS, N_("wrong sendauth application version (%s)", ""), her_appl_version); free (her_appl_version); return KRB5_SENDAUTH_BADAPPLVERS; } free (her_appl_version); /* * Send OK. */ repl = 0; if (krb5_net_write (context, p_fd, &repl, 1) != 1) { ret = errno; krb5_set_error_message(context, ret, "write: %s", strerror(ret)); return ret; } /* * Until here, the fields in the message were in cleartext and unauthenticated. * From now on, Kerberos kicks in. */ /* * Expect AP_REQ. */ krb5_data_zero (&data); ret = krb5_read_message (context, p_fd, &data); if (ret) return ret; ret = krb5_rd_req (context, auth_context, &data, server, keytab, &ap_options, ticket); krb5_data_free (&data); if (ret) { krb5_data error_data; krb5_error_code ret2; ret2 = krb5_mk_error (context, ret, NULL, NULL, NULL, server, NULL, NULL, &error_data); if (ret2 == 0) { krb5_write_message (context, p_fd, &error_data); krb5_data_free (&error_data); } return ret; } /* * Send OK. */ len = 0; if (krb5_net_write (context, p_fd, &len, 4) != 4) { ret = errno; krb5_set_error_message(context, ret, "write: %s", strerror(ret)); krb5_free_ticket(context, *ticket); *ticket = NULL; return ret; } /* * If client requires mutual authentication, send AP_REP. */ if (ap_options & AP_OPTS_MUTUAL_REQUIRED) { ret = krb5_mk_rep (context, *auth_context, &data); if (ret) { krb5_free_ticket(context, *ticket); *ticket = NULL; return ret; } ret = krb5_write_message (context, p_fd, &data); if (ret) { krb5_free_ticket(context, *ticket); *ticket = NULL; return ret; } krb5_data_free (&data); } return 0; }
Code_t ZCheckSrvAuthentication(ZNotice_t *notice, struct sockaddr_in *from, char *realm) { #ifdef HAVE_KRB5 unsigned char *authbuf; krb5_principal princ; krb5_data packet; krb5_ticket *tkt; char *name; krb5_error_code result; krb5_principal server; krb5_keytab keytabid = 0; krb5_auth_context authctx; krb5_keyblock *keyblock; krb5_enctype enctype; krb5_cksumtype cksumtype; krb5_data cksumbuf; int valid; char *cksum0_base, *cksum1_base = NULL, *cksum2_base; char *x; unsigned char *asn1_data, *key_data, *cksum_data; int asn1_len, key_len, cksum0_len = 0, cksum1_len = 0, cksum2_len = 0; KRB5_AUTH_CON_FLAGS_TYPE acflags; #ifdef KRB5_AUTH_CON_GETAUTHENTICATOR_TAKES_DOUBLE_POINTER krb5_authenticator *authenticator; #define KRB5AUTHENT authenticator #else krb5_authenticator authenticator; #define KRB5AUTHENT &authenticator #endif int len; char *sender; char rlmprincipal[MAX_PRINCIPAL_SIZE]; if (!notice->z_auth) return ZAUTH_NO; /* Check for bogus authentication data length. */ if (notice->z_authent_len <= 0) { syslog(LOG_DEBUG, "ZCheckSrvAuthentication: bogus authenticator length"); return ZAUTH_FAILED; } #ifdef HAVE_KRB4 if (notice->z_ascii_authent[0] != 'Z' && realm == NULL) return ZCheckAuthentication4(notice, from); #endif len = strlen(notice->z_ascii_authent)+1; authbuf = malloc(len); /* Read in the authentication data. */ if (ZReadZcode((unsigned char *)notice->z_ascii_authent, authbuf, len, &len) == ZERR_BADFIELD) { syslog(LOG_DEBUG, "ZCheckSrvAuthentication: ZReadZcode: Improperly formatted field"); return ZAUTH_FAILED; } if (realm == NULL) { sender = notice->z_sender; } else { (void) snprintf(rlmprincipal, MAX_PRINCIPAL_SIZE, "%s/%s@%s", SERVER_SERVICE, SERVER_INSTANCE, realm); sender = rlmprincipal; } packet.length = len; packet.data = (char *)authbuf; result = krb5_kt_resolve(Z_krb5_ctx, keytab_file, &keytabid); if (result) { free(authbuf); syslog(LOG_DEBUG, "ZCheckSrvAuthentication: krb5_kt_resolve: %s", error_message(result)); return ZAUTH_FAILED; } /* HOLDING: authbuf, keytabid */ /* Create the auth context */ result = krb5_auth_con_init(Z_krb5_ctx, &authctx); if (result) { krb5_kt_close(Z_krb5_ctx, keytabid); free(authbuf); syslog(LOG_DEBUG, "ZCheckSrvAuthentication: krb5_auth_con_init: %s", error_message(result)); return ZAUTH_FAILED; } /* HOLDING: authbuf, keytabid, authctx */ result = krb5_auth_con_getflags(Z_krb5_ctx, authctx, &acflags); if (result) { krb5_auth_con_free(Z_krb5_ctx, authctx); krb5_kt_close(Z_krb5_ctx, keytabid); free(authbuf); syslog(LOG_DEBUG, "ZCheckSrvAuthentication: krb5_auth_con_getflags: %s", error_message(result)); return ZAUTH_FAILED; } acflags &= ~KRB5_AUTH_CONTEXT_DO_TIME; result = krb5_auth_con_setflags(Z_krb5_ctx, authctx, acflags); if (result) { krb5_auth_con_free(Z_krb5_ctx, authctx); krb5_kt_close(Z_krb5_ctx, keytabid); free(authbuf); syslog(LOG_DEBUG, "ZCheckSrvAuthentication: krb5_auth_con_setflags: %s", error_message(result)); return ZAUTH_FAILED; } result = krb5_build_principal(Z_krb5_ctx, &server, strlen(__Zephyr_realm), __Zephyr_realm, SERVER_SERVICE, SERVER_INSTANCE, NULL); if (!result) { result = krb5_rd_req(Z_krb5_ctx, &authctx, &packet, server, keytabid, NULL, &tkt); krb5_free_principal(Z_krb5_ctx, server); } krb5_kt_close(Z_krb5_ctx, keytabid); /* HOLDING: authbuf, authctx */ if (result) { if (result == KRB5KRB_AP_ERR_REPEAT) syslog(LOG_DEBUG, "ZCheckSrvAuthentication: k5 auth failed: %s", error_message(result)); else syslog(LOG_WARNING,"ZCheckSrvAuthentication: k5 auth failed: %s", error_message(result)); free(authbuf); krb5_auth_con_free(Z_krb5_ctx, authctx); return ZAUTH_FAILED; } /* HOLDING: authbuf, authctx, tkt */ if (tkt == 0 || !Z_tktprincp(tkt)) { if (tkt) krb5_free_ticket(Z_krb5_ctx, tkt); free(authbuf); krb5_auth_con_free(Z_krb5_ctx, authctx); syslog(LOG_WARNING, "ZCheckSrvAuthentication: No Ticket"); return ZAUTH_FAILED; } princ = Z_tktprinc(tkt); if (princ == 0) { krb5_free_ticket(Z_krb5_ctx, tkt); free(authbuf); krb5_auth_con_free(Z_krb5_ctx, authctx); syslog(LOG_WARNING, "ZCheckSrvAuthentication: No... Ticket?"); return ZAUTH_FAILED; } /* HOLDING: authbuf, authctx, tkt */ result = krb5_unparse_name(Z_krb5_ctx, princ, &name); if (result) { syslog(LOG_WARNING, "ZCheckSrvAuthentication: krb5_unparse_name failed: %s", error_message(result)); free(authbuf); krb5_auth_con_free(Z_krb5_ctx, authctx); krb5_free_ticket(Z_krb5_ctx, tkt); return ZAUTH_FAILED; } krb5_free_ticket(Z_krb5_ctx, tkt); /* HOLDING: authbuf, authctx, name */ if (strcmp(name, sender)) { syslog(LOG_WARNING, "ZCheckSrvAuthentication: name mismatch: '%s' vs '%s'", name, sender); krb5_auth_con_free(Z_krb5_ctx, authctx); #ifdef HAVE_KRB5_FREE_UNPARSED_NAME krb5_free_unparsed_name(Z_krb5_ctx, name); #else free(name); #endif free(authbuf); return ZAUTH_FAILED; } #ifdef HAVE_KRB5_FREE_UNPARSED_NAME krb5_free_unparsed_name(Z_krb5_ctx, name); #else free(name); #endif free(authbuf); /* HOLDING: authctx */ /* Get an authenticator so we can get the keyblock */ result = krb5_auth_con_getauthenticator (Z_krb5_ctx, authctx, &authenticator); if (result) { krb5_auth_con_free(Z_krb5_ctx, authctx); syslog(LOG_WARNING, "ZCheckSrvAuthentication: krb5_auth_con_getauthenticator failed: %s", error_message(result)); return ZAUTH_FAILED; } /* HOLDING: authctx, authenticator */ result = krb5_auth_con_getkey(Z_krb5_ctx, authctx, &keyblock); if (result) { krb5_auth_con_free(Z_krb5_ctx, authctx); krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT); syslog(LOG_WARNING, "ZCheckSrvAuthentication: krb5_auth_con_getkey failed: %s", error_message(result)); return (ZAUTH_FAILED); } /* HOLDING: authctx, authenticator, keyblock */ /* Figure out what checksum type to use */ key_data = Z_keydata(keyblock); key_len = Z_keylen(keyblock); result = Z_ExtractEncCksum(keyblock, &enctype, &cksumtype); if (result) { krb5_free_keyblock(Z_krb5_ctx, keyblock); krb5_auth_con_free(Z_krb5_ctx, authctx); krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT); syslog(LOG_WARNING, "ZCheckSrvAuthentication: Z_ExtractEncCksum failed: %s", error_message(result)); return (ZAUTH_FAILED); } /* HOLDING: authctx, authenticator, keyblock */ if (realm == NULL) ZSetSession(keyblock); /* Assemble the things to be checksummed */ /* first part is from start of packet through z_default_format: * - z_version * - z_num_other_fields * - z_kind * - z_uid * - z_port * - z_auth * - z_authent_len * - z_ascii_authent * - z_class * - z_class_inst * - z_opcode * - z_sender * - z_recipient * - z_default_format */ cksum0_base = notice->z_packet; x = notice->z_default_format; cksum0_len = x + strlen(x) + 1 - cksum0_base; /* second part is from z_multinotice through other fields: * - z_multinotice * - z_multiuid * - z_sender_(sock)addr * - z_charset * - z_other_fields[] */ if (notice->z_num_hdr_fields > 15 ) { cksum1_base = notice->z_multinotice; if (notice->z_num_other_fields) x = notice->z_other_fields[notice->z_num_other_fields - 1]; else { /* see also ZCheckRealmAuthentication and lib/ZCkZaut.c:ZCheckZcodeAuthentication */ /* XXXXXXXXXXXXXXXXXXXXXXX */ if (notice->z_num_hdr_fields > 16) x = cksum1_base + strlen(cksum1_base) + 1; /* multinotice */ if (notice->z_num_hdr_fields > 17) x = x + strlen(x) + 1; /* multiuid */ if (notice->z_num_hdr_fields > 18) x = x + strlen(x) + 1; /* sender */ } cksum1_len = x + strlen(x) + 1 - cksum1_base; /* charset / extra field */ } /* last part is the message body */ cksum2_base = notice->z_message; cksum2_len = notice->z_message_len; /*XXX we may wish to ditch this code someday?*/ if ((!notice->z_ascii_checksum || *notice->z_ascii_checksum != 'Z') && key_len == 8 && (enctype == (krb5_enctype)ENCTYPE_DES_CBC_CRC || enctype == (krb5_enctype)ENCTYPE_DES_CBC_MD4 || enctype == (krb5_enctype)ENCTYPE_DES_CBC_MD5)) { /* try old-format checksum (covers cksum0 only) */ ZChecksum_t our_checksum; if (realm == NULL) our_checksum = compute_checksum(notice, key_data); else our_checksum = compute_rlm_checksum(notice, key_data); krb5_free_keyblock(Z_krb5_ctx, keyblock); krb5_auth_con_free(Z_krb5_ctx, authctx); krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT); if (our_checksum == notice->z_checksum) { return ZAUTH_YES; } else { syslog(LOG_DEBUG, "ZCheckSrvAuthentication: des quad checksum mismatch"); return ZAUTH_FAILED; } } /* HOLDING: authctx, authenticator */ cksumbuf.length = cksum0_len + cksum1_len + cksum2_len; cksumbuf.data = malloc(cksumbuf.length); if (!cksumbuf.data) { krb5_free_keyblock(Z_krb5_ctx, keyblock); krb5_auth_con_free(Z_krb5_ctx, authctx); krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT); syslog(LOG_ERR, "ZCheckSrvAuthentication: malloc(cksumbuf.data): %m"); return ZAUTH_FAILED; } /* HOLDING: authctx, authenticator, cksumbuf.data */ cksum_data = (unsigned char *)cksumbuf.data; memcpy(cksum_data, cksum0_base, cksum0_len); if (cksum1_len) memcpy(cksum_data + cksum0_len, cksum1_base, cksum1_len); memcpy(cksum_data + cksum0_len + cksum1_len, cksum2_base, cksum2_len); /* decode zcoded checksum */ /* The encoded form is always longer than the original */ asn1_len = strlen(notice->z_ascii_checksum) + 1; asn1_data = malloc(asn1_len); if (!asn1_data) { krb5_free_keyblock(Z_krb5_ctx, keyblock); krb5_auth_con_free(Z_krb5_ctx, authctx); krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT); free(cksumbuf.data); syslog(LOG_ERR, "ZCheckSrvAuthentication: malloc(asn1_data): %m"); return ZAUTH_FAILED; } /* HOLDING: authctx, authenticator, cksumbuf.data, asn1_data */ result = ZReadZcode((unsigned char *)notice->z_ascii_checksum, asn1_data, asn1_len, &asn1_len); if (result != ZERR_NONE) { krb5_free_keyblock(Z_krb5_ctx, keyblock); krb5_auth_con_free(Z_krb5_ctx, authctx); krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT); free(asn1_data); free(cksumbuf.data); syslog(LOG_WARNING, "ZCheckSrvAuthentication: ZReadZcode: %s", error_message(result)); return ZAUTH_FAILED; } /* HOLDING: asn1_data, cksumbuf.data, authctx, authenticator */ valid = Z_krb5_verify_cksum(keyblock, &cksumbuf, cksumtype, Z_KEYUSAGE_CLT_CKSUM, asn1_data, asn1_len); /* XXX compatibility with unreleased interrealm krb5; drop in 3.1 */ if (!valid && realm) valid = Z_krb5_verify_cksum(keyblock, &cksumbuf, cksumtype, Z_KEYUSAGE_SRV_CKSUM, asn1_data, asn1_len); free(asn1_data); krb5_auth_con_free(Z_krb5_ctx, authctx); krb5_free_authenticator(Z_krb5_ctx, KRB5AUTHENT); krb5_free_keyblock(Z_krb5_ctx, keyblock); free(cksumbuf.data); if (valid) { return ZAUTH_YES; } else { syslog(LOG_DEBUG, "ZCheckSrvAuthentication: Z_krb5_verify_cksum: failed"); return ZAUTH_FAILED; } #else return (notice->z_auth) ? ZAUTH_YES : ZAUTH_NO; #endif }
/* returns 0 for failure, 1 for success */ static int k5support_verify_tgt(krb5_context context, krb5_ccache ccache) { krb5_principal server; krb5_data packet; krb5_keyblock *keyblock = NULL; krb5_auth_context auth_context = NULL; krb5_error_code k5_retcode; krb5_keytab kt = NULL; char thishost[BUFSIZ]; int result = 0; memset(&packet, 0, sizeof(packet)); if ((k5_retcode = krb5_sname_to_principal(context, NULL, verify_principal, KRB5_NT_SRV_HST, &server))) { k5support_log_err(context, k5_retcode, "krb5_sname_to_principal()"); return 0; } if (keytabname) { if ((k5_retcode = krb5_kt_resolve(context, keytabname, &kt))) { k5support_log_err(context, k5_retcode, "krb5_kt_resolve()"); goto fini; } } if ((k5_retcode = krb5_kt_read_service_key(context, kt, server, 0, 0, &keyblock))) { k5support_log_err(context, k5_retcode, "krb5_kt_read_service_key()"); goto fini; } if (keyblock) { krb5_free_keyblock(context, keyblock); } /* this duplicates work done in krb5_sname_to_principal * oh well. */ if (gethostname(thishost, BUFSIZ) < 0) { goto fini; } thishost[BUFSIZ-1] = '\0'; if ((k5_retcode = krb5_mk_req(context, &auth_context, 0, verify_principal, thishost, NULL, ccache, &packet))) { k5support_log_err(context, k5_retcode, "krb5_mk_req()"); } if (auth_context) { krb5_auth_con_free(context, auth_context); auth_context = NULL; } if (k5_retcode) { goto fini; } if ((k5_retcode = krb5_rd_req(context, &auth_context, &packet, server, NULL, NULL, NULL))) { k5support_log_err(context, k5_retcode, "krb5_rd_req()"); goto fini; } if (auth_context) { krb5_auth_con_free(context, auth_context); auth_context = NULL; } /* all is good now */ result = 1; fini: if (!k5_retcode) { krb5_free_data_contents(context, &packet); } krb5_free_principal(context, server); return result; }
int Condor_Auth_Kerberos :: authenticate_server_kerberos() { krb5_error_code code; krb5_flags flags = 0; krb5_data request, reply; priv_state priv; krb5_keytab keytab = 0; int message, rc = FALSE; krb5_ticket * ticket = NULL; request.data = 0; reply.data = 0; keytabName_ = param(STR_KERBEROS_SERVER_KEYTAB); //------------------------------------------ // Getting keytab info //------------------------------------------ if (keytabName_) { code = krb5_kt_resolve(krb_context_, keytabName_, &keytab); } else { code = krb5_kt_default(krb_context_, &keytab); } if (code) { dprintf( D_ALWAYS, "1: Kerberos server authentication error:%s\n", error_message(code) ); goto error; } //------------------------------------------ // Get te KRB_AP_REQ message //------------------------------------------ if(read_request(&request) == FALSE) { dprintf( D_ALWAYS, "KERBEROS: Server is unable to read request\n" ); goto error; } dprintf( D_SECURITY, "Reading kerberos request object (krb5_rd_req)\n"); dprintf_krb5_principal( D_FULLDEBUG, "KERBEROS: krb_principal_ is '%s'\n", krb_principal_); priv = set_root_priv(); // Get the old privilige if ((code = krb5_rd_req(krb_context_, &auth_context_, &request, //krb_principal_, NULL, keytab, &flags, &ticket))) { set_priv(priv); // Reset dprintf( D_ALWAYS, "2: Kerberos server authentication error:%s\n", error_message(code) ); goto error; } set_priv(priv); // Reset dprintf ( D_FULLDEBUG, "KERBEROS: krb5_rd_req done.\n"); //------------------------------------------ // See if mutual authentication is required //------------------------------------------ if (flags & AP_OPTS_MUTUAL_REQUIRED) { if ((code = krb5_mk_rep(krb_context_, auth_context_, &reply))) { dprintf( D_ALWAYS, "3: Kerberos server authentication error:%s\n", error_message(code) ); goto error; } mySock_->encode(); message = KERBEROS_MUTUAL; if (!mySock_->code(message) || !mySock_->end_of_message()) { goto error; } // send the message if (send_request(&reply) != KERBEROS_GRANT) { goto cleanup; } } //------------------------------------------ // extract client addresses //------------------------------------------ if (ticket->enc_part2->caddrs) { struct in_addr in; memcpy(&(in.s_addr), ticket->enc_part2->caddrs[0]->contents, sizeof(in_addr)); setRemoteHost(inet_ntoa(in)); dprintf(D_SECURITY, "Client address is %s\n", getRemoteHost()); } // First, map the name, this has to take place before receive_tgt_creds! if (!map_kerberos_name(&(ticket->enc_part2->client))) { dprintf(D_SECURITY, "Unable to map Kerberos name\n"); goto error; } // copy the session key if ((code = krb5_copy_keyblock(krb_context_, ticket->enc_part2->session, &sessionKey_))){ dprintf(D_SECURITY, "4: Kerberos server authentication error:%s\n", error_message(code)); goto error; } // Next, see if we need client to forward the credential as well if (receive_tgt_creds(ticket)) { goto cleanup; } //------------------------------------------ // We are now authenticated! //------------------------------------------ dprintf(D_SECURITY, "User %s is now authenticated!\n", getRemoteUser()); rc = TRUE; goto cleanup; error: message = KERBEROS_DENY; mySock_->encode(); if ((!mySock_->code(message)) || (!mySock_->end_of_message())) { dprintf( D_ALWAYS, "KERBEROS: Failed to send response message!\n" ); } cleanup: //------------------------------------------ // Free up some stuff //------------------------------------------ if (ticket) { krb5_free_ticket(krb_context_, ticket); } if (keytab) { krb5_kt_close(krb_context_, keytab); } //------------------------------------------ // Free it for now, in the future, we might // need this for future secure transctions. //------------------------------------------ if (request.data) { free(request.data); } if (reply.data) { free(reply.data); } return rc; }
int main(int argc, char **argv) { int log_level = 0; char *ap_req_str = NULL; /* 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 */ } { printf("dbg: %s starts\n", prog); } 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 */ { 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); } /* make AP_REP */ { krb5_data ap_rep; ret = krb5_mk_rep(context, auth_context, &ap_rep); if (ret != 0) { printf("ERR:krb5_mk_rep:%s\n", krb5_get_err_text(context, ret)); return(ret); } { /* stdout */ int i = 0; unsigned char *p; p = (unsigned char *)ap_rep.data; printf("std:ap_rep:"); for (i = 0; i < ap_rep.length; i++) { printf("%02x", *p++); } printf("\n"); } } /* 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 ads_secrets_verify_ticket(krb5_context context, krb5_auth_context auth_context, krb5_principal host_princ, const DATA_BLOB *ticket, krb5_ticket **pp_tkt, krb5_keyblock **keyblock, krb5_error_code *perr) { krb5_error_code ret = 0; bool auth_ok = False; bool cont = true; char *password_s = NULL; /* Let's make some room for 2 password (old and new)*/ krb5_data passwords[2]; krb5_enctype enctypes[] = { #ifdef HAVE_ENCTYPE_AES256_CTS_HMAC_SHA1_96 ENCTYPE_AES256_CTS_HMAC_SHA1_96, #endif #ifdef HAVE_ENCTYPE_AES128_CTS_HMAC_SHA1_96 ENCTYPE_AES128_CTS_HMAC_SHA1_96, #endif ENCTYPE_ARCFOUR_HMAC, ENCTYPE_DES_CBC_CRC, ENCTYPE_DES_CBC_MD5, ENCTYPE_NULL }; krb5_data packet; int i, j; *pp_tkt = NULL; *keyblock = NULL; *perr = 0; ZERO_STRUCT(passwords); if (!secrets_init()) { DEBUG(1,("ads_secrets_verify_ticket: secrets_init failed\n")); *perr = KRB5_CONFIG_CANTOPEN; return False; } password_s = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL); if (!password_s) { DEBUG(1,(__location__ ": failed to fetch machine password\n")); *perr = KRB5_LIBOS_CANTREADPWD; return False; } passwords[0].data = password_s; passwords[0].length = strlen(password_s); password_s = secrets_fetch_prev_machine_password(lp_workgroup()); if (password_s) { DEBUG(10, (__location__ ": found previous password\n")); passwords[1].data = password_s; passwords[1].length = strlen(password_s); } /* CIFS doesn't use addresses in tickets. This would break NAT. JRA */ packet.length = ticket->length; packet.data = (char *)ticket->data; /* We need to setup a auth context with each possible encoding type * in turn. */ for (j=0; j<2 && passwords[j].length; j++) { for (i=0;enctypes[i];i++) { krb5_keyblock *key = NULL; if (!(key = SMB_MALLOC_P(krb5_keyblock))) { ret = ENOMEM; goto out; } if (create_kerberos_key_from_string(context, host_princ, &passwords[j], key, enctypes[i], false)) { SAFE_FREE(key); continue; } krb5_auth_con_setuseruserkey(context, auth_context, key); if (!(ret = krb5_rd_req(context, &auth_context, &packet, NULL, NULL, NULL, pp_tkt))) { DEBUG(10, (__location__ ": enc type [%u] " "decrypted message !\n", (unsigned int)enctypes[i])); auth_ok = True; cont = false; krb5_copy_keyblock(context, key, keyblock); krb5_free_keyblock(context, key); break; } DEBUG((ret != KRB5_BAD_ENCTYPE) ? 3 : 10, (__location__ ": enc type [%u] failed to " "decrypt with error %s\n", (unsigned int)enctypes[i], error_message(ret))); /* successfully decrypted but ticket is just not * valid at the moment */ if (ret == KRB5KRB_AP_ERR_TKT_NYV || ret == KRB5KRB_AP_ERR_TKT_EXPIRED || ret == KRB5KRB_AP_ERR_SKEW) { krb5_free_keyblock(context, key); cont = false; break; } krb5_free_keyblock(context, key); } if (!cont) { /* If we found a valid pass then no need to try * the next one or we have invalid ticket so no need * to try next password*/ break; } } out: SAFE_FREE(passwords[0].data); SAFE_FREE(passwords[1].data); *perr = ret; return auth_ok; }
void kerberos5_is(Authenticator *ap, unsigned char *data, int cnt) { krb5_error_code ret; krb5_data outbuf; krb5_keyblock *key_block; char *name; krb5_principal server; krb5_authenticator authenticator; int zero = 0; if (cnt-- < 1) return; switch (*data++) { case KRB_AUTH: auth.data = (char *)data; auth.length = cnt; auth_context = NULL; ret = krb5_auth_con_init (context, &auth_context); if (ret) { Data(ap, KRB_REJECT, "krb5_auth_con_init failed", -1); auth_finished(ap, AUTH_REJECT); if (auth_debug_mode) printf("Kerberos V5: krb5_auth_con_init failed (%s)\r\n", krb5_get_err_text(context, ret)); return; } ret = krb5_auth_con_setaddrs_from_fd (context, auth_context, &zero); if (ret) { Data(ap, KRB_REJECT, "krb5_auth_con_setaddrs_from_fd failed", -1); auth_finished(ap, AUTH_REJECT); if (auth_debug_mode) printf("Kerberos V5: " "krb5_auth_con_setaddrs_from_fd failed (%s)\r\n", krb5_get_err_text(context, ret)); return; } ret = krb5_sock_to_principal (context, 0, "host", KRB5_NT_SRV_HST, &server); if (ret) { Data(ap, KRB_REJECT, "krb5_sock_to_principal failed", -1); auth_finished(ap, AUTH_REJECT); if (auth_debug_mode) printf("Kerberos V5: " "krb5_sock_to_principal failed (%s)\r\n", krb5_get_err_text(context, ret)); return; } ret = krb5_rd_req(context, &auth_context, &auth, server, NULL, NULL, &ticket); krb5_free_principal (context, server); if (ret) { char *errbuf; asprintf(&errbuf, "Read req failed: %s", krb5_get_err_text(context, ret)); Data(ap, KRB_REJECT, errbuf, -1); if (auth_debug_mode) printf("%s\r\n", errbuf); free (errbuf); return; } ret = krb5_auth_con_getkey(context, auth_context, &key_block); if (ret) { Data(ap, KRB_REJECT, "krb5_auth_con_getkey failed", -1); auth_finished(ap, AUTH_REJECT); if (auth_debug_mode) printf("Kerberos V5: " "krb5_auth_con_getkey failed (%s)\r\n", krb5_get_err_text(context, ret)); return; } ret = krb5_auth_getauthenticator (context, auth_context, &authenticator); if (ret) { Data(ap, KRB_REJECT, "krb5_auth_getauthenticator failed", -1); auth_finished(ap, AUTH_REJECT); if (auth_debug_mode) printf("Kerberos V5: " "krb5_auth_getauthenticator failed (%s)\r\n", krb5_get_err_text(context, ret)); return; } if (authenticator->cksum) { char foo[2]; foo[0] = ap->type; foo[1] = ap->way; ret = krb5_verify_checksum (context, foo, sizeof(foo), key_block, authenticator->cksum); if (ret) { Data(ap, KRB_REJECT, "No checksum", -1); if (auth_debug_mode) printf ("No checksum\r\n"); krb5_free_authenticator (context, &authenticator); return; } } krb5_free_authenticator (context, &authenticator); ret = krb5_auth_con_getremotesubkey (context, auth_context, &key_block); if (ret) { Data(ap, KRB_REJECT, "krb5_auth_con_getremotesubkey failed", -1); auth_finished(ap, AUTH_REJECT); if (auth_debug_mode) printf("Kerberos V5: " "krb5_auth_con_getremotesubkey failed (%s)\r\n", krb5_get_err_text(context, ret)); return; } if ((ap->way & AUTH_HOW_MASK) == AUTH_HOW_MUTUAL) { ret = krb5_mk_rep(context, &auth_context, &outbuf); if (ret) { Data(ap, KRB_REJECT, "krb5_mk_rep failed", -1); auth_finished(ap, AUTH_REJECT); if (auth_debug_mode) printf("Kerberos V5: " "krb5_mk_rep failed (%s)\r\n", krb5_get_err_text(context, ret)); return; } Data(ap, KRB_RESPONSE, outbuf.data, outbuf.length); } if (krb5_unparse_name(context, ticket->client, &name)) name = 0; if(UserNameRequested && krb5_kuserok(context, ticket->client, UserNameRequested)) { Data(ap, KRB_ACCEPT, name, name ? -1 : 0); if (auth_debug_mode) { printf("Kerberos5 identifies him as ``%s''\r\n", name ? name : ""); } if(key_block->keytype == KEYTYPE_DES) { Session_Key skey; skey.type = SK_DES; skey.length = 8; skey.data = key_block->keyvalue.data; encrypt_session_key(&skey, 0); } } else { char *msg; asprintf (&msg, "user `%s' is not authorized to " "login as `%s'", name ? name : "<unknown>", UserNameRequested ? UserNameRequested : "<nobody>"); if (msg == NULL) Data(ap, KRB_REJECT, NULL, 0); else { Data(ap, KRB_REJECT, (void *)msg, -1); free(msg); } } auth_finished(ap, AUTH_USER); krb5_free_keyblock_contents(context, key_block); break; #ifdef FORWARD case KRB_FORWARD: { struct passwd *pwd; char ccname[1024]; /* XXX */ krb5_data inbuf; krb5_ccache ccache; inbuf.data = (char *)data; inbuf.length = cnt; pwd = getpwnam (UserNameRequested); if (pwd == NULL) break; snprintf (ccname, sizeof(ccname), "FILE:/tmp/krb5cc_%u", pwd->pw_uid); ret = krb5_cc_resolve (context, ccname, &ccache); if (ret) { if (auth_debug_mode) printf ("Kerberos V5: could not get ccache: %s\r\n", krb5_get_err_text(context, ret)); break; } ret = krb5_cc_initialize (context, ccache, ticket->client); if (ret) { if (auth_debug_mode) printf ("Kerberos V5: could not init ccache: %s\r\n", krb5_get_err_text(context, ret)); break; } ret = krb5_rd_cred (context, auth_context, ccache, &inbuf); if(ret) { char *errbuf; asprintf (&errbuf, "Read forwarded creds failed: %s", krb5_get_err_text (context, ret)); if(errbuf == NULL) Data(ap, KRB_FORWARD_REJECT, NULL, 0); else Data(ap, KRB_FORWARD_REJECT, errbuf, -1); if (auth_debug_mode) printf("Could not read forwarded credentials: %s\r\n", errbuf); free (errbuf); } else Data(ap, KRB_FORWARD_ACCEPT, 0, 0); chown (ccname + 5, pwd->pw_uid, -1); if (auth_debug_mode) printf("Forwarded credentials obtained\r\n"); break; } #endif /* FORWARD */ default: if (auth_debug_mode) printf("Unknown Kerberos option %d\r\n", data[-1]); Data(ap, KRB_REJECT, 0, 0); break; } }
static bool ads_dedicated_keytab_verify_ticket(krb5_context context, krb5_auth_context auth_context, const DATA_BLOB *ticket, krb5_ticket **pp_tkt, krb5_keyblock **keyblock, krb5_error_code *perr) { krb5_error_code ret = 0; bool auth_ok = false; krb5_keytab keytab = NULL; krb5_keytab_entry kt_entry; krb5_ticket *dec_ticket = NULL; krb5_data packet; krb5_kvno kvno = 0; krb5_enctype enctype; *pp_tkt = NULL; *keyblock = NULL; *perr = 0; ZERO_STRUCT(kt_entry); ret = smb_krb5_open_keytab(context, lp_dedicated_keytab_file(), true, &keytab); if (ret) { DEBUG(1, ("smb_krb5_open_keytab failed (%s)\n", error_message(ret))); goto out; } packet.length = ticket->length; packet.data = (char *)ticket->data; ret = krb5_rd_req(context, &auth_context, &packet, NULL, keytab, NULL, &dec_ticket); if (ret) { DEBUG(0, ("krb5_rd_req failed (%s)\n", error_message(ret))); goto out; } #ifdef HAVE_ETYPE_IN_ENCRYPTEDDATA /* Heimdal */ enctype = dec_ticket->ticket.key.keytype; #else /* MIT */ enctype = dec_ticket->enc_part.enctype; kvno = dec_ticket->enc_part.kvno; #endif /* Get the key for checking the pac signature */ ret = krb5_kt_get_entry(context, keytab, dec_ticket->server, kvno, enctype, &kt_entry); if (ret) { DEBUG(0, ("krb5_kt_get_entry failed (%s)\n", error_message(ret))); goto out; } ret = krb5_copy_keyblock(context, KRB5_KT_KEY(&kt_entry), keyblock); smb_krb5_kt_free_entry(context, &kt_entry); if (ret) { DEBUG(0, ("failed to copy key: %s\n", error_message(ret))); goto out; } auth_ok = true; *pp_tkt = dec_ticket; dec_ticket = NULL; out: if (dec_ticket) krb5_free_ticket(context, dec_ticket); if (keytab) krb5_kt_close(context, keytab); *perr = ret; return auth_ok; }
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 krb5_error_code process_chpw_request(krb5_context context, void *server_handle, char *realm, krb5_keytab keytab, const krb5_fulladdr *local_faddr, const krb5_fulladdr *remote_faddr, krb5_data *req, krb5_data *rep) { krb5_error_code ret; char *ptr; unsigned int plen, vno; krb5_data ap_req, ap_rep = empty_data(); krb5_data cipher = empty_data(), clear = empty_data(); krb5_auth_context auth_context = NULL; krb5_principal changepw = NULL; krb5_principal client, target = NULL; krb5_ticket *ticket = NULL; krb5_replay_data replay; krb5_error krberror; int numresult; char strresult[1024]; char *clientstr = NULL, *targetstr = NULL; const char *errmsg = NULL; size_t clen; char *cdots; struct sockaddr_storage ss; socklen_t salen; char addrbuf[100]; krb5_address *addr = remote_faddr->address; *rep = empty_data(); if (req->length < 4) { /* either this, or the server is printing bad messages, or the caller passed in garbage */ ret = KRB5KRB_AP_ERR_MODIFIED; numresult = KRB5_KPASSWD_MALFORMED; strlcpy(strresult, "Request was truncated", sizeof(strresult)); goto bailout; } ptr = req->data; /* verify length */ plen = (*ptr++ & 0xff); plen = (plen<<8) | (*ptr++ & 0xff); if (plen != req->length) { ret = KRB5KRB_AP_ERR_MODIFIED; numresult = KRB5_KPASSWD_MALFORMED; strlcpy(strresult, "Request length was inconsistent", sizeof(strresult)); goto bailout; } /* verify version number */ vno = (*ptr++ & 0xff) ; vno = (vno<<8) | (*ptr++ & 0xff); if (vno != 1 && vno != RFC3244_VERSION) { ret = KRB5KDC_ERR_BAD_PVNO; numresult = KRB5_KPASSWD_BAD_VERSION; snprintf(strresult, sizeof(strresult), "Request contained unknown protocol version number %d", vno); goto bailout; } /* read, check ap-req length */ ap_req.length = (*ptr++ & 0xff); ap_req.length = (ap_req.length<<8) | (*ptr++ & 0xff); if (ptr + ap_req.length >= req->data + req->length) { ret = KRB5KRB_AP_ERR_MODIFIED; numresult = KRB5_KPASSWD_MALFORMED; strlcpy(strresult, "Request was truncated in AP-REQ", sizeof(strresult)); goto bailout; } /* verify ap_req */ ap_req.data = ptr; ptr += ap_req.length; ret = krb5_auth_con_init(context, &auth_context); if (ret) { numresult = KRB5_KPASSWD_HARDERROR; strlcpy(strresult, "Failed initializing auth context", sizeof(strresult)); goto chpwfail; } ret = krb5_auth_con_setflags(context, auth_context, KRB5_AUTH_CONTEXT_DO_SEQUENCE); if (ret) { numresult = KRB5_KPASSWD_HARDERROR; strlcpy(strresult, "Failed initializing auth context", sizeof(strresult)); goto chpwfail; } ret = krb5_build_principal(context, &changepw, strlen(realm), realm, "kadmin", "changepw", NULL); if (ret) { numresult = KRB5_KPASSWD_HARDERROR; strlcpy(strresult, "Failed building kadmin/changepw principal", sizeof(strresult)); goto chpwfail; } ret = krb5_rd_req(context, &auth_context, &ap_req, changepw, keytab, NULL, &ticket); if (ret) { numresult = KRB5_KPASSWD_AUTHERROR; strlcpy(strresult, "Failed reading application request", sizeof(strresult)); goto chpwfail; } /* construct the ap-rep */ ret = krb5_mk_rep(context, auth_context, &ap_rep); if (ret) { numresult = KRB5_KPASSWD_AUTHERROR; strlcpy(strresult, "Failed replying to application request", sizeof(strresult)); goto chpwfail; } /* decrypt the ChangePasswdData */ cipher.length = (req->data + req->length) - ptr; cipher.data = ptr; /* * Don't set a remote address in auth_context before calling krb5_rd_priv, * so that we can work against clients behind a NAT. Reflection attacks * aren't a concern since we use sequence numbers and since our requests * don't look anything like our responses. Also don't set a local address, * since we don't know what interface the request was received on. */ ret = krb5_rd_priv(context, auth_context, &cipher, &clear, &replay); if (ret) { numresult = KRB5_KPASSWD_HARDERROR; strlcpy(strresult, "Failed decrypting request", sizeof(strresult)); goto chpwfail; } client = ticket->enc_part2->client; /* decode ChangePasswdData for setpw requests */ if (vno == RFC3244_VERSION) { krb5_data *clear_data; ret = decode_krb5_setpw_req(&clear, &clear_data, &target); if (ret != 0) { numresult = KRB5_KPASSWD_MALFORMED; strlcpy(strresult, "Failed decoding ChangePasswdData", sizeof(strresult)); goto chpwfail; } zapfree(clear.data, clear.length); clear = *clear_data; free(clear_data); if (target != NULL) { ret = krb5_unparse_name(context, target, &targetstr); if (ret != 0) { numresult = KRB5_KPASSWD_HARDERROR; strlcpy(strresult, "Failed unparsing target name for log", sizeof(strresult)); goto chpwfail; } } } ret = krb5_unparse_name(context, client, &clientstr); if (ret) { numresult = KRB5_KPASSWD_HARDERROR; strlcpy(strresult, "Failed unparsing client name for log", sizeof(strresult)); goto chpwfail; } /* for cpw, verify that this is an AS_REQ ticket */ if (vno == 1 && (ticket->enc_part2->flags & TKT_FLG_INITIAL) == 0) { numresult = KRB5_KPASSWD_INITIAL_FLAG_NEEDED; strlcpy(strresult, "Ticket must be derived from a password", sizeof(strresult)); goto chpwfail; } /* change the password */ ptr = k5memdup0(clear.data, clear.length, &ret); ret = schpw_util_wrapper(server_handle, client, target, (ticket->enc_part2->flags & TKT_FLG_INITIAL) != 0, ptr, NULL, strresult, sizeof(strresult)); if (ret) errmsg = krb5_get_error_message(context, ret); /* zap the password */ zapfree(clear.data, clear.length); zapfree(ptr, clear.length); clear = empty_data(); clen = strlen(clientstr); trunc_name(&clen, &cdots); switch (addr->addrtype) { case ADDRTYPE_INET: { struct sockaddr_in *sin = ss2sin(&ss); sin->sin_family = AF_INET; memcpy(&sin->sin_addr, addr->contents, addr->length); sin->sin_port = htons(remote_faddr->port); salen = sizeof(*sin); break; } case ADDRTYPE_INET6: { struct sockaddr_in6 *sin6 = ss2sin6(&ss); sin6->sin6_family = AF_INET6; memcpy(&sin6->sin6_addr, addr->contents, addr->length); sin6->sin6_port = htons(remote_faddr->port); salen = sizeof(*sin6); break; } default: { struct sockaddr *sa = ss2sa(&ss); sa->sa_family = AF_UNSPEC; salen = sizeof(*sa); break; } } if (getnameinfo(ss2sa(&ss), salen, addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST | NI_NUMERICSERV) != 0) strlcpy(addrbuf, "<unprintable>", sizeof(addrbuf)); if (vno == RFC3244_VERSION) { size_t tlen; char *tdots; const char *targetp; if (target == NULL) { tlen = clen; tdots = cdots; targetp = targetstr; } else { tlen = strlen(targetstr); trunc_name(&tlen, &tdots); targetp = clientstr; } krb5_klog_syslog(LOG_NOTICE, _("setpw request from %s by %.*s%s for " "%.*s%s: %s"), addrbuf, (int) clen, clientstr, cdots, (int) tlen, targetp, tdots, errmsg ? errmsg : "success"); } else { krb5_klog_syslog(LOG_NOTICE, _("chpw request from %s for %.*s%s: %s"), addrbuf, (int) clen, clientstr, cdots, errmsg ? errmsg : "success"); } switch (ret) { case KADM5_AUTH_CHANGEPW: numresult = KRB5_KPASSWD_ACCESSDENIED; break; case KADM5_PASS_Q_TOOSHORT: case KADM5_PASS_REUSE: case KADM5_PASS_Q_CLASS: case KADM5_PASS_Q_DICT: case KADM5_PASS_Q_GENERIC: case KADM5_PASS_TOOSOON: numresult = KRB5_KPASSWD_SOFTERROR; break; case 0: numresult = KRB5_KPASSWD_SUCCESS; strlcpy(strresult, "", sizeof(strresult)); break; default: numresult = KRB5_KPASSWD_HARDERROR; break; } chpwfail: ret = alloc_data(&clear, 2 + strlen(strresult)); if (ret) goto bailout; ptr = clear.data; *ptr++ = (numresult>>8) & 0xff; *ptr++ = numresult & 0xff; memcpy(ptr, strresult, strlen(strresult)); cipher = empty_data(); if (ap_rep.length) { ret = krb5_auth_con_setaddrs(context, auth_context, local_faddr->address, NULL); if (ret) { numresult = KRB5_KPASSWD_HARDERROR; strlcpy(strresult, "Failed storing client and server internet addresses", sizeof(strresult)); } else { ret = krb5_mk_priv(context, auth_context, &clear, &cipher, &replay); if (ret) { numresult = KRB5_KPASSWD_HARDERROR; strlcpy(strresult, "Failed encrypting reply", sizeof(strresult)); } } } /* if no KRB-PRIV was constructed, then we need a KRB-ERROR. if this fails, just bail. there's nothing else we can do. */ if (cipher.length == 0) { /* clear out ap_rep now, so that it won't be inserted in the reply */ if (ap_rep.length) { free(ap_rep.data); ap_rep = empty_data(); } krberror.ctime = 0; krberror.cusec = 0; krberror.susec = 0; ret = krb5_timeofday(context, &krberror.stime); if (ret) goto bailout; /* this is really icky. but it's what all the other callers to mk_error do. */ krberror.error = ret; krberror.error -= ERROR_TABLE_BASE_krb5; if (krberror.error < 0 || krberror.error > KRB_ERR_MAX) krberror.error = KRB_ERR_GENERIC; krberror.client = NULL; ret = krb5_build_principal(context, &krberror.server, strlen(realm), realm, "kadmin", "changepw", NULL); if (ret) goto bailout; krberror.text.length = 0; krberror.e_data = clear; ret = krb5_mk_error(context, &krberror, &cipher); krb5_free_principal(context, krberror.server); if (ret) goto bailout; } /* construct the reply */ ret = alloc_data(rep, 6 + ap_rep.length + cipher.length); if (ret) goto bailout; ptr = rep->data; /* length */ *ptr++ = (rep->length>>8) & 0xff; *ptr++ = rep->length & 0xff; /* version == 0x0001 big-endian */ *ptr++ = 0; *ptr++ = 1; /* ap_rep length, big-endian */ *ptr++ = (ap_rep.length>>8) & 0xff; *ptr++ = ap_rep.length & 0xff; /* ap-rep data */ if (ap_rep.length) { memcpy(ptr, ap_rep.data, ap_rep.length); ptr += ap_rep.length; } /* krb-priv or krb-error */ memcpy(ptr, cipher.data, cipher.length); bailout: krb5_auth_con_free(context, auth_context); krb5_free_principal(context, changepw); krb5_free_ticket(context, ticket); free(ap_rep.data); free(clear.data); free(cipher.data); krb5_free_principal(context, target); krb5_free_unparsed_name(context, targetstr); krb5_free_unparsed_name(context, clientstr); krb5_free_error_message(context, errmsg); return ret; }
static void test_ap(krb5_context context, krb5_principal target, krb5_principal server, krb5_keytab keytab, krb5_ccache ccache, const krb5_flags client_flags) { krb5_error_code ret; krb5_auth_context client_ac = NULL, server_ac = NULL; krb5_data data; krb5_flags server_flags; krb5_ticket *ticket = NULL; int32_t server_seq, client_seq; ret = krb5_mk_req_exact(context, &client_ac, client_flags, target, NULL, ccache, &data); if (ret) krb5_err(context, 1, ret, "krb5_mk_req_exact"); ret = krb5_rd_req(context, &server_ac, &data, server, keytab, &server_flags, &ticket); if (ret) krb5_err(context, 1, ret, "krb5_rd_req"); if (server_flags & AP_OPTS_MUTUAL_REQUIRED) { krb5_ap_rep_enc_part *repl; krb5_data_free(&data); if ((client_flags & AP_OPTS_MUTUAL_REQUIRED) == 0) krb5_errx(context, 1, "client flag missing mutual req"); ret = krb5_mk_rep (context, server_ac, &data); if (ret) krb5_err(context, 1, ret, "krb5_mk_rep"); ret = krb5_rd_rep (context, client_ac, &data, &repl); if (ret) krb5_err(context, 1, ret, "krb5_rd_rep"); krb5_free_ap_rep_enc_part (context, repl); } else { if (client_flags & AP_OPTS_MUTUAL_REQUIRED) krb5_errx(context, 1, "server flag missing mutual req"); } krb5_auth_con_getremoteseqnumber(context, server_ac, &server_seq); krb5_auth_con_getremoteseqnumber(context, client_ac, &client_seq); if (server_seq != client_seq) krb5_errx(context, 1, "seq num differ"); krb5_auth_con_getlocalseqnumber(context, server_ac, &server_seq); krb5_auth_con_getlocalseqnumber(context, client_ac, &client_seq); if (server_seq != client_seq) krb5_errx(context, 1, "seq num differ"); krb5_data_free(&data); krb5_auth_con_free(context, client_ac); krb5_auth_con_free(context, server_ac); if (verify_pac) { krb5_pac pac; ret = krb5_ticket_get_authorization_data_type(context, ticket, KRB5_AUTHDATA_WIN2K_PAC, &data); if (ret) krb5_err(context, 1, ret, "get pac"); ret = krb5_pac_parse(context, data.data, data.length, &pac); if (ret) krb5_err(context, 1, ret, "pac parse"); krb5_pac_free(context, pac); } krb5_free_ticket(context, ticket); }
static krb5_error_code process_chpw_request(krb5_context context, void *server_handle, char *realm, int s, krb5_keytab keytab, struct sockaddr_in *sin, krb5_data *req, krb5_data *rep) { krb5_error_code ret; char *ptr; int plen, vno; krb5_address local_kaddr, remote_kaddr; int allocated_mem = 0; krb5_data ap_req, ap_rep; krb5_auth_context auth_context; krb5_principal changepw; krb5_ticket *ticket; krb5_data cipher, clear; struct sockaddr local_addr, remote_addr; int addrlen; krb5_replay_data replay; krb5_error krberror; int numresult; char strresult[1024]; ret = 0; rep->length = 0; auth_context = NULL; changepw = NULL; ap_rep.length = 0; ap_rep.data = NULL; ticket = NULL; clear.length = 0; clear.data = NULL; cipher.length = 0; cipher.data = NULL; if (req->length < 4) { /* * either this, or the server is printing bad messages, * or the caller passed in garbage */ ret = KRB5KRB_AP_ERR_MODIFIED; numresult = KRB5_KPASSWD_MALFORMED; (void) strlcpy(strresult, "Request was truncated", sizeof (strresult)); goto chpwfail; } ptr = req->data; /* * Verify length */ plen = (*ptr++ & 0xff); plen = (plen<<8) | (*ptr++ & 0xff); if (plen != req->length) return (KRB5KRB_AP_ERR_MODIFIED); /* * Verify version number */ vno = (*ptr++ & 0xff); vno = (vno<<8) | (*ptr++ & 0xff); if (vno != 1) { ret = KRB5KDC_ERR_BAD_PVNO; numresult = KRB5_KPASSWD_MALFORMED; (void) snprintf(strresult, sizeof (strresult), "Request contained unknown protocol version number %d", vno); goto chpwfail; } /* * Read, check ap-req length */ ap_req.length = (*ptr++ & 0xff); ap_req.length = (ap_req.length<<8) | (*ptr++ & 0xff); if (ptr + ap_req.length >= req->data + req->length) { ret = KRB5KRB_AP_ERR_MODIFIED; numresult = KRB5_KPASSWD_MALFORMED; (void) strlcpy(strresult, "Request was truncated in AP-REQ", sizeof (strresult)); goto chpwfail; } /* * Verify ap_req */ ap_req.data = ptr; ptr += ap_req.length; if (ret = krb5_auth_con_init(context, &auth_context)) { krb5_klog_syslog(LOG_ERR, gettext("Change password request failed. " "Failed initializing auth context: %s"), error_message(ret)); numresult = KRB5_KPASSWD_HARDERROR; (void) strlcpy(strresult, "Failed initializing auth context", sizeof (strresult)); goto chpwfail; } if (ret = krb5_auth_con_setflags(context, auth_context, KRB5_AUTH_CONTEXT_DO_SEQUENCE)) { krb5_klog_syslog(LOG_ERR, gettext("Change password request failed. " "Failed setting auth " "context flags: %s"), error_message(ret)); numresult = KRB5_KPASSWD_HARDERROR; (void) strlcpy(strresult, "Failed initializing auth context", sizeof (strresult)); goto chpwfail; } if (ret = krb5_build_principal(context, &changepw, strlen(realm), realm, "kadmin", "changepw", NULL)) { krb5_klog_syslog(LOG_ERR, gettext("Change password request failed " "Failed to build kadmin/changepw " "principal: %s"), error_message(ret)); numresult = KRB5_KPASSWD_HARDERROR; (void) strlcpy(strresult, "Failed building kadmin/changepw principal", sizeof (strresult)); goto chpwfail; } ret = krb5_rd_req(context, &auth_context, &ap_req, changepw, keytab, NULL, &ticket); if (ret) { char kt_name[MAX_KEYTAB_NAME_LEN]; if (krb5_kt_get_name(context, keytab, kt_name, sizeof (kt_name))) strncpy(kt_name, "default keytab", sizeof (kt_name)); switch (ret) { case KRB5_KT_NOTFOUND: krb5_klog_syslog(LOG_ERR, gettext("Change password request failed because " "keytab entry \"kadmin/changepw\" " "is missing from \"%s\""), kt_name); break; case ENOENT: krb5_klog_syslog(LOG_ERR, gettext("Change password request failed because " "keytab file \"%s\" does not exist"), kt_name); break; default: krb5_klog_syslog(LOG_ERR, gettext("Change password request failed. " "Failed to parse Kerberos AP_REQ message: %s"), error_message(ret)); } numresult = KRB5_KPASSWD_AUTHERROR; (void) strlcpy(strresult, "Failed reading application request", sizeof (strresult)); goto chpwfail; } /* * Set up address info */ addrlen = sizeof (local_addr); if (getsockname(s, &local_addr, &addrlen) < 0) { ret = errno; numresult = KRB5_KPASSWD_HARDERROR; (void) strlcpy(strresult, "Failed getting server internet address", sizeof (strresult)); goto chpwfail; } /* * Some brain-dead OS's don't return useful information from * the getsockname call. Namely, windows and solaris. */ if (((struct sockaddr_in *)&local_addr)->sin_addr.s_addr != 0) { local_kaddr.addrtype = ADDRTYPE_INET; local_kaddr.length = sizeof (((struct sockaddr_in *) &local_addr)->sin_addr); /* CSTYLED */ local_kaddr.contents = (krb5_octet *) &(((struct sockaddr_in *)&local_addr)->sin_addr); } else { krb5_address **addrs; krb5_os_localaddr(context, &addrs); local_kaddr.magic = addrs[0]->magic; local_kaddr.addrtype = addrs[0]->addrtype; local_kaddr.length = addrs[0]->length; if ((local_kaddr.contents = malloc(addrs[0]->length)) == 0) { ret = errno; numresult = KRB5_KPASSWD_HARDERROR; (void) strlcpy(strresult, "Malloc failed for local_kaddr", sizeof (strresult)); goto chpwfail; } (void) memcpy(local_kaddr.contents, addrs[0]->contents, addrs[0]->length); allocated_mem++; krb5_free_addresses(context, addrs); } addrlen = sizeof (remote_addr); if (getpeername(s, &remote_addr, &addrlen) < 0) { ret = errno; numresult = KRB5_KPASSWD_HARDERROR; (void) strlcpy(strresult, "Failed getting client internet address", sizeof (strresult)); goto chpwfail; } remote_kaddr.addrtype = ADDRTYPE_INET; remote_kaddr.length = sizeof (((struct sockaddr_in *) &remote_addr)->sin_addr); /* CSTYLED */ remote_kaddr.contents = (krb5_octet *) &(((struct sockaddr_in *)&remote_addr)->sin_addr); remote_kaddr.addrtype = ADDRTYPE_INET; remote_kaddr.length = sizeof (sin->sin_addr); remote_kaddr.contents = (krb5_octet *) &sin->sin_addr; /* * mk_priv requires that the local address be set. * getsockname is used for this. rd_priv requires that the * remote address be set. recvfrom is used for this. If * rd_priv is given a local address, and the message has the * recipient addr in it, this will be checked. However, there * is simply no way to know ahead of time what address the * message will be delivered *to*. Therefore, it is important * that either no recipient address is in the messages when * mk_priv is called, or that no local address is passed to * rd_priv. Both is a better idea, and I have done that. In * summary, when mk_priv is called, *only* a local address is * specified. when rd_priv is called, *only* a remote address * is specified. Are we having fun yet? */ if (ret = krb5_auth_con_setaddrs(context, auth_context, NULL, &remote_kaddr)) { numresult = KRB5_KPASSWD_HARDERROR; (void) strlcpy(strresult, "Failed storing client internet address", sizeof (strresult)); goto chpwfail; } /* * Verify that this is an AS_REQ ticket */ if (!(ticket->enc_part2->flags & TKT_FLG_INITIAL)) { numresult = KRB5_KPASSWD_AUTHERROR; (void) strlcpy(strresult, "Ticket must be derived from a password", sizeof (strresult)); goto chpwfail; } /* * Construct the ap-rep */ if (ret = krb5_mk_rep(context, auth_context, &ap_rep)) { numresult = KRB5_KPASSWD_AUTHERROR; (void) strlcpy(strresult, "Failed replying to application request", sizeof (strresult)); goto chpwfail; } /* * Decrypt the new password */ cipher.length = (req->data + req->length) - ptr; cipher.data = ptr; if (ret = krb5_rd_priv(context, auth_context, &cipher, &clear, &replay)) { numresult = KRB5_KPASSWD_HARDERROR; (void) strlcpy(strresult, "Failed decrypting request", sizeof (strresult)); goto chpwfail; } /* * Change the password */ if ((ptr = (char *)malloc(clear.length + 1)) == NULL) { ret = errno; numresult = KRB5_KPASSWD_HARDERROR; (void) strlcpy(strresult, "Malloc failed for ptr", sizeof (strresult)); goto chpwfail; } (void) memcpy(ptr, clear.data, clear.length); ptr[clear.length] = '\0'; ret = (kadm5_ret_t)kadm5_chpass_principal_util(server_handle, ticket->enc_part2->client, ptr, NULL, strresult, sizeof (strresult)); /* * Zap the password */ (void) memset(clear.data, 0, clear.length); (void) memset(ptr, 0, clear.length); if (clear.data != NULL) { krb5_xfree(clear.data); clear.data = NULL; } free(ptr); clear.length = 0; if (ret) { if ((ret != KADM5_PASS_Q_TOOSHORT) && (ret != KADM5_PASS_REUSE) && (ret != KADM5_PASS_Q_CLASS) && (ret != KADM5_PASS_Q_DICT) && (ret != KADM5_PASS_TOOSOON)) numresult = KRB5_KPASSWD_HARDERROR; else numresult = KRB5_KPASSWD_SOFTERROR; /* * strresult set by kadb5_chpass_principal_util() */ goto chpwfail; } /* * Success! */ numresult = KRB5_KPASSWD_SUCCESS; (void) strlcpy(strresult, "", sizeof (strresult)); chpwfail: clear.length = 2 + strlen(strresult); if (clear.data != NULL) { krb5_xfree(clear.data); clear.data = NULL; } if ((clear.data = (char *)malloc(clear.length)) == NULL) { ret = errno; numresult = KRB5_KPASSWD_HARDERROR; (void) strlcpy(strresult, "Malloc failed for clear.data", sizeof (strresult)); } cipher.length = 0; if (ap_rep.length) { if (ret = krb5_auth_con_setaddrs(context, auth_context, &local_kaddr, NULL)) { numresult = KRB5_KPASSWD_HARDERROR; (void) strlcpy(strresult, "Failed storing client and server internet addresses", sizeof (strresult)); } else { if (ret = krb5_mk_priv(context, auth_context, &clear, &cipher, &replay)) { numresult = KRB5_KPASSWD_HARDERROR; (void) strlcpy(strresult, "Failed encrypting reply", sizeof (strresult)); } } } ptr = clear.data; *ptr++ = (numresult>>8) & 0xff; *ptr++ = numresult & 0xff; (void) memcpy(ptr, strresult, strlen(strresult)); /* * If no KRB-PRIV was constructed, then we need a KRB-ERROR. * If this fails, just bail. There's nothing else we can do. */ if (cipher.length == 0) { /* * Clear out ap_rep now, so that it won't be inserted * in the reply */ if (ap_rep.length) { if (ap_rep.data != NULL) krb5_xfree(ap_rep.data); ap_rep.data = NULL; ap_rep.length = 0; } krberror.ctime = 0; krberror.cusec = 0; krberror.susec = 0; if (ret = krb5_timeofday(context, &krberror.stime)) goto bailout; /* * This is really icky. but it's what all the other callers * to mk_error do. */ krberror.error = ret; krberror.error -= ERROR_TABLE_BASE_krb5; if (krberror.error < 0 || krberror.error > 128) krberror.error = KRB_ERR_GENERIC; krberror.client = NULL; if (ret = krb5_build_principal(context, &krberror.server, strlen(realm), realm, "kadmin", "changepw", NULL)) { goto bailout; } krberror.text.length = 0; krberror.e_data = clear; ret = krb5_mk_error(context, &krberror, &cipher); krb5_free_principal(context, krberror.server); if (ret) goto bailout; } /* * Construct the reply */ rep->length = 6 + ap_rep.length + cipher.length; if ((rep->data = (char *)malloc(rep->length)) == NULL) { ret = errno; goto bailout; } ptr = rep->data; /* * Length */ *ptr++ = (rep->length>>8) & 0xff; *ptr++ = rep->length & 0xff; /* * Version == 0x0001 big-endian */ *ptr++ = 0; *ptr++ = 1; /* * ap_rep length, big-endian */ *ptr++ = (ap_rep.length>>8) & 0xff; *ptr++ = ap_rep.length & 0xff; /* * ap-rep data */ if (ap_rep.length) { (void) memcpy(ptr, ap_rep.data, ap_rep.length); ptr += ap_rep.length; } /* * krb-priv or krb-error */ (void) memcpy(ptr, cipher.data, cipher.length); bailout: if (auth_context) krb5_auth_con_free(context, auth_context); if (changepw) krb5_free_principal(context, changepw); if (ap_rep.data != NULL) krb5_xfree(ap_rep.data); if (ticket) krb5_free_ticket(context, ticket); if (clear.data != NULL) krb5_xfree(clear.data); if (cipher.data != NULL) krb5_xfree(cipher.data); if (allocated_mem) krb5_xfree(local_kaddr.contents); return (ret); }