Index: Makefile.in =================================================================== --- Makefile.in (revision 10425) +++ Makefile.in (working copy) @@ -715,7 +715,7 @@ libsmb/asn1.o libsmb/spnego.o libsmb/clikrb5.o libads/kerberos.o \ libads/kerberos_verify.o $(SECRETS_OBJ) $(SERVER_MUTEX_OBJ) \ libads/authdata.o $(RPC_PARSE_OBJ0) $(PASSDB_OBJ) $(GROUPDB_OBJ) \ - $(SMBLDAP_OBJ) $(DOSERR_OBJ) + $(SMBLDAP_OBJ) $(DOSERR_OBJ) rpc_parse/parse_net.o ###################################################################### # now the rules... Index: smbd/sesssetup.c =================================================================== --- smbd/sesssetup.c (revision 10425) +++ smbd/sesssetup.c (working copy) @@ -139,6 +139,7 @@ int length, int bufsize, DATA_BLOB *secblob) { + TALLOC_CTX *mem_ctx; DATA_BLOB ticket; char *client, *p, *domain; fstring netbios_domain_name; @@ -146,7 +147,7 @@ fstring user; int sess_vuid; NTSTATUS ret; - DATA_BLOB auth_data; + PAC_DATA *pac_data; DATA_BLOB ap_rep, ap_rep_wrapped, response; auth_serversupplied_info *server_info = NULL; DATA_BLOB session_key = data_blob(NULL, 0); @@ -154,18 +155,24 @@ DATA_BLOB nullblob = data_blob(NULL, 0); fstring real_username; BOOL map_domainuser_to_guest = False; + PAC_LOGON_INFO *logon_info = NULL; + int i; ZERO_STRUCT(ticket); - ZERO_STRUCT(auth_data); + ZERO_STRUCT(pac_data); ZERO_STRUCT(ap_rep); ZERO_STRUCT(ap_rep_wrapped); ZERO_STRUCT(response); + mem_ctx = talloc_init("reply_spnego_kerberos"); + if (mem_ctx == NULL) + return ERROR_NT(NT_STATUS_NO_MEMORY); + if (!spnego_parse_krb5_wrap(*secblob, &ticket, tok_id)) { return ERROR_NT(NT_STATUS_LOGON_FAILURE); } - ret = ads_verify_ticket(lp_realm(), &ticket, &client, &auth_data, &ap_rep, &session_key); + ret = ads_verify_ticket(mem_ctx, lp_realm(), &ticket, &client, &pac_data, &ap_rep, &session_key); data_blob_free(&ticket); @@ -174,8 +181,19 @@ return ERROR_NT(NT_STATUS_LOGON_FAILURE); } - data_blob_free(&auth_data); + if (pac_data) { + /* get the logon_info */ + for (i=0; i < pac_data->num_buffers; i++) { + + if (pac_data->pac_buffer[i].type != PAC_TYPE_LOGON_INFO) + continue; + + logon_info = pac_data->pac_buffer[i].ctr->pac.logon_info; + break; + } + } + DEBUG(3,("Ticket name is [%s]\n", client)); p = strchr_m(client, '@'); @@ -203,7 +221,14 @@ domain = p+1; - { + if (logon_info && logon_info->info3.hdr_logon_dom.uni_str_len) { + + unistr2_to_ascii(netbios_domain_name, &logon_info->info3.uni_logon_dom, -1); + domain = netbios_domain_name; + DEBUG(10, ("Mapped to [%s] (using PAC)\n", domain)); + + } else { + /* If we have winbind running, we can (and must) shorten the username by using the short netbios name. Otherwise we will have inconsistent user names. With Kerberos, we get the @@ -231,7 +256,7 @@ wb_response.data.domain_info.name); domain = netbios_domain_name; - DEBUG(10, ("Mapped to [%s]\n", domain)); + DEBUG(10, ("Mapped to [%s] (using Winbind)\n", domain)); } else { DEBUG(3, ("Could not find short name -- winbind " "not running?\n")); @@ -274,8 +299,21 @@ reload_services(True); if ( map_domainuser_to_guest ) { make_server_info_guest(&server_info); + } else if (logon_info) { + ret = make_server_info_pac(&server_info, real_username, pw, logon_info); + + if ( !NT_STATUS_IS_OK(ret) ) { + DEBUG(1,("make_server_info_pac failed!\n")); + SAFE_FREE(client); + data_blob_free(&ap_rep); + data_blob_free(&session_key); + passwd_free(&pw); + return ERROR_NT(ret); + } + } else { ret = make_server_info_pw(&server_info, real_username, pw); + if ( !NT_STATUS_IS_OK(ret) ) { DEBUG(1,("make_server_info_from_pw failed!\n")); SAFE_FREE(client); @@ -284,15 +322,17 @@ passwd_free(&pw); return ERROR_NT(ret); } + + /* make_server_info_pw does not set the domain. Without this we end up + * with the local netbios name in substitutions for %D. */ + + if (server_info->sam_account != NULL) { + pdb_set_domain(server_info->sam_account, domain, PDB_SET); + } } - passwd_free(&pw); - /* make_server_info_pw does not set the domain. Without this we end up - * with the local netbios name in substitutions for %D. */ - if (server_info->sam_account != NULL) { - pdb_set_domain(server_info->sam_account, domain, PDB_SET); - } + passwd_free(&pw); /* register_vuid keeps the server info */ /* register_vuid takes ownership of session_key, no need to free after this. @@ -339,6 +379,7 @@ data_blob_free(&ap_rep); data_blob_free(&ap_rep_wrapped); data_blob_free(&response); + talloc_destroy(mem_ctx); return -1; /* already replied */ } Index: auth/auth_util.c =================================================================== --- auth/auth_util.c (revision 10425) +++ auth/auth_util.c (working copy) @@ -832,6 +832,61 @@ } /*************************************************************************** + Make (and fill) a user_info struct from a Kerberos PAC logon_info by conversion + to a SAM_ACCOUNT +***************************************************************************/ + +NTSTATUS make_server_info_pac(auth_serversupplied_info **server_info, + char *unix_username, + struct passwd *pwd, + PAC_LOGON_INFO *logon_info) +{ + NTSTATUS nt_status; + SAM_ACCOUNT *sampass = NULL; + DOM_SID user_sid, group_sid; + fstring dom_name; + + if (!NT_STATUS_IS_OK(nt_status = pdb_init_sam_pw(&sampass, pwd))) { + return nt_status; + } + if (!NT_STATUS_IS_OK(nt_status = make_server_info(server_info))) { + return nt_status; + } + + /* only copy user_sid, group_sid and domain name out of the PAC for + * now, we will benefit from more later - Guenther */ + + sid_copy(&user_sid, &logon_info->info3.dom_sid.sid); + sid_append_rid(&user_sid, logon_info->info3.user_rid); + pdb_set_user_sid(sampass, &user_sid, PDB_SET); + + sid_copy(&group_sid, &logon_info->info3.dom_sid.sid); + sid_append_rid(&group_sid, logon_info->info3.group_rid); + pdb_set_group_sid(sampass, &group_sid, PDB_SET); + + unistr2_to_ascii(dom_name, &logon_info->info3.uni_logon_dom, -1); + pdb_set_domain(sampass, dom_name, PDB_SET); + + pdb_set_logon_count(sampass, logon_info->info3.logon_count, PDB_SET); + + (*server_info)->sam_account = sampass; + + if (!NT_STATUS_IS_OK(nt_status = add_user_groups(server_info, unix_username, + sampass, pwd->pw_uid, pwd->pw_gid))) + { + return nt_status; + } + + (*server_info)->unix_name = smb_xstrdup(unix_username); + + (*server_info)->sam_fill_level = SAM_FILL_ALL; + (*server_info)->uid = pwd->pw_uid; + (*server_info)->gid = pwd->pw_gid; + return nt_status; +} + + +/*************************************************************************** Make (and fill) a user_info struct from a 'struct passwd' by conversion to a SAM_ACCOUNT ***************************************************************************/ Index: auth/auth_winbind.c =================================================================== --- auth/auth_winbind.c (revision 10425) +++ auth/auth_winbind.c (working copy) @@ -38,7 +38,7 @@ } prs_copy_data_in(&ps, (char *)info3_ndr, len); prs_set_offset(&ps,0); - if (!net_io_user_info3("", info3, &ps, 1, 3)) { + if (!net_io_user_info3("", info3, &ps, 1, 3, False)) { DEBUG(2, ("get_info3_from_ndr: could not parse info3 struct!\n")); return NT_STATUS_UNSUCCESSFUL; } Index: nsswitch/winbindd_pam.c =================================================================== --- nsswitch/winbindd_pam.c (revision 10425) +++ nsswitch/winbindd_pam.c (working copy) @@ -37,7 +37,7 @@ if (!prs_init(&ps, 256 /* Random, non-zero number */, mem_ctx, MARSHALL)) { return NT_STATUS_NO_MEMORY; } - if (!net_io_user_info3("", info3, &ps, 1, 3)) { + if (!net_io_user_info3("", info3, &ps, 1, 3, False)) { prs_mem_free(&ps); return NT_STATUS_UNSUCCESSFUL; } Index: lib/time.c =================================================================== --- lib/time.c (revision 10425) +++ lib/time.c (working copy) @@ -783,6 +783,15 @@ } /**************************************************************************** + Check if two NTTIMEs are the same. +****************************************************************************/ + +BOOL nt_time_equals(NTTIME *nt1, NTTIME *nt2) +{ + return (nt1->high == nt2->high && nt1->low == nt2->low); +} + +/**************************************************************************** Return a timeval difference in usec. ****************************************************************************/ Index: libsmb/clikrb5.c =================================================================== --- libsmb/clikrb5.c (revision 10425) +++ libsmb/clikrb5.c (working copy) @@ -3,6 +3,8 @@ simple kerberos5 routines for active directory Copyright (C) Andrew Tridgell 2001 Copyright (C) Luke Howard 2002-2003 + Copyright (C) Andrew Bartlett 2005 + Copyright (C) Guenther Deschner 2005 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -186,17 +188,70 @@ } #endif - void get_auth_data_from_tkt(DATA_BLOB *auth_data, krb5_ticket *tkt) + BOOL get_auth_data_from_tkt(TALLOC_CTX *mem_ctx, DATA_BLOB *auth_data, krb5_ticket *tkt) { + DATA_BLOB auth_data_wrapped; + BOOL got_auth_data_pac = False; + int i; + #if defined(HAVE_KRB5_TKT_ENC_PART2) - if (tkt->enc_part2 && tkt->enc_part2->authorization_data && tkt->enc_part2->authorization_data[0] && tkt->enc_part2->authorization_data[0]->length) - *auth_data = data_blob(tkt->enc_part2->authorization_data[0]->contents, - tkt->enc_part2->authorization_data[0]->length); + if (tkt->enc_part2 && tkt->enc_part2->authorization_data && + tkt->enc_part2->authorization_data[0] && + tkt->enc_part2->authorization_data[0]->length) + { + for (i = 0; tkt->enc_part2->authorization_data[i] != NULL; i++) { + + if (tkt->enc_part2->authorization_data[i]->ad_type != + KRB5_AUTHDATA_IF_RELEVANT) { + DEBUG(10,("get_auth_data_from_tkt: ad_type is %d\n", + kt->enc_part2->authorization_data[i]->ad_type)); + continue; + } + + auth_data_wrapped = data_blob(tkt->enc_part2->authorization_data[i]->contents, + tkt->enc_part2->authorization_data[i]->length); + + /* check if it is a PAC */ + got_auth_data_pac = unwrap_pac(mem_ctx, &auth_data_wrapped, auth_data); + data_blob_free(&auth_data_wrapped); + + if (!got_auth_data_pac) { + continue; + } + } + + return got_auth_data_pac; + } + #else - if (tkt->ticket.authorization_data && tkt->ticket.authorization_data->len) - *auth_data = data_blob(tkt->ticket.authorization_data->val->ad_data.data, - tkt->ticket.authorization_data->val->ad_data.length); + if (tkt->ticket.authorization_data && + tkt->ticket.authorization_data->len) + { + for (i = 0; i < tkt->ticket.authorization_data->len; i++) { + + if (tkt->ticket.authorization_data->val[i].ad_type != + KRB5_AUTHDATA_IF_RELEVANT) { + DEBUG(10,("get_auth_data_from_tkt: ad_type is %d\n", + tkt->ticket.authorization_data->val[i].ad_type)); + continue; + } + + auth_data_wrapped = data_blob(tkt->ticket.authorization_data->val[i].ad_data.data, + tkt->ticket.authorization_data->val[i].ad_data.length); + + /* check if it is a PAC */ + got_auth_data_pac = unwrap_pac(mem_ctx, &auth_data_wrapped, auth_data); + data_blob_free(&auth_data_wrapped); + + if (!got_auth_data_pac) { + continue; + } + } + + return got_auth_data_pac; + } #endif + return False; } krb5_const_principal get_principal_from_tkt(krb5_ticket *tkt) @@ -550,6 +605,345 @@ #endif } +void smb_krb5_checksum_from_pac_sig(krb5_checksum *cksum, + PAC_SIGNATURE_DATA *sig) +{ +#ifdef HAVE_CHECKSUM_IN_KRB5_CHECKSUM + cksum->cksumtype = (krb5_cksumtype)sig->type; + cksum->checksum.length = sig->signature.buf_len; + cksum->checksum.data = sig->signature.buffer; +#else + cksum->checksum_type = (krb5_cksumtype)sig->type; + cksum->length = sig->signature.buf_len; + cksum->contents = sig->signature.buffer; +#endif +} + +krb5_error_code smb_krb5_verify_checksum(krb5_context context, + krb5_keyblock *keyblock, + krb5_keyusage usage, + krb5_checksum *cksum, + uint8 *data, + size_t length) +{ + krb5_error_code ret; + + /* verify the checksum */ + + /* welcome to the wonderful world of samba's kerberos abstraction layer: + * + * function heimdal 0.6.1rc3 heimdal 0.7 MIT krb 1.4.2 + * ----------------------------------------------------------------------------- + * krb5_c_verify_checksum - works works + * krb5_verify_checksum works (6 args) works (6 args) broken (7 args) + */ + +#if defined(HAVE_KRB5_C_VERIFY_CHECKSUM) + { + krb5_boolean checksum_valid = False; + krb5_data input; + + input.data = (char *)data; + input.length = length; + + ret = krb5_c_verify_checksum(context, + keyblock, + usage, + &input, + cksum, + &checksum_valid); + if (!checksum_valid) + ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; + } + +#elif KRB5_VERIFY_CHECKSUM_ARGS == 6 && defined(HAVE_KRB5_CRYPTO_INIT) && defined(HAVE_KRB5_CRYPTO) && defined(HAVE_KRB5_CRYPTO_DESTROY) + + /* Warning: MIT's krb5_verify_checksum cannot be used as it will use a key + * without enctype and it ignores any key_usage types - Guenther */ + + { + + krb5_crypto crypto; + ret = krb5_crypto_init(context, + keyblock, + 0, + &crypto); + if (ret) { + DEBUG(0,("smb_krb5_verify_checksum: krb5_crypto_init() failed: %s\n", + error_message(ret))); + return ret; + } + + ret = krb5_verify_checksum(context, + crypto, + usage, + data, + length, + cksum); + + krb5_crypto_destroy(context, crypto); + } + +#else +#error UNKNOWN_KRB5_VERIFY_CHECKSUM_FUNCTION +#endif + + return ret; +} + +time_t get_authtime_from_tkt(krb5_ticket *tkt) +{ +#if defined(HAVE_KRB5_TKT_ENC_PART2) + return tkt->enc_part2->times.authtime; +#else + return tkt->ticket.authtime; +#endif +} + +static int get_kvno_from_ap_req(krb5_ap_req *ap_req) +{ +#ifdef HAVE_TICKET_POINTER_IN_KRB5_AP_REQ /* MIT */ + if (ap_req->ticket->enc_part.kvno) + return ap_req->ticket->enc_part.kvno; +#else /* Heimdal */ + if (ap_req->ticket.enc_part.kvno) + return *ap_req->ticket.enc_part.kvno; +#endif + return 0; +} + +static krb5_enctype get_enctype_from_ap_req(krb5_ap_req *ap_req) +{ +#ifdef HAVE_ETYPE_IN_ENCRYPTEDDATA /* Heimdal */ + return ap_req->ticket.enc_part.etype; +#else /* MIT */ + return ap_req->ticket->enc_part.enctype; +#endif +} + +static krb5_error_code +get_key_from_keytab(krb5_context context, + krb5_keytab keytab, + krb5_const_principal server, + krb5_enctype enctype, + krb5_kvno kvno, + krb5_keyblock **out_key) +{ + krb5_keytab_entry entry; + krb5_error_code ret; + krb5_keytab real_keytab; + char *name = NULL; + + if (keytab == NULL) { + krb5_kt_default(context, &real_keytab); + } else { + real_keytab = keytab; + } + + if ( DEBUGLEVEL >= 10 ) { + krb5_unparse_name(context, server, &name); + DEBUG(10,("get_key_from_keytab: will look for kvno %d, enctype %d and name: %s\n", + kvno, enctype, name)); + krb5_free_unparsed_name(context, name); + } + + ret = krb5_kt_get_entry(context, + real_keytab, + server, + kvno, + enctype, + &entry); + + if (ret) { + DEBUG(0,("get_key_from_keytab: failed to retrieve key: %s\n", error_message(ret))); + goto out; + } + +#ifdef HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK /* Heimdal */ + ret = krb5_copy_keyblock(context, &entry.keyblock, out_key); +#elif defined(HAVE_KRB5_KEYTAB_ENTRY_KEY) /* MIT */ + ret = krb5_copy_keyblock(context, &entry.key, out_key); +#else +#error UNKNOWN_KRB5_KEYTAB_ENTRY_FORMAT +#endif + + if (ret) { + DEBUG(0,("get_key_from_keytab: failed to copy key: %s\n", error_message(ret))); + goto out; + } + + smb_krb5_kt_free_entry(context, &entry); + +out: + if (keytab == NULL) { + krb5_kt_close(context, real_keytab); + } + + return ret; +} + +void smb_krb5_free_ap_req(krb5_context context, + krb5_ap_req *ap_req) +{ +#ifdef HAVE_KRB5_FREE_AP_REQ /* MIT */ + krb5_free_ap_req(context, ap_req); +#elif defined(HAVE_FREE_AP_REQ) /* Heimdal */ + free_AP_REQ(ap_req); +#else +#error UNKNOWN_KRB5_AP_REQ_FREE_FUNCTION +#endif +} + +/* Prototypes */ +#if defined(HAVE_DECODE_KRB5_AP_REQ) /* MIT */ +krb5_error_code decode_krb5_ap_req(const krb5_data *code, krb5_ap_req **rep); +#endif + +krb5_error_code smb_krb5_get_keyinfo_from_ap_req(krb5_context context, + const krb5_data *inbuf, + krb5_kvno *kvno, + krb5_enctype *enctype) +{ + krb5_error_code ret; +#ifdef HAVE_KRB5_DECODE_AP_REQ /* Heimdal */ + { + krb5_ap_req ap_req; + + ret = krb5_decode_ap_req(context, inbuf, &ap_req); + if (ret) + return ret; + + *kvno = get_kvno_from_ap_req(&ap_req); + *enctype = get_enctype_from_ap_req(&ap_req); + + smb_krb5_free_ap_req(context, &ap_req); + } +#elif defined(HAVE_DECODE_KRB5_AP_REQ) /* MIT */ + { + krb5_ap_req *ap_req = NULL; + + ret = decode_krb5_ap_req(inbuf, &ap_req); + if (ret) + return ret; + + *kvno = get_kvno_from_ap_req(ap_req); + *enctype = get_enctype_from_ap_req(ap_req); + + smb_krb5_free_ap_req(context, ap_req); + } +#else +#error UNKOWN_KRB5_AP_REQ_DECODING_FUNCTION +#endif + return ret; +} + +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_ap_req *ap_req = NULL; + 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; + } + + ret = smb_krb5_get_keyinfo_from_ap_req(context, inbuf, &kvno, &enctype); + if (ret) { + return ret; + } + + ret = get_key_from_keytab(context, + keytab, + 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 (ap_req) { + smb_krb5_free_ap_req(context, ap_req); + } + + if (ret && local_keyblock != NULL) { + krb5_free_keyblock(context, local_keyblock); + } else { + *keyblock = local_keyblock; + } + + return ret; +} + +krb5_error_code smb_krb5_parse_name_norealm(krb5_context context, + const char *name, + krb5_principal *principal) +{ +#ifdef HAVE_KRB5_PARSE_NAME_NOREALM + return krb5_parse_name_norealm(context, name, principal); +#endif + + /* we are cheating here because parse_name will in fact set the realm. + * We don't care as the only caller of smb_krb5_parse_name_norealm + * ignores the realm anyway when calling + * smb_krb5_principal_compare_any_realm later - Guenther */ + + return krb5_parse_name(context, name, principal); +} + +BOOL smb_krb5_principal_compare_any_realm(krb5_context context, + krb5_const_principal princ1, + krb5_const_principal princ2) +{ +#ifdef HAVE_KRB5_PRINCIPAL_COMPARE_ANY_REALM + + return krb5_principal_compare_any_realm(context, princ1, princ2); + +/* krb5_princ_size is a macro in MIT */ +#elif defined(HAVE_KRB5_PRINC_SIZE) || defined(krb5_princ_size) + + int i, len1, len2; + const krb5_data *p1, *p2; + + len1 = krb5_princ_size(context, princ1); + len2 = krb5_princ_size(context, princ2); + + if (len1 != len2) + return False; + + for (i = 0; i < len1; i++) { + + p1 = krb5_princ_component(context, CONST_DISCARD(krb5_principal, princ1), i); + p2 = krb5_princ_component(context, CONST_DISCARD(krb5_principal, princ2), i); + + if (p1->length != p2->length || memcmp(p1->data, p2->data, p1->length)) + return False; + } + + return True; +#else +#error NO_SUITABLE_PRINCIPAL_COMPARE_FUNCTION +#endif +} + #else /* HAVE_KRB5 */ /* this saves a few linking headaches */ int cli_krb5_get_ticket(const char *principal, time_t time_offset, Index: rpc_parse/parse_net.c =================================================================== --- rpc_parse/parse_net.c (revision 10425) +++ rpc_parse/parse_net.c (working copy) @@ -1496,7 +1497,7 @@ ********************************************************************/ BOOL net_io_user_info3(const char *desc, NET_USER_INFO_3 *usr, prs_struct *ps, - int depth, uint16 validation_level) + int depth, uint16 validation_level, BOOL kerb_validation_level) { unsigned int i; @@ -1595,6 +1596,18 @@ } } + /* get kerb validation info (not really part of user_info_3) - Guenther */ + + if (kerb_validation_level) { + + if(!prs_uint32("ptr_res_group_dom_sid", ps, depth, &usr->ptr_res_group_dom_sid)) + return False; + if(!prs_uint32("res_group_count", ps, depth, &usr->res_group_count)) + return False; + if(!prs_uint32("ptr_res_groups", ps, depth, &usr->ptr_res_groups)) + return False; + } + if(!smb_io_unistr2("uni_user_name", &usr->uni_user_name, usr->hdr_user_name.buffer, ps, depth)) /* username unicode string */ return False; if(!smb_io_unistr2("uni_full_name", &usr->uni_full_name, usr->hdr_full_name.buffer, ps, depth)) /* user's full name unicode string */ @@ -1636,6 +1649,11 @@ uint32 num_other_sids = usr->num_other_sids; + if (!(usr->user_flgs & LOGON_EXTRA_SIDS)) { + DEBUG(10,("net_io_user_info3: user_flgs attribute does not have LOGON_EXTRA_SIDS\n")); + /* return False; */ + } + if (!prs_uint32("num_other_sids", ps, depth, &num_other_sids)) return False; @@ -1733,11 +1751,11 @@ return False; #if 1 /* W2k always needs this - even for bad passwd. JRA */ - if(!net_io_user_info3("", r_l->user, ps, depth, r_l->switch_value)) + if(!net_io_user_info3("", r_l->user, ps, depth, r_l->switch_value, False)) return False; #else if (r_l->switch_value != 0) { - if(!net_io_user_info3("", r_l->user, ps, depth, r_l->switch_value)) + if(!net_io_user_info3("", r_l->user, ps, depth, r_l->switch_value, False)) return False; } #endif Index: include/authdata.h =================================================================== --- include/authdata.h (revision 10425) +++ include/authdata.h (working copy) @@ -23,12 +23,22 @@ #define _AUTHDATA_H #include "rpc_misc.h" +#include "rpc_netlogon.h" #define PAC_TYPE_LOGON_INFO 1 #define PAC_TYPE_SERVER_CHECKSUM 6 #define PAC_TYPE_PRIVSVR_CHECKSUM 7 #define PAC_TYPE_LOGON_NAME 10 +#ifndef KRB5_AUTHDATA_WIN2K_PAC +#define KRB5_AUTHDATA_WIN2K_PAC 128 +#endif + +#ifndef KRB5_AUTHDATA_IF_RELEVANT +#define KRB5_AUTHDATA_IF_RELEVANT 1 +#endif + + typedef struct pac_logon_name { NTTIME logon_time; uint16 len; @@ -37,7 +47,7 @@ typedef struct pac_signature_data { uint32 type; - uint8 *signature; + RPC_DATA_BLOB signature; /* this not the on-wire-format (!) */ } PAC_SIGNATURE_DATA; typedef struct group_membership { @@ -50,6 +60,8 @@ GROUP_MEMBERSHIP *group_membership; } GROUP_MEMBERSHIP_ARRAY; +#if 0 /* Unused, replaced by NET_USER_INFO_3 - Guenther */ + typedef struct krb_sid_and_attrs { uint32 sid_ptr; uint32 attrs; @@ -82,7 +94,7 @@ UNIHDR hdr_dir_drive; uint16 logon_count; /* number of times user has logged onto domain */ - uint16 reserved12; + uint16 bad_password_count; /* samba4 idl */ uint32 user_rid; uint32 group_rid; @@ -90,16 +102,16 @@ uint32 group_membership_ptr; uint32 user_flags; - uint32 reserved13[4]; + uint8 session_key[16]; /* samba4 idl */ UNIHDR hdr_dom_controller; UNIHDR hdr_dom_name; uint32 ptr_dom_sid; - - uint32 reserved16[2]; - uint32 reserved17; /* looks like it may be acb_info */ - uint32 reserved18[7]; + uint8 lm_session_key[8]; /* samba4 idl */ + uint32 acct_flags; /* samba4 idl */ + uint32 unknown[7]; + uint32 sid_count; uint32 ptr_extra_sids; @@ -122,7 +134,15 @@ GROUP_MEMBERSHIP_ARRAY res_groups; } PAC_LOGON_INFO; +#endif +typedef struct pac_logon_info { + NET_USER_INFO_3 info3; + DOM_SID2 res_group_dom_sid; + GROUP_MEMBERSHIP_ARRAY res_groups; + +} PAC_LOGON_INFO; + typedef struct pac_info_ctr { union @@ -134,18 +154,19 @@ } pac; } PAC_INFO_CTR; -typedef struct pac_info_hdr { +typedef struct pac_buffer { uint32 type; uint32 size; uint32 offset; uint32 offsethi; PAC_INFO_CTR *ctr; -} PAC_INFO_HDR; + uint32 pad; +} PAC_BUFFER; typedef struct pac_data { uint32 num_buffers; uint32 version; - PAC_INFO_HDR *pac_info_hdr_ptr; + PAC_BUFFER *pac_buffer; } PAC_DATA; Index: include/includes.h =================================================================== --- include/includes.h (revision 10425) +++ include/includes.h (working copy) @@ -27,7 +27,8 @@ #ifndef __cplusplus #define class #error DONT_USE_CPLUSPLUS_RESERVED_NAMES -#define private #error DONT_USE_CPLUSPLUS_RESERVED_NAMES +/* allow to build with newer heimdal releases */ +/* #define private #error DONT_USE_CPLUSPLUS_RESERVED_NAMES */ #define public #error DONT_USE_CPLUSPLUS_RESERVED_NAMES #define protected #error DONT_USE_CPLUSPLUS_RESERVED_NAMES #define template #error DONT_USE_CPLUSPLUS_RESERVED_NAMES @@ -1414,7 +1415,7 @@ void setup_kaddr( krb5_address *pkaddr, struct sockaddr *paddr); int create_kerberos_key_from_string(krb5_context context, krb5_principal host_princ, krb5_data *password, krb5_keyblock *key, krb5_enctype enctype); int create_kerberos_key_from_string_direct(krb5_context context, krb5_principal host_princ, krb5_data *password, krb5_keyblock *key, krb5_enctype enctype); -void get_auth_data_from_tkt(DATA_BLOB *auth_data, krb5_ticket *tkt); +BOOL get_auth_data_from_tkt(TALLOC_CTX *mem_ctx, DATA_BLOB *auth_data, krb5_ticket *tkt); krb5_const_principal get_principal_from_tkt(krb5_ticket *tkt); krb5_error_code krb5_locate_kdc(krb5_context ctx, const krb5_data *realm, struct sockaddr **addr_pp, int *naddrs, int get_masters); krb5_error_code get_kerberos_allowed_etypes(krb5_context context, krb5_enctype **enctypes); Index: include/rpc_netlogon.h =================================================================== --- include/rpc_netlogon.h (revision 10425) +++ include/rpc_netlogon.h (working copy) @@ -84,6 +84,18 @@ #define NL_CTRL_REPL_IN_PROGRESS 0x0002 #define NL_CTRL_FULL_SYNC 0x0004 +#define LOGON_EXTRA_SIDS 0x0020 +#define LOGON_RESOURCE_GROUPS 0x0200 + +#define SE_GROUP_MANDATORY 0x00000001 +#define SE_GROUP_ENABLED_BY_DEFAULT 0x00000002 +#define SE_GROUP_ENABLED 0x00000004 +#define SE_GROUP_OWNER 0x00000008 +#define SE_GROUP_USE_FOR_DENY_ONLY 0x00000010 +#define SE_GROUP_LOGON_ID 0xC0000000 +#define SE_GROUP_RESOURCE 0x20000000 + + #if 0 /* I think this is correct - it's what gets parsed on the wire. JRA. */ /* NET_USER_INFO_2 */ @@ -186,6 +198,13 @@ uint32 num_other_sids; /* number of foreign/trusted domain sids */ uint32 buffer_other_sids; + /* The next three uint32 are not really part of user_info_3 but here + * for parsing convenience. They are only valid in Kerberos PAC + * parsing - Guenther */ + uint32 ptr_res_group_dom_sid; + uint32 res_group_count; + uint32 ptr_res_groups; + UNISTR2 uni_user_name; /* username unicode string */ UNISTR2 uni_full_name; /* user's full name unicode string */ UNISTR2 uni_logon_script; /* logon script unicode string */ Index: include/ads.h =================================================================== --- include/ads.h (revision 10425) +++ include/ads.h (working copy) @@ -130,7 +130,7 @@ #define UF_UNUSED_5 0x00800000 #define UF_UNUSED_6 0x01000000 -#define UF_UNUSED_7 0x02000000 +#define UF_NO_AUTH_DATA_REQUIRED 0x02000000 #define UF_UNUSED_8 0x04000000 #define UF_UNUSED_9 0x08000000 Index: configure.in =================================================================== --- configure.in (revision 10425) +++ configure.in (working copy) @@ -3048,9 +3048,77 @@ AC_CHECK_FUNC_EXT(krb5_krbhst_get_addrinfo, $KRB5_LIBS) AC_CHECK_FUNC_EXT(krb5_c_enctype_compare, $KRB5_LIBS) AC_CHECK_FUNC_EXT(krb5_enctypes_compatible_keys, $KRB5_LIBS) + AC_CHECK_FUNC_EXT(krb5_crypto_init, $KRB5_LIBS) + AC_CHECK_FUNC_EXT(krb5_crypto_destroy, $KRB5_LIBS) + AC_CHECK_FUNC_EXT(krb5_decode_ap_req, $KRB5_LIBS) + AC_CHECK_FUNC_EXT(decode_krb5_ap_req, $KRB5_LIBS) + AC_CHECK_FUNC_EXT(krb5_free_ap_req, $KRB5_LIBS) + AC_CHECK_FUNC_EXT(free_AP_REQ, $KRB5_LIBS) + AC_CHECK_FUNC_EXT(krb5_c_verify_checksum, $KRB5_LIBS) + AC_CHECK_FUNC_EXT(krb5_principal_compare_any_realm, $KRB5_LIBS) + AC_CHECK_FUNC_EXT(krb5_parse_name_norealm, $KRB5_LIBS) + AC_CHECK_FUNC_EXT(krb5_princ_size, $KRB5_LIBS) LIBS="$KRB5_LIBS $LIBS" - + + AC_CACHE_CHECK(whether krb5_verify_checksum takes 7 arguments, smb_krb5_verify_checksum, [ + AC_TRY_COMPILE([ + #include ], + [krb5_verify_checksum(0, 0, 0, 0, 0, 0, 0);], + [smb_krb5_verify_checksum=7], + [smb_krb5_verify_checksum=6], + ) + ]) + AC_DEFINE_UNQUOTED(KRB5_VERIFY_CHECKSUM_ARGS, $smb_krb5_verify_checksum, [Number of arguments to krb5_verify_checksum]) + + AC_CACHE_CHECK([for checksum in krb5_checksum], + samba_cv_HAVE_CHECKSUM_IN_KRB5_CHECKSUM,[ + AC_TRY_COMPILE([#include ], + [krb5_checksum cksum; cksum.checksum.length = 0;], + samba_cv_HAVE_CHECKSUM_IN_KRB5_CHECKSUM=yes, + samba_cv_HAVE_CHECKSUM_IN_KRB5_CHECKSUM=no)]) + + if test x"$samba_cv_HAVE_CHECKSUM_IN_KRB5_CHECKSUM" = x"yes"; then + AC_DEFINE(HAVE_CHECKSUM_IN_KRB5_CHECKSUM,1, + [Whether the krb5_checksum struct has a checksum property]) + fi + + AC_CACHE_CHECK([for etype in EncryptedData], + samba_cv_HAVE_ETYPE_IN_ENCRYPTEDDATA,[ + AC_TRY_COMPILE([#include ], + [EncryptedData edata; edata.etype = 0;], + samba_cv_HAVE_ETYPE_IN_ENCRYPTEDDATA=yes, + samba_cv_HAVE_ETYPE_IN_ENCRYPTEDDATA=no)]) + + if test x"$samba_cv_HAVE_ETYPE_IN_ENCRYPTEDDATA" = x"yes"; then + AC_DEFINE(HAVE_ETYPE_IN_ENCRYPTEDDATA,1, + [Whether the EncryptedData struct has a etype property]) + fi + + AC_CACHE_CHECK([for ticket pointer in krb5_ap_req], + samba_cv_HAVE_TICKET_POINTER_IN_KRB5_AP_REQ,[ + AC_TRY_COMPILE([#include ], + [krb5_ap_req *ap_req; ap_req->ticket = NULL;], + samba_cv_HAVE_TICKET_POINTER_IN_KRB5_AP_REQ=yes, + samba_cv_HAVE_TICKET_POINTER_IN_KRB5_AP_REQ=no)]) + + if test x"$samba_cv_HAVE_TICKET_POINTER_IN_KRB5_AP_REQ" = x"yes"; then + AC_DEFINE(HAVE_TICKET_POINTER_IN_KRB5_AP_REQ,1, + [Whether the krb5_ap_req struct has a ticket pointer]) + fi + + AC_CACHE_CHECK([for krb5_crypto type], + samba_cv_HAVE_KRB5_CRYPTO,[ + AC_TRY_COMPILE([#include ], + [krb5_crypto crypto;], + samba_cv_HAVE_KRB5_CRYPTO=yes, + samba_cv_HAVE_KRB5_CRYPTO=no)]) + + if test x"$samba_cv_HAVE_KRB5_CRYPTO" = x"yes"; then + AC_DEFINE(HAVE_KRB5_CRYPTO,1, + [Whether the type krb5_crypto exists]) + fi + AC_CACHE_CHECK([for krb5_encrypt_block type], samba_cv_HAVE_KRB5_ENCRYPT_BLOCK,[ AC_TRY_COMPILE([#include ], @@ -3178,6 +3246,30 @@ [Whether the KV5M_KEYTAB option is available]) fi + AC_CACHE_CHECK([for KRB5_KU_OTHER_CKSUM], + samba_cv_HAVE_KRB5_KU_OTHER_CKSUM,[ + AC_TRY_COMPILE([#include ], + [krb5_keyusage usage = KRB5_KU_OTHER_CKSUM;], + samba_cv_HAVE_KRB5_KU_OTHER_CKSUM=yes, + samba_cv_HAVE_KRB5_KU_OTHER_CKSUM=no)]) + + if test x"$samba_cv_HAVE_KRB5_KU_OTHER_CKSUM" = x"yes"; then + AC_DEFINE(HAVE_KRB5_KU_OTHER_CKSUM,1, + [Whether KRB5_KU_OTHER_CKSUM is available]) + fi + + AC_CACHE_CHECK([for KRB5_KEYUSAGE_APP_DATA_CKSUM], + samba_cv_HAVE_KRB5_KEYUSAGE_APP_DATA_CKSUM,[ + AC_TRY_COMPILE([#include ], + [krb5_keyusage usage = KRB5_KEYUSAGE_APP_DATA_CKSUM;], + samba_cv_HAVE_KRB5_KEYUSAGE_APP_DATA_CKSUM=yes, + samba_cv_HAVE_KRB5_KEYUSAGE_APP_DATA_CKSUM=no)]) + + if test x"$samba_cv_HAVE_KRB5_KEYUSAGE_APP_DATA_CKSUM" = x"yes"; then + AC_DEFINE(HAVE_KRB5_KEYUSAGE_APP_DATA_CKSUM,1, + [Whether KRB5_KEYUSAGE_APP_DATA_CKSUM is available]) + fi + AC_CACHE_CHECK([for the krb5_princ_component macro], samba_cv_HAVE_KRB5_PRINC_COMPONENT,[ AC_TRY_LINK([#include ], Index: libads/kerberos_verify.c =================================================================== --- libads/kerberos_verify.c (revision 10425) +++ libads/kerberos_verify.c (working copy) @@ -4,8 +4,9 @@ Copyright (C) Andrew Tridgell 2001 Copyright (C) Remus Koos 2001 Copyright (C) Luke Howard 2003 - Copyright (C) Guenther Deschner 2003 + Copyright (C) Guenther Deschner 2003, 2005 Copyright (C) Jim McDonough (jmcd@us.ibm.com) 2003 + Copyright (C) Andrew Bartlett 2004-2005 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -37,7 +38,8 @@ ***********************************************************************************/ 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) + const DATA_BLOB *ticket, krb5_data *p_packet, krb5_ticket **pp_tkt, + krb5_keyblock **keyblock) { krb5_error_code ret = 0; BOOL auth_ok = False; @@ -100,12 +102,18 @@ p_packet->length = ticket->length; p_packet->data = (krb5_pointer)ticket->data; *pp_tkt = NULL; - ret = krb5_rd_req(context, &auth_context, p_packet, kt_entry.principal, keytab, NULL, pp_tkt); + + ret = krb5_rd_req_return_keyblock_from_keytab(context, &auth_context, p_packet, + kt_entry.principal, keytab, + NULL, pp_tkt, keyblock); + if (ret) { - DEBUG(10, ("ads_keytab_verify_ticket: krb5_rd_req(%s) failed: %s\n", + DEBUG(10,("ads_keytab_verify_ticket: " + "krb5_rd_req_return_keyblock_from_keytab(%s) failed: %s\n", entry_princ_s, error_message(ret))); } else { - DEBUG(3,("ads_keytab_verify_ticket: krb5_rd_req succeeded for principal %s\n", + DEBUG(3,("ads_keytab_verify_ticket: " + "krb5_rd_req_return_keyblock_from_keytab succeeded for principal %s\n", entry_princ_s)); auth_ok = True; break; @@ -172,8 +180,9 @@ ***********************************************************************************/ 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_principal host_princ, + const DATA_BLOB *ticket, krb5_data *p_packet, krb5_ticket **pp_tkt, + krb5_keyblock **keyblock) { krb5_error_code ret = 0; BOOL auth_ok = False; @@ -182,6 +191,8 @@ krb5_enctype *enctypes = NULL; int i; + ZERO_STRUCTP(keyblock); + if (!secrets_init()) { DEBUG(1,("ads_secrets_verify_ticket: secrets_init failed\n")); return False; @@ -222,20 +233,23 @@ 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; + 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))); + + krb5_free_keyblock(context, key); + } out: @@ -251,27 +265,33 @@ authorization_data if available. ***********************************************************************************/ -NTSTATUS ads_verify_ticket(const char *realm, const DATA_BLOB *ticket, - char **principal, DATA_BLOB *auth_data, +NTSTATUS ads_verify_ticket(TALLOC_CTX *mem_ctx, + const char *realm, const DATA_BLOB *ticket, + char **principal, PAC_DATA **pac_data, DATA_BLOB *ap_rep, DATA_BLOB *session_key) { NTSTATUS sret = NT_STATUS_LOGON_FAILURE; + DATA_BLOB auth_data; krb5_context context = NULL; krb5_auth_context auth_context = NULL; krb5_data packet; krb5_ticket *tkt = NULL; krb5_rcache rcache = NULL; + krb5_keyblock *keyblock = NULL; + time_t authtime; int ret; krb5_principal host_princ = NULL; + krb5_const_principal client_principal = NULL; char *host_princ_s = NULL; BOOL got_replay_mutex = False; BOOL auth_ok = False; + BOOL got_auth_data = False; ZERO_STRUCT(packet); - ZERO_STRUCTP(auth_data); + ZERO_STRUCT(auth_data); ZERO_STRUCTP(ap_rep); ZERO_STRUCTP(session_key); @@ -335,20 +355,30 @@ } if (lp_use_kerberos_keytab()) { - auth_ok = ads_keytab_verify_ticket(context, auth_context, ticket, &packet, &tkt); + auth_ok = ads_keytab_verify_ticket(context, auth_context, ticket, &packet, &tkt, &keyblock); } if (!auth_ok) { auth_ok = ads_secrets_verify_ticket(context, auth_context, host_princ, - ticket, &packet, &tkt); + ticket, &packet, &tkt, &keyblock); } release_server_mutex(); got_replay_mutex = False; +#if 0 + /* Heimdal leaks here, if we fix the leak, MIT crashes */ + if (rcache) { + krb5_rc_close(context, rcache); + } +#endif + if (!auth_ok) { DEBUG(3,("ads_verify_ticket: krb5_rd_req with auth failed (%s)\n", error_message(ret))); goto out; + } else { + authtime = get_authtime_from_tkt(tkt); + client_principal = get_principal_from_tkt(tkt); } ret = krb5_mk_rep(context, auth_context, &packet); @@ -369,21 +399,41 @@ file_save("/tmp/ticket.dat", ticket->data, ticket->length); #endif - get_auth_data_from_tkt(auth_data, tkt); + /* continue when no PAC is retrieved + (like accounts that have the UF_NO_AUTH_DATA_REQUIRED flag set) */ - { - TALLOC_CTX *ctx = talloc_init("pac data"); - decode_pac_data(auth_data, ctx); - talloc_destroy(ctx); + got_auth_data = get_auth_data_from_tkt(mem_ctx, &auth_data, tkt); + if (!got_auth_data) { + DEBUG(3,("ads_verify_ticket: did not retrieve auth data. continuing without PAC\n")); } + if (got_auth_data && pac_data != NULL) { + + sret = decode_pac_data(mem_ctx, &auth_data, context, keyblock, client_principal, authtime, pac_data); + if (!NT_STATUS_IS_OK(sret)) { + DEBUG(0,("ads_verify_ticket: failed to decode PAC_DATA: %s\n", nt_errstr(sret))); + goto out; + } + data_blob_free(&auth_data); + } + #if 0 +#if defined(HAVE_KRB5_TKT_ENC_PART2) + /* MIT */ if (tkt->enc_part2) { file_save("/tmp/authdata.dat", tkt->enc_part2->authorization_data[0]->contents, tkt->enc_part2->authorization_data[0]->length); } +#else + /* Heimdal */ + if (tkt->ticket.authorization_data) { + file_save("/tmp/authdata.dat", + tkt->ticket.authorization_data->val->ad_data.data, + tkt->ticket.authorization_data->val->ad_data.length); + } #endif +#endif if ((ret = krb5_unparse_name(context, get_principal_from_tkt(tkt), principal))) { @@ -402,7 +452,7 @@ } if (!NT_STATUS_IS_OK(sret)) { - data_blob_free(auth_data); + data_blob_free(&auth_data); } if (!NT_STATUS_IS_OK(sret)) { @@ -413,6 +463,10 @@ krb5_free_principal(context, host_princ); } + if (keyblock) { + krb5_free_keyblock(context, keyblock); + } + if (tkt != NULL) { krb5_free_ticket(context, tkt); } Index: libads/authdata.c =================================================================== --- libads/authdata.c (revision 10425) +++ libads/authdata.c (working copy) @@ -2,6 +2,11 @@ Unix SMB/CIFS implementation. kerberos authorization data (PAC) utility library Copyright (C) Jim McDonough 2003 + Copyright (C) Andrew Bartlett 2004-2005 + Copyright (C) Andrew Tridgell 2001 + Copyright (C) Luke Howard 2002-2003 + Copyright (C) Stefan Metzmacher 2004-2005 + Copyright (C) Guenther Deschner 2005 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -22,17 +27,28 @@ #ifdef HAVE_KRB5 -static DATA_BLOB unwrap_pac(DATA_BLOB *auth_data) +BOOL unwrap_pac(TALLOC_CTX *mem_ctx, DATA_BLOB *auth_data, DATA_BLOB *unwrapped_pac_data) { DATA_BLOB pac_contents; ASN1_DATA data; int data_type; + if (!auth_data->length) { + return False; + } + asn1_load(&data, *auth_data); asn1_start_tag(&data, ASN1_SEQUENCE(0)); asn1_start_tag(&data, ASN1_SEQUENCE(0)); asn1_start_tag(&data, ASN1_CONTEXT(0)); asn1_read_Integer(&data, &data_type); + + if (data_type != KRB5_AUTHDATA_WIN2K_PAC ) { + DEBUG(10,("authorization data is not a Windows PAC (type: %d)\n", data_type)); + asn1_free(&data); + return False; + } + asn1_end_tag(&data); asn1_start_tag(&data, ASN1_CONTEXT(1)); asn1_read_OctetString(&data, &pac_contents); @@ -40,7 +56,12 @@ asn1_end_tag(&data); asn1_end_tag(&data); asn1_free(&data); - return pac_contents; + + *unwrapped_pac_data = data_blob_talloc(mem_ctx, pac_contents.data, pac_contents.length); + + data_blob_free(&pac_contents); + + return True; } static BOOL pac_io_logon_name(const char *desc, PAC_LOGON_NAME *logon_name, @@ -75,6 +96,8 @@ } + +#if 0 /* Unused (handled now in net_io_user_info3()) - Guenther */ static BOOL pac_io_krb_sids(const char *desc, KRB_SID_AND_ATTRS *sid_and_attr, prs_struct *ps, int depth) { @@ -159,6 +182,7 @@ return True; } +#endif static BOOL pac_io_group_membership(const char *desc, GROUP_MEMBERSHIP *membership, @@ -216,27 +240,34 @@ } +#if 0 /* Unused, replaced using an expanded net_io_user_info3() now - Guenther */ static BOOL pac_io_pac_logon_info(const char *desc, PAC_LOGON_INFO *info, prs_struct *ps, int depth) { - uint32 garbage; + uint32 garbage, i; + if (NULL == info) return False; prs_debug(ps, depth, desc, "pac_io_pac_logon_info"); depth++; - if (!prs_uint32("unknown", ps, depth, &garbage)) + if (!prs_align(ps)) return False; - if (!prs_uint32("unknown", ps, depth, &garbage)) + if (!prs_uint32("unknown", ps, depth, &garbage)) /* 00081001 */ return False; + if (!prs_uint32("unknown", ps, depth, &garbage)) /* cccccccc */ + return False; if (!prs_uint32("bufferlen", ps, depth, &garbage)) return False; - if (!prs_uint32("bufferlenhi", ps, depth, &garbage)) + if (!prs_uint32("bufferlenhi", ps, depth, &garbage)) /* 00000000 */ return False; + if (!prs_uint32("pointer", ps, depth, &garbage)) return False; + if (!prs_align(ps)) + return False; if (!smb_io_time("logon_time", &info->logon_time, ps, depth)) return False; if (!smb_io_time("logoff_time", &info->logoff_time, ps, depth)) @@ -270,7 +301,7 @@ if (!prs_uint16("logon_count", ps, depth, &info->logon_count)) return False; - if (!prs_uint16("reserved12", ps, depth, &info->reserved12)) + if (!prs_uint16("bad_password_count", ps, depth, &info->bad_password_count)) return False; if (!prs_uint32("user_rid", ps, depth, &info->user_rid)) return False; @@ -287,14 +318,8 @@ if (!prs_uint32("user_flags", ps, depth, &info->user_flags)) return False; - if (!prs_uint32("reserved13.0", ps, depth, &info->reserved13[0])) + if (!prs_uint8s(False, "session_key", ps, depth, info->session_key, 16)) return False; - if (!prs_uint32("reserved13.1", ps, depth, &info->reserved13[1])) - return False; - if (!prs_uint32("reserved13.2", ps, depth, &info->reserved13[2])) - return False; - if (!prs_uint32("reserved13.3", ps, depth, &info->reserved13[3])) - return False; if (!smb_io_unihdr("hdr_dom_controller", &info->hdr_dom_controller, ps, depth)) @@ -306,31 +331,18 @@ if (!prs_uint32("ptr_dom_sid", ps, depth, &info->ptr_dom_sid)) return False; - if (!prs_uint32("reserved16.0", ps, depth, &info->reserved16[0])) + if (!prs_uint8s(False, "lm_session_key", ps, depth, info->lm_session_key, 8)) return False; - if (!prs_uint32("reserved16.1", ps, depth, &info->reserved16[1])) - return False; - /* might be acb_info */ - if (!prs_uint32("reserved17", ps, depth, &info->reserved17)) + if (!prs_uint32("acct_flags", ps, depth, &info->acct_flags)) return False; + for (i = 0; i < 7; i++) + { + if (!prs_uint32("unkown", ps, depth, &info->unknown[i])) /* unknown */ + return False; + } - if (!prs_uint32("reserved18.0", ps, depth, &info->reserved18[0])) - return False; - if (!prs_uint32("reserved18.1", ps, depth, &info->reserved18[1])) - return False; - if (!prs_uint32("reserved18.2", ps, depth, &info->reserved18[2])) - return False; - if (!prs_uint32("reserved18.3", ps, depth, &info->reserved18[3])) - return False; - if (!prs_uint32("reserved18.4", ps, depth, &info->reserved18[4])) - return False; - if (!prs_uint32("reserved18.5", ps, depth, &info->reserved18[5])) - return False; - if (!prs_uint32("reserved18.6", ps, depth, &info->reserved18[6])) - return False; - if (!prs_uint32("sid_count", ps, depth, &info->sid_count)) return False; if (!prs_uint32("ptr_extra_sids", ps, depth, &info->ptr_extra_sids)) @@ -395,44 +407,109 @@ &info->res_group_dom_sid, ps, depth)) return False; - if (info->ptr_res_groups) + if (info->ptr_res_groups) { + + if (!(info->user_flgs & LOGON_RESOURCE_GROUPS)) { + DEBUG(0,("user_flgs attribute does not have LOGON_RESOURCE_GROUPS\n")); + /* return False; */ + } + if (!pac_io_group_membership_array("res group membership", &info->res_groups, info->res_group_count, ps, depth)) return False; + } return True; } +#endif +static BOOL pac_io_pac_logon_info(const char *desc, PAC_LOGON_INFO *info, + prs_struct *ps, int depth) +{ + uint32 garbage; + BOOL kerb_validation_info = True; + if (NULL == info) + return False; + + prs_debug(ps, depth, desc, "pac_io_pac_logon_info"); + depth++; + + if (!prs_align(ps)) + return False; + if (!prs_uint32("unknown", ps, depth, &garbage)) /* 00081001 */ + return False; + if (!prs_uint32("unknown", ps, depth, &garbage)) /* cccccccc */ + return False; + if (!prs_uint32("bufferlen", ps, depth, &garbage)) + return False; + if (!prs_uint32("bufferlenhi", ps, depth, &garbage)) /* 00000000 */ + return False; + + if(!net_io_user_info3("", &info->info3, ps, depth, 3, kerb_validation_info)) + return False; + + if (info->info3.ptr_res_group_dom_sid) { + if (!smb_io_dom_sid2("res_group_dom_sid", + &info->res_group_dom_sid, ps, depth)) + return False; + } + + if (info->info3.ptr_res_groups) { + + if (!(info->info3.user_flgs & LOGON_RESOURCE_GROUPS)) { + DEBUG(0,("user_flgs attribute does not have LOGON_RESOURCE_GROUPS\n")); + /* return False; */ + } + + if (!pac_io_group_membership_array("res group membership", + &info->res_groups, + info->info3.res_group_count, + ps, depth)) + return False; + } + + return True; +} + + + static BOOL pac_io_pac_signature_data(const char *desc, PAC_SIGNATURE_DATA *data, uint32 length, prs_struct *ps, int depth) { uint32 siglen = length - sizeof(uint32); - if (NULL == data) - return False; - prs_debug(ps, depth, desc, "pac_io_pac_signature_data"); depth++; + + if (data == NULL) + return False; + if (!prs_align(ps)) + return False; if (!prs_uint32("type", ps, depth, &data->type)) return False; - if (UNMARSHALLING(ps)) { - data->signature = PRS_ALLOC_MEM(ps, unsigned char, siglen); - if (!data->signature) { + + if (UNMARSHALLING(ps) && length) { + data->signature.buffer = PRS_ALLOC_MEM(ps, uint8, siglen); + if (!data->signature.buffer) { DEBUG(3, ("No memory available\n")); return False; } } - if (!prs_uint8s(False, "signature", ps, depth, data->signature,siglen)) + + data->signature.buf_len = siglen; + + if (!prs_uint8s(False, "signature", ps, depth, data->signature.buffer, data->signature.buf_len)) return False; + return True; } -static BOOL pac_io_pac_info_hdr_ctr(const char *desc, PAC_INFO_HDR *hdr, +static BOOL pac_io_pac_info_hdr_ctr(const char *desc, PAC_BUFFER *hdr, prs_struct *ps, int depth) { if (NULL == hdr) @@ -445,8 +522,8 @@ return False; if (hdr->offset != prs_offset(ps)) { - DEBUG(5, ("offset in header(x%x) and data(x%x) do not match\n", - hdr->offset, prs_offset(ps))); + DEBUG(5,("offset in header(x%x) and data(x%x) do not match, correcting\n", + hdr->offset, prs_offset(ps))); prs_set_offset(ps, hdr->offset); } @@ -518,10 +595,15 @@ prs_set_offset(ps, prs_offset(ps) + hdr->size); } +#if 0 + /* obscure pad */ + if (!prs_uint32("pad", ps, depth, &hdr->pad)) + return False; +#endif return True; } -static BOOL pac_io_pac_info_hdr(const char *desc, PAC_INFO_HDR *hdr, +static BOOL pac_io_pac_info_hdr(const char *desc, PAC_BUFFER *hdr, prs_struct *ps, int depth) { if (NULL == hdr) @@ -563,19 +645,19 @@ return False; if (UNMARSHALLING(ps) && data->num_buffers > 0) { - if ((data->pac_info_hdr_ptr = PRS_ALLOC_MEM(ps, PAC_INFO_HDR, data->num_buffers)) == NULL) { + if ((data->pac_buffer = PRS_ALLOC_MEM(ps, PAC_BUFFER, data->num_buffers)) == NULL) { return False; } } for (i=0; inum_buffers; i++) { - if (!pac_io_pac_info_hdr(desc, &data->pac_info_hdr_ptr[i], ps, + if (!pac_io_pac_info_hdr(desc, &data->pac_buffer[i], ps, depth)) return False; } for (i=0; inum_buffers; i++) { - if (!pac_io_pac_info_hdr_ctr(desc, &data->pac_info_hdr_ptr[i], + if (!pac_io_pac_info_hdr_ctr(desc, &data->pac_buffer[i], ps, depth)) return False; } @@ -583,25 +665,300 @@ return True; } -PAC_DATA *decode_pac_data(DATA_BLOB *auth_data, TALLOC_CTX *ctx) +static NTSTATUS check_pac_checksum(TALLOC_CTX *mem_ctx, + DATA_BLOB pac_data, + PAC_SIGNATURE_DATA *sig, + krb5_context context, + krb5_keyblock *keyblock) { - DATA_BLOB pac_data_blob = unwrap_pac(auth_data); + krb5_error_code ret; + krb5_checksum cksum; + krb5_keyusage usage = 0; + + smb_krb5_checksum_from_pac_sig(&cksum, sig); + +#ifdef HAVE_KRB5_KU_OTHER_CKSUM /* Heimdal */ + usage = KRB5_KU_OTHER_CKSUM; +#elif defined(HAVE_KRB5_KEYUSAGE_APP_DATA_CKSUM) /* MIT */ + usage = KRB5_KEYUSAGE_APP_DATA_CKSUM; +#else +#error UNKNOWN_KRB5_KEYUSAGE +#endif + + ret = smb_krb5_verify_checksum(context, + keyblock, + usage, + &cksum, + pac_data.data, + pac_data.length); + + if (ret) { + DEBUG(2,("check_pac_checksum: PAC Verification failed: %s (%d)\n", + error_message(ret), ret)); + return NT_STATUS_ACCESS_DENIED; + } + + return NT_STATUS_OK; +} + +static NTSTATUS parse_pac_data(TALLOC_CTX *mem_ctx, DATA_BLOB *pac_data_blob, PAC_DATA *pac_data) +{ prs_struct ps; - PAC_DATA *pac_data; + PAC_DATA *my_pac; - DEBUG(5,("dump_pac_data\n")); - prs_init(&ps, pac_data_blob.length, ctx, UNMARSHALL); - prs_copy_data_in(&ps, (char *)pac_data_blob.data, pac_data_blob.length); + if (!prs_init(&ps, pac_data_blob->length, mem_ctx, UNMARSHALL)) + return NT_STATUS_NO_MEMORY; + + if (!prs_copy_data_in(&ps, (char *)pac_data_blob->data, pac_data_blob->length)) + return NT_STATUS_INVALID_PARAMETER; + prs_set_offset(&ps, 0); - data_blob_free(&pac_data_blob); + my_pac = TALLOC_ZERO_P(mem_ctx, PAC_DATA); + if (!pac_io_pac_data("pac data", my_pac, &ps, 0)) + return NT_STATUS_INVALID_PARAMETER; - pac_data = TALLOC_ZERO_P(ctx, PAC_DATA); - pac_io_pac_data("pac data", pac_data, &ps, 0); - prs_mem_free(&ps); - return pac_data; + *pac_data = *my_pac; + + return NT_STATUS_OK; } +/* just for debugging, will be removed later - Guenther */ +char *pac_group_attr_string(uint32 attr) +{ + fstring name = ""; + + if (!attr) + return NULL; + + if (attr & SE_GROUP_MANDATORY) fstrcat(name, "SE_GROUP_MANDATORY "); + if (attr & SE_GROUP_ENABLED_BY_DEFAULT) fstrcat(name, "SE_GROUP_ENABLED_BY_DEFAULT "); + if (attr & SE_GROUP_ENABLED) fstrcat(name, "SE_GROUP_ENABLED "); + if (attr & SE_GROUP_OWNER) fstrcat(name, "SE_GROUP_OWNER "); + if (attr & SE_GROUP_USE_FOR_DENY_ONLY) fstrcat(name, "SE_GROUP_USE_FOR_DENY_ONLY "); + if (attr & SE_GROUP_LOGON_ID) fstrcat(name, "SE_GROUP_LOGON_ID "); + if (attr & SE_GROUP_RESOURCE) fstrcat(name, "SE_GROUP_RESOURCE "); + + return SMB_STRDUP(name); +} + +/* just for debugging, will be removed later - Guenther */ +static void dump_pac_logon_info(PAC_LOGON_INFO *logon_info) { + + DOM_SID dom_sid, res_group_dom_sid; + int i; + char *attr_string; + uint32 user_flgs = logon_info->info3.user_flgs; + + if (logon_info->info3.ptr_res_group_dom_sid) { + sid_copy(&res_group_dom_sid, &logon_info->res_group_dom_sid.sid); + } + sid_copy(&dom_sid, &logon_info->info3.dom_sid.sid); + + DEBUG(10,("The PAC:\n")); + + DEBUGADD(10,("\tUser Flags: 0x%x (%d)\n", user_flgs, user_flgs)); + if (user_flgs & LOGON_EXTRA_SIDS) + DEBUGADD(10,("\tUser Flags: LOGON_EXTRA_SIDS 0x%x (%d)\n", LOGON_EXTRA_SIDS, LOGON_EXTRA_SIDS)); + if (user_flgs & LOGON_RESOURCE_GROUPS) + DEBUGADD(10,("\tUser Flags: LOGON_RESOURCE_GROUPS 0x%x (%d)\n", LOGON_RESOURCE_GROUPS, LOGON_RESOURCE_GROUPS)); + DEBUGADD(10,("\tUser SID: %s-%d\n", sid_string_static(&dom_sid), logon_info->info3.user_rid)); + DEBUGADD(10,("\tGroup SID: %s-%d\n", sid_string_static(&dom_sid), logon_info->info3.group_rid)); + + DEBUGADD(10,("\tGroup Membership (Global and Universal Groups of own domain):\n")); + for (i = 0; i < logon_info->info3.num_groups; i++) { + attr_string = pac_group_attr_string(logon_info->info3.gids[i].attr); + DEBUGADD(10,("\t\t%d: sid: %s-%d\n\t\t attr: 0x%x == %s\n", + i, sid_string_static(&dom_sid), + logon_info->info3.gids[i].g_rid, + logon_info->info3.gids[i].attr, + attr_string)); + SAFE_FREE(attr_string); + } + + DEBUGADD(10,("\tGroup Membership (Domain Local Groups and Groups from Trusted Domains):\n")); + for (i = 0; i < logon_info->info3.num_other_sids; i++) { + attr_string = pac_group_attr_string(logon_info->info3.other_sids_attrib[i]); + DEBUGADD(10,("\t\t%d: sid: %s\n\t\t attr: 0x%x == %s\n", + i, sid_string_static(&logon_info->info3.other_sids[i].sid), + logon_info->info3.other_sids_attrib[i], + attr_string)); + SAFE_FREE(attr_string); + } + + DEBUGADD(10,("\tGroup Membership (Ressource Groups (SID History ?)):\n")); + for (i = 0; i < logon_info->info3.res_group_count; i++) { + attr_string = pac_group_attr_string(logon_info->res_groups.group_membership[i].attrs); + DEBUGADD(10,("\t\t%d: sid: %s-%d\n\t\t attr: 0x%x == %s\n", + i, sid_string_static(&res_group_dom_sid), + logon_info->res_groups.group_membership[i].rid, + logon_info->res_groups.group_membership[i].attrs, + attr_string)); + SAFE_FREE(attr_string); + } +} + +NTSTATUS decode_pac_data(TALLOC_CTX *mem_ctx, + DATA_BLOB *pac_data_blob, + krb5_context context, + krb5_keyblock *service_keyblock, + krb5_const_principal client_principal, + time_t tgs_authtime, + PAC_DATA **pac_data) + +{ + DATA_BLOB modified_pac_blob; + PAC_DATA *my_pac; + NTSTATUS nt_status; + krb5_error_code ret; + PAC_SIGNATURE_DATA *srv_sig = NULL; + PAC_SIGNATURE_DATA *kdc_sig = NULL; + PAC_LOGON_NAME *logon_name = NULL; + PAC_LOGON_INFO *logon_info = NULL; + krb5_principal client_principal_pac; + NTTIME tgs_authtime_nttime; + int i, srv_sig_pos = 0, kdc_sig_pos = 0; + fstring username; + + *pac_data = NULL; + + my_pac = talloc(mem_ctx, PAC_DATA); + if (!my_pac) { + return NT_STATUS_NO_MEMORY; + } + + nt_status = parse_pac_data(mem_ctx, pac_data_blob, my_pac); + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(0,("decode_pac_data: failed to parse PAC\n")); + return nt_status; + } + + modified_pac_blob = data_blob_talloc(mem_ctx, pac_data_blob->data, pac_data_blob->length); + + if (my_pac->num_buffers < 4) { + nt_status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + + /* store signatures */ + for (i=0; i < my_pac->num_buffers; i++) { + + switch (my_pac->pac_buffer[i].type) { + + case PAC_TYPE_SERVER_CHECKSUM: + if (!my_pac->pac_buffer[i].ctr->pac.srv_cksum) { + break; + } + + srv_sig = my_pac->pac_buffer[i].ctr->pac.srv_cksum; + + /* get position of signature buffer */ + srv_sig_pos = my_pac->pac_buffer[i].offset; + srv_sig_pos += sizeof(uint32); + + break; + + case PAC_TYPE_PRIVSVR_CHECKSUM: + if (!my_pac->pac_buffer[i].ctr->pac.privsrv_cksum) { + break; + } + + kdc_sig = my_pac->pac_buffer[i].ctr->pac.privsrv_cksum; + + /* get position of signature buffer */ + kdc_sig_pos = my_pac->pac_buffer[i].offset; + kdc_sig_pos += sizeof(uint32); + + break; + + case PAC_TYPE_LOGON_NAME: + if (!my_pac->pac_buffer[i].ctr->pac.logon_name) { + break; + } + + logon_name = my_pac->pac_buffer[i].ctr->pac.logon_name; + break; + + case PAC_TYPE_LOGON_INFO: + if (!my_pac->pac_buffer[i].ctr->pac.logon_info) { + break; + } + + logon_info = my_pac->pac_buffer[i].ctr->pac.logon_info; + break; + } + + } + + if (!srv_sig || !kdc_sig || !logon_name || !logon_info) { + nt_status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + + /* zero PAC_SIGNATURE_DATA signature buffer */ + memset(&modified_pac_blob.data[srv_sig_pos], '\0', srv_sig->signature.buf_len); + memset(&modified_pac_blob.data[kdc_sig_pos], '\0', kdc_sig->signature.buf_len); + + /* check server signature */ + nt_status = check_pac_checksum(mem_ctx, modified_pac_blob, srv_sig, context, service_keyblock); + if (!NT_STATUS_IS_OK(nt_status)) { + DEBUG(0,("decode_pac_data: failed to verify PAC server signature\n")); + goto out; + } + + /* Convert to NT time, so as not to loose accuracy in comparison */ + unix_to_nt_time(&tgs_authtime_nttime, tgs_authtime); + + if (!nt_time_equals(&tgs_authtime_nttime, &logon_name->logon_time)) { + + DEBUG(2,("decode_pac_data: Logon time mismatch between ticket and PAC!\n")); + DEBUGADD(2, ("decode_pac_data: PAC: %s\n", + http_timestring(nt_time_to_unix(&logon_name->logon_time)))); + DEBUGADD(2, ("decode_pac_data: Ticket: %s\n", + http_timestring(nt_time_to_unix(&tgs_authtime_nttime)))); + + nt_status = NT_STATUS_ACCESS_DENIED; + goto out; + } + + if (!logon_name->len) { + DEBUG(2,("decode_pac_data: No Logon Name available\n")); + nt_status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + rpcstr_pull(username, logon_name->username, sizeof(username), -1, STR_TERMINATE); + + ret = smb_krb5_parse_name_norealm(context, username, &client_principal_pac); + if (ret) { + DEBUG(2,("decode_pac_data: Could not parse name from incoming PAC: [%s]: %s\n", + username, error_message(ret))); + nt_status = NT_STATUS_INVALID_PARAMETER; + goto out; + } + + if (!smb_krb5_principal_compare_any_realm(context, client_principal, client_principal_pac)) { + DEBUG(2,("decode_pac_data: Name in PAC [%s] does not match principal name in ticket\n", + username)); + nt_status = NT_STATUS_ACCESS_DENIED; + goto out; + } + + DEBUG(10,("Successfully validated Kerberos PAC\n")); + + dump_pac_logon_info(logon_info); + + *pac_data = my_pac; + + nt_status = NT_STATUS_OK; + +out: + if (client_principal_pac) { + krb5_free_principal(context, client_principal_pac); + } + + return nt_status; +} + #endif Index: utils/ntlm_auth.c =================================================================== --- utils/ntlm_auth.c (revision 10425) +++ utils/ntlm_auth.c (working copy) @@ -793,6 +793,7 @@ DATA_BLOB token; NTSTATUS status; ssize_t len; + TALLOC_CTX *mem_ctx = talloc_init("manage_gss_spnego_request"); char *user = NULL; char *domain = NULL; @@ -895,7 +896,6 @@ if (strcmp(request.negTokenInit.mechTypes[0], OID_KERBEROS5_OLD) == 0) { char *principal; - DATA_BLOB auth_data; DATA_BLOB ap_rep; DATA_BLOB session_key; @@ -910,11 +910,13 @@ response.negTokenTarg.mechListMIC = data_blob(NULL, 0); response.negTokenTarg.responseToken = data_blob(NULL, 0); - status = ads_verify_ticket(lp_realm(), + status = ads_verify_ticket(mem_ctx, lp_realm(), &request.negTokenInit.mechToken, - &principal, &auth_data, &ap_rep, + &principal, NULL, &ap_rep, &session_key); + talloc_destroy(mem_ctx); + /* Now in "principal" we have the name we are authenticated as. */ @@ -934,7 +936,6 @@ user = SMB_STRDUP(principal); data_blob_free(&ap_rep); - data_blob_free(&auth_data); SAFE_FREE(principal); }