static bool ads_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_kt_cursor kt_cursor; krb5_keytab_entry kt_entry; char *valid_princ_formats[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL }; char *entry_princ_s = NULL; fstring my_name, my_fqdn; int i; int number_matched_principals = 0; krb5_data packet; *pp_tkt = NULL; *keyblock = NULL; *perr = 0; /* Generate the list of principal names which we expect * clients might want to use for authenticating to the file * service. We allow name$,{host,cifs}/{name,fqdn,name.REALM}. */ fstrcpy(my_name, global_myname()); my_fqdn[0] = '\0'; name_to_fqdn(my_fqdn, global_myname()); if (asprintf(&valid_princ_formats[0], "%s$@%s", my_name, lp_realm()) == -1) { goto out; } if (asprintf(&valid_princ_formats[1], "host/%s@%s", my_name, lp_realm()) == -1) { goto out; } if (asprintf(&valid_princ_formats[2], "host/%s@%s", my_fqdn, lp_realm()) == -1) { goto out; } if (asprintf(&valid_princ_formats[3], "host/%s.%s@%s", my_name, lp_realm(), lp_realm()) == -1) { goto out; } if (asprintf(&valid_princ_formats[4], "cifs/%s@%s", my_name, lp_realm()) == -1) { goto out; } if (asprintf(&valid_princ_formats[5], "cifs/%s@%s", my_fqdn, lp_realm()) == -1) { goto out; } if (asprintf(&valid_princ_formats[6], "cifs/%s.%s@%s", my_name, lp_realm(), lp_realm()) == -1) { goto out; } ZERO_STRUCT(kt_entry); ZERO_STRUCT(kt_cursor); ret = smb_krb5_open_keytab(context, NULL, False, &keytab); if (ret) { DEBUG(1, ("ads_keytab_verify_ticket: smb_krb5_open_keytab failed (%s)\n", error_message(ret))); goto out; } /* Iterate through the keytab. For each key, if the principal * name case-insensitively matches one of the allowed formats, * try verifying the ticket using that principal. */ ret = krb5_kt_start_seq_get(context, keytab, &kt_cursor); if (ret) { DEBUG(1, ("ads_keytab_verify_ticket: krb5_kt_start_seq_get failed (%s)\n", error_message(ret))); goto out; } while (!auth_ok && (krb5_kt_next_entry(context, keytab, &kt_entry, &kt_cursor) == 0)) { ret = smb_krb5_unparse_name(context, kt_entry.principal, &entry_princ_s); if (ret) { DEBUG(1, ("ads_keytab_verify_ticket: smb_krb5_unparse_name failed (%s)\n", error_message(ret))); goto out; } for (i = 0; i < ARRAY_SIZE(valid_princ_formats); i++) { if (!strequal(entry_princ_s, valid_princ_formats[i])) { continue; } number_matched_principals++; packet.length = ticket->length; packet.data = (char *)ticket->data; *pp_tkt = NULL; ret = krb5_rd_req_return_keyblock_from_keytab(context, &auth_context, &packet, kt_entry.principal, keytab, NULL, pp_tkt, keyblock); if (ret) { DEBUG(10,("ads_keytab_verify_ticket: " "krb5_rd_req_return_keyblock_from_keytab(%s) failed: %s\n", entry_princ_s, error_message(ret))); /* workaround for MIT: * as krb5_ktfile_get_entry will explicitly * close the krb5_keytab as soon as krb5_rd_req * has successfully decrypted the ticket but the * ticket is not valid yet (due to clockskew) * there is no point in querying more keytab * entries - Guenther */ if (ret == KRB5KRB_AP_ERR_TKT_NYV || ret == KRB5KRB_AP_ERR_TKT_EXPIRED || ret == KRB5KRB_AP_ERR_SKEW) { break; } } else { 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; } } /* Free the name we parsed. */ SAFE_FREE(entry_princ_s); /* Free the entry we just read. */ smb_krb5_kt_free_entry(context, &kt_entry); ZERO_STRUCT(kt_entry); } krb5_kt_end_seq_get(context, keytab, &kt_cursor); ZERO_STRUCT(kt_cursor); out: for (i = 0; i < ARRAY_SIZE(valid_princ_formats); i++) { SAFE_FREE(valid_princ_formats[i]); } if (!auth_ok) { if (!number_matched_principals) { DEBUG(3, ("ads_keytab_verify_ticket: no keytab principals matched expected file service name.\n")); } else { DEBUG(3, ("ads_keytab_verify_ticket: krb5_rd_req failed for all %d matched keytab principals\n", number_matched_principals)); } } SAFE_FREE(entry_princ_s); { krb5_keytab_entry zero_kt_entry; ZERO_STRUCT(zero_kt_entry); if (memcmp(&zero_kt_entry, &kt_entry, sizeof(krb5_keytab_entry))) { smb_krb5_kt_free_entry(context, &kt_entry); } } { krb5_kt_cursor zero_csr; ZERO_STRUCT(zero_csr); if ((memcmp(&kt_cursor, &zero_csr, sizeof(krb5_kt_cursor)) != 0) && keytab) { krb5_kt_end_seq_get(context, keytab, &kt_cursor); } } if (keytab) { krb5_kt_close(context, keytab); } *perr = ret; return auth_ok; }
static int negprot_spnego(char *p, uint8 *pkeylen) { DATA_BLOB blob; nstring dos_name; fstring unix_name; char guid[17]; const char *OIDs_krb5[] = {OID_KERBEROS5, OID_KERBEROS5_OLD, OID_NTLMSSP, NULL}; const char *OIDs_plain[] = {OID_NTLMSSP, NULL}; int len; global_spnego_negotiated = True; ZERO_STRUCT(guid); safe_strcpy(unix_name, global_myname(), sizeof(unix_name)-1); strlower_m(unix_name); push_ascii_nstring(dos_name, unix_name); safe_strcpy(guid, dos_name, sizeof(guid)-1); #ifdef DEVELOPER /* valgrind fixer... */ { size_t sl = strlen(guid); if (sizeof(guid)-sl) memset(&guid[sl], '\0', sizeof(guid)-sl); } #endif /* strangely enough, NT does not sent the single OID NTLMSSP when not a ADS member, it sends no OIDs at all OLD COMMENT : "we can't do this until we teach our sesssion setup parser to know about raw NTLMSSP (clients send no ASN.1 wrapping if we do this)" Our sessionsetup code now handles raw NTLMSSP connects, so we can go back to doing what W2K3 does here. This is needed to make PocketPC 2003 CIFS connections work with SPNEGO. See bugzilla bugs #1828 and #3133 for details. JRA. */ if (lp_security() != SEC_ADS && !lp_use_kerberos_keytab()) { #if 0 /* Code for PocketPC client */ blob = data_blob(guid, 16); #else /* Code for standalone WXP client */ blob = spnego_gen_negTokenInit(guid, OIDs_plain, "NONE"); #endif } else { fstring myname; char *host_princ_s = NULL; name_to_fqdn(myname, global_myname()); strlower_m(myname); asprintf(&host_princ_s, "cifs/%s@%s", myname, lp_realm()); blob = spnego_gen_negTokenInit(guid, OIDs_krb5, host_princ_s); SAFE_FREE(host_princ_s); } memcpy(p, blob.data, blob.length); len = blob.length; if (len > 256) { DEBUG(0,("negprot_spnego: blob length too long (%d)\n", len)); len = 255; } data_blob_free(&blob); if (lp_security() != SEC_ADS && !lp_use_kerberos_keytab()) { *pkeylen = 0; } else { *pkeylen = len; } return len; }
DATA_BLOB negprot_spnego(TALLOC_CTX *ctx, struct smbd_server_connection *sconn) { DATA_BLOB blob = data_blob_null; DATA_BLOB blob_out = data_blob_null; nstring dos_name; fstring unix_name; #ifdef DEVELOPER size_t slen; #endif const char *OIDs_krb5[] = {OID_KERBEROS5, OID_KERBEROS5_OLD, OID_NTLMSSP, NULL}; const char *OIDs_ntlm[] = {OID_NTLMSSP, NULL}; sconn->smb1.negprot.spnego = true; /* strangely enough, NT does not sent the single OID NTLMSSP when not a ADS member, it sends no OIDs at all OLD COMMENT : "we can't do this until we teach our sesssion setup parser to know about raw NTLMSSP (clients send no ASN.1 wrapping if we do this)" Our sessionsetup code now handles raw NTLMSSP connects, so we can go back to doing what W2K3 does here. This is needed to make PocketPC 2003 CIFS connections work with SPNEGO. See bugzilla bugs #1828 and #3133 for details. JRA. */ if (lp_security() != SEC_ADS && !USE_KERBEROS_KEYTAB) { #if 0 /* Code for PocketPC client */ blob = data_blob(guid, 16); #else /* Code for standalone WXP client */ blob = spnego_gen_negTokenInit(ctx, OIDs_ntlm, NULL, "NONE"); #endif } else if (!lp_send_spnego_principal()) { /* By default, Windows 2008 and later sends not_defined_in_RFC4178@please_ignore */ blob = spnego_gen_negTokenInit(ctx, OIDs_krb5, NULL, ADS_IGNORE_PRINCIPAL); } else { fstring myname; char *host_princ_s = NULL; name_to_fqdn(myname, global_myname()); strlower_m(myname); if (asprintf(&host_princ_s, "cifs/%s@%s", myname, lp_realm()) == -1) { return data_blob_null; } blob = spnego_gen_negTokenInit(ctx, OIDs_krb5, NULL, host_princ_s); SAFE_FREE(host_princ_s); } if (blob.length == 0 || blob.data == NULL) { return data_blob_null; } blob_out = data_blob_talloc(ctx, NULL, 16 + blob.length); if (blob_out.data == NULL) { data_blob_free(&blob); return data_blob_null; } memset(blob_out.data, '\0', 16); safe_strcpy(unix_name, global_myname(), sizeof(unix_name)-1); strlower_m(unix_name); push_ascii_nstring(dos_name, unix_name); strlcpy((char *)blob_out.data, dos_name, 17); #ifdef DEVELOPER /* Fix valgrind 'uninitialized bytes' issue. */ slen = strlen(dos_name); if (slen < 16) { memset(blob_out.data+slen, '\0', 16 - slen); } #endif memcpy(&blob_out.data[16], blob.data, blob.length); data_blob_free(&blob); return blob_out; }
DATA_BLOB negprot_spnego(TALLOC_CTX *ctx, struct smbd_server_connection *sconn) { DATA_BLOB blob = data_blob_null; DATA_BLOB blob_out = data_blob_null; nstring dos_name; fstring unix_name; NTSTATUS status; #ifdef DEVELOPER size_t slen; #endif const char *OIDs_krb5[] = {OID_KERBEROS5, OID_KERBEROS5_OLD, OID_NTLMSSP, NULL}; const char *OIDs_ntlm[] = {OID_NTLMSSP, NULL}; struct auth_ntlmssp_state *auth_ntlmssp_state; sconn->use_gensec_hook = false; /* See if we can get an SPNEGO blob out of the gensec hook (if auth_samba4 is loaded) */ status = auth_ntlmssp_prepare(sconn->remote_address, &auth_ntlmssp_state); if (NT_STATUS_IS_OK(status)) { status = auth_generic_start(auth_ntlmssp_state, GENSEC_OID_SPNEGO); if (NT_STATUS_IS_OK(status)) { status = auth_ntlmssp_update(auth_ntlmssp_state, ctx, data_blob_null, &blob); /* If we get the list of OIDs, the 'OK' answer * is NT_STATUS_MORE_PROCESSING_REQUIRED */ if (NT_STATUS_EQUAL(status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { sconn->use_gensec_hook = true; } } TALLOC_FREE(auth_ntlmssp_state); } sconn->smb1.negprot.spnego = true; /* strangely enough, NT does not sent the single OID NTLMSSP when not a ADS member, it sends no OIDs at all OLD COMMENT : "we can't do this until we teach our sesssion setup parser to know about raw NTLMSSP (clients send no ASN.1 wrapping if we do this)" Our sessionsetup code now handles raw NTLMSSP connects, so we can go back to doing what W2K3 does here. This is needed to make PocketPC 2003 CIFS connections work with SPNEGO. See bugzilla bugs #1828 and #3133 for details. JRA. */ if (sconn->use_gensec_hook) { /* blob initialised above */ } else if (lp_security() != SEC_ADS && !USE_KERBEROS_KEYTAB) { #if 0 /* Code for PocketPC client */ blob = data_blob(guid, 16); #else /* Code for standalone WXP client */ blob = spnego_gen_negTokenInit(ctx, OIDs_ntlm, NULL, "NONE"); #endif } else if (!lp_send_spnego_principal()) { /* By default, Windows 2008 and later sends not_defined_in_RFC4178@please_ignore */ blob = spnego_gen_negTokenInit(ctx, OIDs_krb5, NULL, ADS_IGNORE_PRINCIPAL); } else { fstring myname; char *host_princ_s = NULL; name_to_fqdn(myname, lp_netbios_name()); strlower_m(myname); if (asprintf(&host_princ_s, "cifs/%s@%s", myname, lp_realm()) == -1) { return data_blob_null; } blob = spnego_gen_negTokenInit(ctx, OIDs_krb5, NULL, host_princ_s); SAFE_FREE(host_princ_s); } if (blob.length == 0 || blob.data == NULL) { return data_blob_null; } blob_out = data_blob_talloc(ctx, NULL, 16 + blob.length); if (blob_out.data == NULL) { data_blob_free(&blob); return data_blob_null; } memset(blob_out.data, '\0', 16); checked_strlcpy(unix_name, lp_netbios_name(), sizeof(unix_name)); strlower_m(unix_name); push_ascii_nstring(dos_name, unix_name); strlcpy((char *)blob_out.data, dos_name, 17); #ifdef DEVELOPER /* Fix valgrind 'uninitialized bytes' issue. */ slen = strlen(dos_name); if (slen < 16) { memset(blob_out.data+slen, '\0', 16 - slen); } #endif memcpy(&blob_out.data[16], blob.data, blob.length); data_blob_free(&blob); return blob_out; }