From bdb0b60861dd2c352dd30ff1c1822c57ce304d0f Mon Sep 17 00:00:00 2001 From: Andrew Bartlett Date: Fri, 9 Jul 2004 11:46:42 +0000 Subject: r1418: Merge Samba 3.0's recent kerberos changes into Samba4. None of this is used yet. Andrew Bartlett (This used to be commit 7596f311c9a18314716f64476030ce3dfcdd98bb) --- source4/libcli/auth/clikrb5.c | 22 ++- source4/libcli/auth/kerberos.c | 4 +- source4/libcli/auth/kerberos_verify.c | 314 +++++++++++++++++++++++----------- 3 files changed, 233 insertions(+), 107 deletions(-) (limited to 'source4/libcli/auth') diff --git a/source4/libcli/auth/clikrb5.c b/source4/libcli/auth/clikrb5.c index 6e19d4dc18..b3aa9008ca 100644 --- a/source4/libcli/auth/clikrb5.c +++ b/source4/libcli/auth/clikrb5.c @@ -124,13 +124,13 @@ #endif #if defined(HAVE_KRB5_GET_PERMITTED_ENCTYPES) -krb5_error_code get_kerberos_allowed_etypes(krb5_context context, + krb5_error_code get_kerberos_allowed_etypes(krb5_context context, krb5_enctype **enctypes) { return krb5_get_permitted_enctypes(context, enctypes); } #elif defined(HAVE_KRB5_GET_DEFAULT_IN_TKT_ETYPES) -krb5_error_code get_kerberos_allowed_etypes(krb5_context context, + krb5_error_code get_kerberos_allowed_etypes(krb5_context context, krb5_enctype **enctypes) { return krb5_get_default_in_tkt_etypes(context, enctypes); @@ -434,9 +434,12 @@ int cli_krb5_get_ticket(const char *principal, time_t time_offset, failed: if ( context ) { -#if 0 /* JERRY -- disabled since it causes heimdal 0.6.1rc3 to die - SuSE 9.1 Pro */ +/* Removed by jra. They really need to fix their kerberos so we don't leak memory. + JERRY -- disabled since it causes heimdal 0.6.1rc3 to die + SuSE 9.1 Pro +*/ if (ccdef) +#if 0 /* redisabled by gd :) at least until any official heimdal version has it fixed. */ krb5_cc_close(context, ccdef); #endif if (auth_context) @@ -486,6 +489,17 @@ failed: } #endif + krb5_error_code smb_krb5_kt_free_entry(krb5_context context, krb5_keytab_entry *kt_entry) +{ +#if defined(HAVE_KRB5_KT_FREE_ENTRY) + return krb5_kt_free_entry(context, kt_entry); +#elif defined(HAVE_KRB5_FREE_KEYTAB_ENTRY_CONTENTS) + return krb5_free_keytab_entry_contents(context, kt_entry); +#else +#error UNKNOWN_KT_FREE_FUNCTION +#endif +} + #else /* HAVE_KRB5 */ /* this saves a few linking headaches */ int cli_krb5_get_ticket(const char *principal, time_t time_offset, diff --git a/source4/libcli/auth/kerberos.c b/source4/libcli/auth/kerberos.c index e8bf4b0846..97b895a241 100644 --- a/source4/libcli/auth/kerberos.c +++ b/source4/libcli/auth/kerberos.c @@ -79,9 +79,9 @@ int kerberos_kinit_password(const char *principal, const char *password, int tim return code; } - if ((code = krb5_get_init_creds_password(ctx, &my_creds, me, NULL, + if ((code = krb5_get_init_creds_password(ctx, &my_creds, me, password, kerb_prompter, - password, 0, NULL, NULL))) { + NULL, 0, NULL, NULL))) { krb5_free_principal(ctx, me); krb5_free_context(ctx); return code; diff --git a/source4/libcli/auth/kerberos_verify.c b/source4/libcli/auth/kerberos_verify.c index 805a3f570f..e93d3aa6e8 100644 --- a/source4/libcli/auth/kerberos_verify.c +++ b/source4/libcli/auth/kerberos_verify.c @@ -26,10 +26,182 @@ #ifdef HAVE_KRB5 -/* - verify an incoming ticket and parse out the principal name and - authorization_data if available -*/ +/********************************************************************************** + Try to verify a ticket using the system keytab... the system keytab has kvno -1 entries, so + it's more like what microsoft does... see comment in utils/net_ads.c in the + ads_keytab_add_entry function for details. +***********************************************************************************/ + +static BOOL ads_keytab_verify_ticket(krb5_context context, krb5_auth_context auth_context, + const DATA_BLOB *ticket, krb5_data *p_packet, krb5_ticket **pp_tkt) +{ + krb5_error_code ret = 0; + BOOL auth_ok = False; + + krb5_keytab keytab = NULL; + krb5_kt_cursor cursor; + krb5_keytab_entry kt_entry; + char *princ_name = NULL; + + ZERO_STRUCT(kt_entry); + ZERO_STRUCT(cursor); + + ret = krb5_kt_default(context, &keytab); + if (ret) { + DEBUG(1, ("ads_keytab_verify_ticket: krb5_kt_default failed (%s)\n", error_message(ret))); + goto out; + } + + ret = krb5_kt_start_seq_get(context, keytab, &cursor); + if (ret) { + DEBUG(1, ("ads_keytab_verify_ticket: krb5_kt_start_seq_get failed (%s)\n", error_message(ret))); + goto out; + } + + while (!krb5_kt_next_entry(context, keytab, &kt_entry, &cursor)) { + ret = krb5_unparse_name(context, kt_entry.principal, &princ_name); + if (ret) { + DEBUG(1, ("ads_keytab_verify_ticket: krb5_unparse_name failed (%s)\n", error_message(ret))); + goto out; + } + /* Look for a CIFS ticket */ + if (!StrnCaseCmp(princ_name, "cifs/", 5)) { +#ifdef HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK + krb5_auth_con_setuseruserkey(context, auth_context, &kt_entry.keyblock); +#else + krb5_auth_con_setuseruserkey(context, auth_context, &kt_entry.key); +#endif + + p_packet->length = ticket->length; + p_packet->data = (krb5_pointer)ticket->data; + + if (!(ret = krb5_rd_req(context, &auth_context, p_packet, NULL, NULL, NULL, pp_tkt))) { + unsigned int keytype; + krb5_free_unparsed_name(context, princ_name); + princ_name = NULL; +#ifdef HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK + keytype = (unsigned int) kt_entry.keyblock.keytype; +#else + keytype = (unsigned int) kt_entry.key.enctype; +#endif + DEBUG(10,("ads_keytab_verify_ticket: enc type [%u] decrypted message !\n", + keytype)); + auth_ok = True; + break; + } + } + krb5_free_unparsed_name(context, princ_name); + princ_name = NULL; + } + if (ret && ret != KRB5_KT_END) { + /* This failed because something went wrong, not because the keytab file was empty. */ + DEBUG(1, ("ads_keytab_verify_ticket: krb5_kt_next_entry failed (%s)\n", error_message(ret))); + goto out; + } + + out: + + if (princ_name) { + krb5_free_unparsed_name(context, princ_name); + } + { + krb5_kt_cursor zero_csr; + ZERO_STRUCT(zero_csr); + if ((memcmp(&cursor, &zero_csr, sizeof(krb5_kt_cursor)) != 0) && keytab) { + krb5_kt_end_seq_get(context, keytab, &cursor); + } + } + if (keytab) { + krb5_kt_close(context, keytab); + } + + return auth_ok; +} + +/********************************************************************************** + Try to verify a ticket using the secrets.tdb. +***********************************************************************************/ + +static BOOL ads_secrets_verify_ticket(krb5_context context, krb5_auth_context auth_context, + krb5_principal host_princ, + const DATA_BLOB *ticket, krb5_data *p_packet, krb5_ticket **pp_tkt) +{ + krb5_error_code ret = 0; + BOOL auth_ok = False; + char *password_s = NULL; + krb5_data password; + krb5_enctype *enctypes = NULL; + int i; + + if (!secrets_init()) { + DEBUG(1,("ads_secrets_verify_ticket: secrets_init failed\n")); + return False; + } + + password_s = secrets_fetch_machine_password(lp_workgroup()); + if (!password_s) { + DEBUG(1,("ads_secrets_verify_ticket: failed to fetch machine password\n")); + return False; + } + + password.data = password_s; + password.length = strlen(password_s); + + /* CIFS doesn't use addresses in tickets. This would break NAT. JRA */ + + if ((ret = get_kerberos_allowed_etypes(context, &enctypes))) { + DEBUG(1,("ads_secrets_verify_ticket: krb5_get_permitted_enctypes failed (%s)\n", + error_message(ret))); + goto out; + } + + p_packet->length = ticket->length; + p_packet->data = (krb5_pointer)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 = (krb5_keyblock *)malloc(sizeof(*key)))) { + goto out; + } + + if (create_kerberos_key_from_string(context, host_princ, &password, key, enctypes[i])) { + SAFE_FREE(key); + continue; + } + + krb5_auth_con_setuseruserkey(context, auth_context, key); + + krb5_free_keyblock(context, key); + + if (!(ret = krb5_rd_req(context, &auth_context, p_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; + 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))); + } + + out: + + free_kerberos_etypes(context, enctypes); + SAFE_FREE(password_s); + + return auth_ok; +} + +/********************************************************************************** + Verify an incoming ticket and parse out the principal name and + authorization_data if available. +***********************************************************************************/ + NTSTATUS ads_verify_ticket(const char *realm, const DATA_BLOB *ticket, char **principal, DATA_BLOB *auth_data, DATA_BLOB *ap_rep, @@ -41,43 +213,21 @@ NTSTATUS ads_verify_ticket(const char *realm, const DATA_BLOB *ticket, krb5_data packet; krb5_ticket *tkt = NULL; krb5_rcache rcache = NULL; - int ret, i; - krb5_keyblock *key = NULL; + int ret; - krb5_principal host_princ; + krb5_principal host_princ = NULL; char *host_princ_s = NULL; - BOOL free_host_princ = False; BOOL got_replay_mutex = False; fstring myname; - char *password_s = NULL; - krb5_data password; - krb5_enctype *enctypes = NULL; -#if 0 - krb5_address local_addr; - krb5_address remote_addr; -#endif BOOL auth_ok = False; ZERO_STRUCT(packet); - ZERO_STRUCT(password); ZERO_STRUCTP(auth_data); ZERO_STRUCTP(ap_rep); + ZERO_STRUCTP(session_key); - if (!secrets_init()) { - DEBUG(1,("ads_verify_ticket: secrets_init failed\n")); - return NT_STATUS_LOGON_FAILURE; - } - - password_s = secrets_fetch_machine_password(lp_workgroup()); - if (!password_s) { - DEBUG(1,("ads_verify_ticket: failed to fetch machine password\n")); - return NT_STATUS_LOGON_FAILURE; - } - - password.data = password_s; - password.length = strlen(password_s); - + initialize_krb5_error_table(); ret = krb5_init_context(&context); if (ret) { DEBUG(1,("ads_verify_ticket: krb5_init_context failed (%s)\n", error_message(ret))); @@ -87,7 +237,6 @@ NTSTATUS ads_verify_ticket(const char *realm, const DATA_BLOB *ticket, ret = krb5_set_default_realm(context, realm); if (ret) { DEBUG(1,("ads_verify_ticket: krb5_set_default_realm failed (%s)\n", error_message(ret))); - sret = NT_STATUS_LOGON_FAILURE; goto out; } @@ -98,22 +247,29 @@ NTSTATUS ads_verify_ticket(const char *realm, const DATA_BLOB *ticket, ret = krb5_auth_con_init(context, &auth_context); if (ret) { DEBUG(1,("ads_verify_ticket: krb5_auth_con_init failed (%s)\n", error_message(ret))); - sret = NT_STATUS_LOGON_FAILURE; goto out; } - fstrcpy(myname, global_myname()); + name_to_fqdn(myname, global_myname()); strlower_m(myname); - asprintf(&host_princ_s, "HOST/%s@%s", myname, lp_realm()); + asprintf(&host_princ_s, "host/%s@%s", myname, lp_realm()); ret = krb5_parse_name(context, host_princ_s, &host_princ); if (ret) { DEBUG(1,("ads_verify_ticket: krb5_parse_name(%s) failed (%s)\n", host_princ_s, error_message(ret))); - sret = NT_STATUS_LOGON_FAILURE; goto out; } - free_host_princ = True; + + /* Lock a mutex surrounding the replay as there is no locking in the MIT krb5 + * code surrounding the replay cache... */ + + if (!grab_server_mutex("replay cache mutex")) { + DEBUG(1,("ads_verify_ticket: unable to protect replay cache with mutex.\n")); + goto out; + } + + got_replay_mutex = True; /* * JRA. We must set the rcache here. This will prevent replay attacks. @@ -122,67 +278,19 @@ NTSTATUS ads_verify_ticket(const char *realm, const DATA_BLOB *ticket, ret = krb5_get_server_rcache(context, krb5_princ_component(context, host_princ, 0), &rcache); if (ret) { DEBUG(1,("ads_verify_ticket: krb5_get_server_rcache failed (%s)\n", error_message(ret))); - sret = NT_STATUS_LOGON_FAILURE; goto out; } ret = krb5_auth_con_setrcache(context, auth_context, rcache); if (ret) { DEBUG(1,("ads_verify_ticket: krb5_auth_con_setrcache failed (%s)\n", error_message(ret))); - sret = NT_STATUS_LOGON_FAILURE; goto out; } - /* CIFS doesn't use addresses in tickets. This would breat NAT. JRA */ - - if ((ret = get_kerberos_allowed_etypes(context, &enctypes))) { - DEBUG(1,("ads_verify_ticket: krb5_get_permitted_enctypes failed (%s)\n", - error_message(ret))); - sret = NT_STATUS_LOGON_FAILURE; - goto out; - } - - /* Lock a mutex surrounding the replay as there is no locking in the MIT krb5 - * code surrounding the replay cache... */ - - if (!grab_server_mutex("replay cache mutex")) { - DEBUG(1,("ads_verify_ticket: unable to protect replay cache with mutex.\n")); - sret = NT_STATUS_LOGON_FAILURE; - goto out; - } - - got_replay_mutex = True; - - /* We need to setup a auth context with each possible encoding type in turn. */ - for (i=0;enctypes[i];i++) { - if (!(key = (krb5_keyblock *)malloc(sizeof(*key)))) { - sret = NT_STATUS_NO_MEMORY; - goto out; - } - - if (create_kerberos_key_from_string(context, host_princ, &password, key, enctypes[i])) { - continue; - } - - krb5_auth_con_setuseruserkey(context, auth_context, key); - - krb5_free_keyblock(context, key); - - packet.length = ticket->length; - packet.data = (krb5_pointer)ticket->data; - - if (!(ret = krb5_rd_req(context, &auth_context, &packet, - NULL, - NULL, NULL, &tkt))) { - DEBUG(10,("ads_verify_ticket: enc type [%u] decrypted message !\n", - (unsigned int)enctypes[i] )); - auth_ok = True; - break; - } - - DEBUG((ret != KRB5_BAD_ENCTYPE) ? 3 : 10, - ("ads_verify_ticket: enc type [%u] failed to decrypt with error %s\n", - (unsigned int)enctypes[i], error_message(ret))); + auth_ok = ads_keytab_verify_ticket(context, auth_context, ticket, &packet, &tkt); + if (!auth_ok) { + auth_ok = ads_secrets_verify_ticket(context, auth_context, host_princ, + ticket, &packet, &tkt); } release_server_mutex(); @@ -191,7 +299,6 @@ NTSTATUS ads_verify_ticket(const char *realm, const DATA_BLOB *ticket, if (!auth_ok) { DEBUG(3,("ads_verify_ticket: krb5_rd_req with auth failed (%s)\n", error_message(ret))); - sret = NT_STATUS_LOGON_FAILURE; goto out; } @@ -199,12 +306,12 @@ NTSTATUS ads_verify_ticket(const char *realm, const DATA_BLOB *ticket, if (ret) { DEBUG(3,("ads_verify_ticket: Failed to generate mutual authentication reply (%s)\n", error_message(ret))); - sret = NT_STATUS_LOGON_FAILURE; goto out; } *ap_rep = data_blob(packet.data, packet.length); - free(packet.data); + SAFE_FREE(packet.data); + packet.length = 0; get_krb5_smb_session_key(context, auth_context, session_key, True); dump_data_pw("SMB session key (from ticket)\n", session_key->data, session_key->length); @@ -213,7 +320,6 @@ NTSTATUS ads_verify_ticket(const char *realm, const DATA_BLOB *ticket, file_save("/tmp/ticket.dat", ticket->data, ticket->length); #endif - /* auth_data is the PAC */ get_auth_data_from_tkt(auth_data, tkt); #if 0 @@ -236,29 +342,35 @@ NTSTATUS ads_verify_ticket(const char *realm, const DATA_BLOB *ticket, out: - if (got_replay_mutex) + if (got_replay_mutex) { release_server_mutex(); + } - if (!NT_STATUS_IS_OK(sret)) + if (!NT_STATUS_IS_OK(sret)) { data_blob_free(auth_data); + } - if (!NT_STATUS_IS_OK(sret)) + if (!NT_STATUS_IS_OK(sret)) { data_blob_free(ap_rep); + } - if (free_host_princ) + if (host_princ) { krb5_free_principal(context, host_princ); + } - if (tkt != NULL) + if (tkt != NULL) { krb5_free_ticket(context, tkt); - free_kerberos_etypes(context, enctypes); - SAFE_FREE(password_s); + } + SAFE_FREE(host_princ_s); - if (auth_context) + if (auth_context) { krb5_auth_con_free(context, auth_context); + } - if (context) + if (context) { krb5_free_context(context); + } return sret; } -- cgit