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 krb5_error_code ads_keytab_verify_for_principal(krb5_context context, krb5_auth_context auth_context, const krb5_keytab_entry * kt_entry, const DATA_BLOB * ticket, const char * svc_principal, krb5_ticket ** pp_tkt, krb5_keyblock ** keyblock) { krb5_error_code ret = 0; krb5_keytab keytab = NULL; krb5_data packet; char * entry_princ_s = NULL; ret = krb5_kt_default(context, &keytab); if (ret) { DEBUG(1, ("ads_keytab_verify_for_principal: krb5_kt_default failed (%s)\n", error_message(ret))); goto out; } ret = smb_krb5_unparse_name(context, kt_entry->principal, &entry_princ_s); if (ret) { DEBUG(1, ("ads_keytab_verify_for_principal: smb_krb5_unparse_name failed (%s)\n", error_message(ret))); goto out; } if (!strequal(entry_princ_s, svc_principal)) { ret = KRB5KRB_AP_ERR_BADMATCH; goto out; } 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, keytab, pp_tkt, keyblock); if (ret) { DEBUG(3,("ads_keytab_verify_for_principal: " "failed for principal %s: %s\n", entry_princ_s, error_message(ret))); goto out; } DEBUG(3,("ads_keytab_verify_for_principal: succeeded for principal %s\n", entry_princ_s)); out: /* Free the name we parsed. */ SAFE_FREE(entry_princ_s); if (keytab) { krb5_kt_close(context, keytab); keytab = NULL; } return ret; }