krb5_error_code k5_pac_validate_client(krb5_context context, const krb5_pac pac, krb5_timestamp authtime, krb5_const_principal principal) { krb5_error_code ret; krb5_data client_info; char *pac_princname; unsigned char *p; krb5_timestamp pac_authtime; krb5_ui_2 pac_princname_length; krb5_int64 pac_nt_authtime; krb5_principal pac_principal; ret = k5_pac_locate_buffer(context, pac, PAC_CLIENT_INFO, &client_info); if (ret != 0) return ret; if (client_info.length < PAC_CLIENT_INFO_LENGTH) return ERANGE; p = (unsigned char *)client_info.data; pac_nt_authtime = load_64_le(p); p += 8; pac_princname_length = load_16_le(p); p += 2; ret = k5_time_to_seconds_since_1970(pac_nt_authtime, &pac_authtime); if (ret != 0) return ret; if (client_info.length < PAC_CLIENT_INFO_LENGTH + pac_princname_length || pac_princname_length % 2) return ERANGE; ret = krb5int_ucs2lecs_to_utf8s(p, (size_t)pac_princname_length / 2, &pac_princname, NULL); if (ret != 0) return ret; ret = krb5_parse_name_flags(context, pac_princname, 0, &pac_principal); if (ret != 0) { free(pac_princname); return ret; } free(pac_princname); if (pac_authtime != authtime || !krb5_principal_compare_flags(context, pac_principal, principal, KRB5_PRINCIPAL_COMPARE_IGNORE_REALM)) ret = KRB5KRB_AP_WRONG_PRINC; krb5_free_principal(context, pac_principal); return ret; }
static int mit_samba_check_s4u2proxy(struct mit_samba_context *ctx, hdb_entry_ex *entry, const char *target_name, bool is_nt_enterprise_name) { krb5_principal target_principal; int flags = 0; int ret; if (is_nt_enterprise_name) { flags = KRB5_PRINCIPAL_PARSE_ENTERPRISE; } ret = krb5_parse_name_flags(ctx->context, target_name, flags, &target_principal); if (ret) { return ret; } ret = samba_kdc_check_identical_client_and_server(ctx->context, ctx->db_ctx, entry, target_principal); krb5_free_principal(ctx->context, target_principal); return ret; }
static WERROR dns_domain_from_principal(TALLOC_CTX *mem_ctx, struct smb_krb5_context *smb_krb5_context, const char *name, struct drsuapi_DsNameInfo1 *info1) { krb5_error_code ret; krb5_principal principal; /* perhaps it's a principal with a realm, so return the right 'domain only' response */ const char *realm; ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, name, KRB5_PRINCIPAL_PARSE_REQUIRE_REALM, &principal); if (ret) { info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; return WERR_OK; } /* This isn't an allocation assignemnt, so it is free'ed with the krb5_free_principal */ realm = krb5_principal_get_realm(smb_krb5_context->krb5_context, principal); info1->dns_domain_name = talloc_strdup(mem_ctx, realm); krb5_free_principal(smb_krb5_context->krb5_context, principal); W_ERROR_HAVE_NO_MEMORY(info1->dns_domain_name); info1->status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY; return WERR_OK; }
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_parse_name(krb5_context context, const char *name, krb5_principal *principal) { return krb5_parse_name_flags(context, name, 0, principal); }
static void parse_name_realm(krb5_context context, const char *name, int flags, const char *realm, krb5_principal *princ) { krb5_error_code ret; if (realm) flags |= KRB5_PRINCIPAL_PARSE_NO_DEF_REALM; if ((ret = krb5_parse_name_flags(context, name, flags, princ)) != 0) krb5_err(context, 1, ret, "krb5_parse_name_flags"); if (realm && krb5_principal_get_realm(context, *princ) == NULL) set_princ_realm(context, *princ, realm); }
krb5_error_code sss_krb5_parse_name_flags(krb5_context context, const char *name, int flags, krb5_principal *principal) { #ifdef HAVE_KRB5_PARSE_NAME_FLAGS return krb5_parse_name_flags(context, name, flags, principal); #else if (flags != 0) { DEBUG(SSSDBG_MINOR_FAILURE, "krb5_parse_name_flags not available on " \ "this plattform, names are parsed " \ "without flags. Some features like " \ "enterprise principals might not work " \ "as expected.\n"); } return krb5_parse_name(context, name, principal); #endif }
krb5_error_code _krb5_get_default_principal_local(krb5_context context, krb5_principal *princ) { /* See if we can get the principal first. We only expect this to work if logged into a domain. */ { char username[1024]; ULONG sz = sizeof(username); if (GetUserNameEx(NameUserPrincipal, username, &sz)) { return krb5_parse_name_flags(context, username, KRB5_PRINCIPAL_PARSE_ENTERPRISE, princ); } } /* Just get the Windows username. This should pretty much always work. */ { char username[1024]; DWORD dsz = sizeof(username); if (GetUserName(username, &dsz)) { return krb5_make_principal(context, princ, NULL, username, NULL); } } /* Failing that, we look at the environment */ { const char * username = get_env_user(); if (username == NULL) { krb5_set_error_string(context, "unable to figure out current principal"); return ENOTTY; /* Really? */ } return krb5_make_principal(context, princ, NULL, username, NULL); } }
static int mit_samba_check_s4u2proxy(struct mit_samba_context *ctx, hdb_entry_ex *entry, const char *target_name, bool is_nt_enterprise_name) { #if 1 /* * This is disabled because mit_samba_update_pac_data() does not handle * S4U_DELEGATION_INFO */ return KRB5KDC_ERR_BADOPTION; #else krb5_principal target_principal; int flags = 0; int ret; if (is_nt_enterprise_name) { flags = KRB5_PRINCIPAL_PARSE_ENTERPRISE; } ret = krb5_parse_name_flags(ctx->context, target_name, flags, &target_principal); if (ret) { return ret; } ret = samba_kdc_check_s4u2proxy(ctx->context, ctx->db_ctx, skdc_entry, target_principal); krb5_free_principal(ctx->context, target_principal); return ret; #endif }
DWORD VMCALoginUserPrivate( PCSTR pszUserName, const char* pszPassword ) /* VMCALoginUser Creates a TGT cache for the Process to communicate with the VMCA */ { DWORD dwError = 0; krb5_context context = NULL; krb5_get_init_creds_opt *opt = NULL; krb5_principal principal = NULL; krb5_ccache ccache = NULL; krb5_creds creds = { 0 }; krb5_error_code err_code = 0; #ifdef DEBUG_KRB PSTR pszErrstr = NULL; #endif err_code = krb5_init_context(&context); BAIL_ON_ERROR(err_code); // it is assumed that pszUserName is in user@REALM format. err_code = krb5_parse_name_flags( context, pszUserName, KRB5_PRINCIPAL_PARSE_REQUIRE_REALM, &principal); BAIL_ON_ERROR(err_code); // Find the default cred cache err_code = krb5_cc_default(context, &ccache); BAIL_ON_ERROR(err_code); #ifdef DEBUG_KRB printf("Default Cache Name is : %s\n", krb5_cc_default_name(context)); #endif err_code = krb5_get_init_creds_opt_alloc(context, &opt); BAIL_ON_ERROR(err_code); krb5_get_init_creds_opt_set_tkt_life(opt, 2 * 60 * 60); //2 hours ticket krb5_get_init_creds_opt_set_forwardable(opt, 1); // Let us get the Creds from the Kerberos Server err_code = krb5_get_init_creds_password( context, &creds, principal, (PSTR) pszPassword, NULL, NULL, 0, NULL, opt); BAIL_ON_ERROR(err_code); err_code = krb5_cc_store_cred(context, ccache, &creds); if ( err_code == KRB5_FCC_NOFILE){ krb5_cc_initialize(context, ccache, principal); err_code = krb5_cc_store_cred(context, ccache, &creds); BAIL_ON_ERROR(err_code); } BAIL_ON_ERROR(err_code); cleanup: if(principal != NULL) { krb5_free_principal(context, principal); } if(opt != NULL){ krb5_get_init_creds_opt_free(context,opt); } if ( ccache != NULL) { krb5_cc_close(context, ccache); } krb5_free_cred_contents(context, &creds); if(context != NULL){ krb5_free_context(context); } return dwError; error : #ifdef DEBUG_KRB pszErrstr = (PSTR) krb5_get_error_message(context, err_code); printf("Error code : %d\n", err_code); printf("Error Message : %s\n", pszErrstr); krb5_free_error_message(context, (const char *)pszErrstr); #endif dwError = err_code; goto cleanup; }
/* Perform one iteration of attempting to get credentials. This includes * searching existing ccache for requested service if INIT_CREDS. */ static kadm5_ret_t gic_iter(kadm5_server_handle_t handle, enum init_type init_type, krb5_ccache ccache, krb5_principal client, char *pass, char *svcname, char *realm, krb5_principal *server_out) { kadm5_ret_t code; krb5_context ctx; krb5_keytab kt; krb5_get_init_creds_opt *opt = NULL; krb5_creds mcreds, outcreds; *server_out = NULL; ctx = handle->context; kt = NULL; memset(&opt, 0, sizeof(opt)); memset(&mcreds, 0, sizeof(mcreds)); memset(&outcreds, 0, sizeof(outcreds)); /* Credentials for kadmin don't need to be forwardable or proxiable. */ if (init_type != INIT_CREDS) { code = krb5_get_init_creds_opt_alloc(ctx, &opt); krb5_get_init_creds_opt_set_forwardable(opt, 0); krb5_get_init_creds_opt_set_proxiable(opt, 0); krb5_get_init_creds_opt_set_out_ccache(ctx, opt, ccache); if (init_type == INIT_ANONYMOUS) krb5_get_init_creds_opt_set_anonymous(opt, 1); } if (init_type == INIT_PASS || init_type == INIT_ANONYMOUS) { code = krb5_get_init_creds_password(ctx, &outcreds, client, pass, krb5_prompter_posix, NULL, 0, svcname, opt); if (code) goto error; } else if (init_type == INIT_SKEY) { if (pass) { code = krb5_kt_resolve(ctx, pass, &kt); if (code) goto error; } code = krb5_get_init_creds_keytab(ctx, &outcreds, client, kt, 0, svcname, opt); if (pass) krb5_kt_close(ctx, kt); if (code) goto error; } else if (init_type == INIT_CREDS) { mcreds.client = client; code = krb5_parse_name_flags(ctx, svcname, KRB5_PRINCIPAL_PARSE_IGNORE_REALM, &mcreds.server); if (code) goto error; code = krb5_set_principal_realm(ctx, mcreds.server, realm); if (code) goto error; code = krb5_cc_retrieve_cred(ctx, ccache, 0, &mcreds, &outcreds); krb5_free_principal(ctx, mcreds.server); if (code) goto error; } else { code = EINVAL; goto error; } /* Steal the server principal of the creds we acquired and return it to the * caller, which needs to knows what service to authenticate to. */ *server_out = outcreds.server; outcreds.server = NULL; error: krb5_free_cred_contents(ctx, &outcreds); if (opt) krb5_get_init_creds_opt_free(ctx, opt); return code; }
int main (int argc, char **argv) { krb5_error_code ret; krb5_context context; krb5_ccache ccache; krb5_principal principal; int optidx = 0; krb5_deltat ticket_life = 0; int parseflags = 0; setprogname (argv[0]); setlocale (LC_ALL, ""); bindtextdomain ("heimdal_kuser", HEIMDAL_LOCALEDIR); textdomain("heimdal_kuser"); ret = krb5_init_context (&context); if (ret == KRB5_CONFIG_BADFORMAT) errx (1, "krb5_init_context failed to parse configuration file"); else if (ret) errx(1, "krb5_init_context failed: %d", ret); if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx)) usage(1); if (help_flag) usage (0); if(version_flag) { print_version(NULL); exit(0); } argc -= optidx; argv += optidx; if (canonicalize_flag || enterprise_flag) parseflags |= KRB5_PRINCIPAL_PARSE_ENTERPRISE; if (pk_enterprise_flag) { ret = krb5_pk_enterprise_cert(context, pk_user_id, argv[0], &principal, &ent_user_id); if (ret) krb5_err(context, 1, ret, "krb5_pk_enterprise_certs"); pk_user_id = NULL; } else if (anonymous_flag) { ret = krb5_make_principal(context, &principal, argv[0], KRB5_WELLKNOWN_NAME, KRB5_ANON_NAME, NULL); if (ret) krb5_err(context, 1, ret, "krb5_make_principal"); krb5_principal_set_type(context, principal, KRB5_NT_WELLKNOWN); } else { if (argv[0]) { ret = krb5_parse_name_flags (context, argv[0], parseflags, &principal); if (ret) krb5_err (context, 1, ret, "krb5_parse_name"); } else { ret = krb5_get_default_principal (context, &principal); if (ret) krb5_err (context, 1, ret, "krb5_get_default_principal"); } } if(fcache_version) krb5_set_fcache_version(context, fcache_version); if(renewable_flag == -1) /* this seems somewhat pointless, but whatever */ krb5_appdefault_boolean(context, "kinit", krb5_principal_get_realm(context, principal), "renewable", FALSE, &renewable_flag); if(do_afslog == -1) krb5_appdefault_boolean(context, "kinit", krb5_principal_get_realm(context, principal), "afslog", TRUE, &do_afslog); if(cred_cache) ret = krb5_cc_resolve(context, cred_cache, &ccache); else { if(argc > 1) { char s[1024]; ret = krb5_cc_new_unique(context, NULL, NULL, &ccache); if(ret) krb5_err(context, 1, ret, "creating cred cache"); snprintf(s, sizeof(s), "%s:%s", krb5_cc_get_type(context, ccache), krb5_cc_get_name(context, ccache)); setenv("KRB5CCNAME", s, 1); } else { ret = krb5_cc_cache_match(context, principal, &ccache); if (ret) { const char *type; ret = krb5_cc_default (context, &ccache); if (ret) krb5_err (context, 1, ret, N_("resolving credentials cache", "")); /* * Check if the type support switching, and we do, * then do that instead over overwriting the current * default credential */ type = krb5_cc_get_type(context, ccache); if (krb5_cc_support_switch(context, type)) { krb5_cc_close(context, ccache); ret = krb5_cc_new_unique(context, type, NULL, &ccache); } } } } if (ret) krb5_err (context, 1, ret, N_("resolving credentials cache", "")); #ifndef NO_AFS if(argc > 1 && k_hasafs ()) k_setpag(); #endif if (lifetime) { int tmp = parse_time (lifetime, "s"); if (tmp < 0) errx (1, N_("unparsable time: %s", ""), lifetime); ticket_life = tmp; } if(addrs_flag == 0 && extra_addresses.num_strings > 0) krb5_errx(context, 1, N_("specifying both extra addresses and " "no addresses makes no sense", "")); { int i; krb5_addresses addresses; memset(&addresses, 0, sizeof(addresses)); for(i = 0; i < extra_addresses.num_strings; i++) { ret = krb5_parse_address(context, extra_addresses.strings[i], &addresses); if (ret == 0) { krb5_add_extra_addresses(context, &addresses); krb5_free_addresses(context, &addresses); } } free_getarg_strings(&extra_addresses); } if(renew_flag || validate_flag) { ret = renew_validate(context, renew_flag, validate_flag, ccache, server_str, ticket_life); exit(ret != 0); } get_new_tickets(context, principal, ccache, ticket_life, 1); #ifndef NO_AFS if(do_afslog && k_hasafs()) krb5_afslog(context, ccache, NULL, NULL); #endif if(argc > 1) { struct renew_ctx ctx; time_t timeout; timeout = ticket_lifetime(context, ccache, principal, server_str, NULL) / 2; ctx.context = context; ctx.ccache = ccache; ctx.principal = principal; ctx.ticket_life = ticket_life; ret = simple_execvp_timed(argv[1], argv+1, renew_func, &ctx, timeout); #define EX_NOEXEC 126 #define EX_NOTFOUND 127 if(ret == EX_NOEXEC) krb5_warnx(context, N_("permission denied: %s", ""), argv[1]); else if(ret == EX_NOTFOUND) krb5_warnx(context, N_("command not found: %s", ""), argv[1]); krb5_cc_destroy(context, ccache); #ifndef NO_AFS if(k_hasafs()) k_unlog(); #endif } else { krb5_cc_close (context, ccache); ret = 0; } krb5_free_principal(context, principal); krb5_free_context (context); return ret; }
static bool torture_pac_self_check(struct torture_context *tctx) { NTSTATUS nt_status; DATA_BLOB tmp_blob; struct PAC_DATA *pac_data; struct PAC_LOGON_INFO *logon_info; union netr_Validation validation; /* Generate a nice, arbitary keyblock */ uint8_t server_bytes[16]; uint8_t krbtgt_bytes[16]; krb5_keyblock server_keyblock; krb5_keyblock krbtgt_keyblock; krb5_error_code ret; struct smb_krb5_context *smb_krb5_context; struct auth_serversupplied_info *server_info; struct auth_serversupplied_info *server_info_out; krb5_principal client_principal; time_t logon_time = time(NULL); TALLOC_CTX *mem_ctx = tctx; torture_assert(tctx, 0 == smb_krb5_init_context(mem_ctx, NULL, tctx->lp_ctx, &smb_krb5_context), "smb_krb5_init_context"); generate_random_buffer(server_bytes, 16); generate_random_buffer(krbtgt_bytes, 16); ret = krb5_keyblock_init(smb_krb5_context->krb5_context, ENCTYPE_ARCFOUR_HMAC, server_bytes, sizeof(server_bytes), &server_keyblock); torture_assert(tctx, !ret, talloc_asprintf(tctx, "(self test) Server Keyblock encoding failed: %s", smb_get_krb5_error_message(smb_krb5_context->krb5_context, ret, mem_ctx))); ret = krb5_keyblock_init(smb_krb5_context->krb5_context, ENCTYPE_ARCFOUR_HMAC, krbtgt_bytes, sizeof(krbtgt_bytes), &krbtgt_keyblock); if (ret) { char *err = smb_get_krb5_error_message(smb_krb5_context->krb5_context, ret, mem_ctx); krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &server_keyblock); torture_fail(tctx, talloc_asprintf(tctx, "(self test) KRBTGT Keyblock encoding failed: %s", err)); } /* We need an input, and this one requires no underlying database */ nt_status = auth_anonymous_server_info(mem_ctx, lp_netbios_name(tctx->lp_ctx), &server_info); if (!NT_STATUS_IS_OK(nt_status)) { krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &server_keyblock); krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &krbtgt_keyblock); torture_fail(tctx, "auth_anonymous_server_info"); } ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, server_info->account_name, KRB5_PRINCIPAL_PARSE_NO_REALM, &client_principal); if (ret) { krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &server_keyblock); krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &krbtgt_keyblock); torture_fail(tctx, "krb5_parse_name_flags(norealm)"); } /* OK, go ahead and make a PAC */ ret = kerberos_create_pac(mem_ctx, lp_iconv_convenience(tctx->lp_ctx), server_info, smb_krb5_context->krb5_context, &krbtgt_keyblock, &server_keyblock, client_principal, logon_time, &tmp_blob); if (ret) { krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &krbtgt_keyblock); krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &server_keyblock); krb5_free_principal(smb_krb5_context->krb5_context, client_principal); torture_fail(tctx, talloc_asprintf(tctx, "(self test) PAC encoding failed: %s", smb_get_krb5_error_message(smb_krb5_context->krb5_context, ret, mem_ctx))); } dump_data(10,tmp_blob.data,tmp_blob.length); /* Now check that we can read it back (using full decode and validate) */ nt_status = kerberos_decode_pac(mem_ctx, lp_iconv_convenience(tctx->lp_ctx), &pac_data, tmp_blob, smb_krb5_context->krb5_context, &krbtgt_keyblock, &server_keyblock, client_principal, logon_time, NULL); if (!NT_STATUS_IS_OK(nt_status)) { krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &krbtgt_keyblock); krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &server_keyblock); krb5_free_principal(smb_krb5_context->krb5_context, client_principal); torture_fail(tctx, talloc_asprintf(tctx, "(self test) PAC decoding failed: %s", nt_errstr(nt_status))); } /* Now check we can read it back (using Heimdal's pac parsing) */ nt_status = kerberos_pac_blob_to_server_info(mem_ctx, lp_iconv_convenience(tctx->lp_ctx), tmp_blob, smb_krb5_context->krb5_context, &server_info_out); if (!dom_sid_equal(server_info->account_sid, server_info_out->account_sid)) { krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &krbtgt_keyblock); krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &server_keyblock); krb5_free_principal(smb_krb5_context->krb5_context, client_principal); torture_fail(tctx, talloc_asprintf(tctx, "(self test) PAC Decode resulted in *different* domain SID: %s != %s", dom_sid_string(mem_ctx, server_info->account_sid), dom_sid_string(mem_ctx, server_info_out->account_sid))); } talloc_free(server_info_out); /* Now check that we can read it back (yet again) */ nt_status = kerberos_pac_logon_info(mem_ctx, lp_iconv_convenience(tctx->lp_ctx), &logon_info, tmp_blob, smb_krb5_context->krb5_context, &krbtgt_keyblock, &server_keyblock, client_principal, logon_time, NULL); if (!NT_STATUS_IS_OK(nt_status)) { krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &krbtgt_keyblock); krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &server_keyblock); krb5_free_principal(smb_krb5_context->krb5_context, client_principal); torture_fail(tctx, talloc_asprintf(tctx, "(self test) PAC decoding (for logon info) failed: %s", nt_errstr(nt_status))); } krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &krbtgt_keyblock); krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &server_keyblock); krb5_free_principal(smb_krb5_context->krb5_context, client_principal); /* And make a server info from the samba-parsed PAC */ validation.sam3 = &logon_info->info3; nt_status = make_server_info_netlogon_validation(mem_ctx, "", 3, &validation, &server_info_out); if (!NT_STATUS_IS_OK(nt_status)) { torture_fail(tctx, talloc_asprintf(tctx, "(self test) PAC decoding (make server info) failed: %s", nt_errstr(nt_status))); } if (!dom_sid_equal(server_info->account_sid, server_info_out->account_sid)) { torture_fail(tctx, talloc_asprintf(tctx, "(self test) PAC Decode resulted in *different* domain SID: %s != %s", dom_sid_string(mem_ctx, server_info->account_sid), dom_sid_string(mem_ctx, server_info_out->account_sid))); } return true; }
static WERROR DsCrackNameSPNAlias(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, struct smb_krb5_context *smb_krb5_context, uint32_t format_flags, uint32_t format_offered, uint32_t format_desired, const char *name, struct drsuapi_DsNameInfo1 *info1) { WERROR wret; krb5_error_code ret; krb5_principal principal; const char *service, *dns_name; char *new_service; char *new_princ; enum drsuapi_DsNameStatus namestatus; /* parse principal */ ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, name, KRB5_PRINCIPAL_PARSE_NO_REALM, &principal); if (ret) { DEBUG(2, ("Could not parse principal: %s: %s", name, smb_get_krb5_error_message(smb_krb5_context->krb5_context, ret, mem_ctx))); return WERR_NOMEM; } /* grab cifs/, http/ etc */ /* This is checked for in callers, but be safe */ if (principal->name.name_string.len < 2) { info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; return WERR_OK; } service = principal->name.name_string.val[0]; dns_name = principal->name.name_string.val[1]; /* MAP it */ namestatus = LDB_lookup_spn_alias(smb_krb5_context->krb5_context, sam_ctx, mem_ctx, service, &new_service); if (namestatus == DRSUAPI_DS_NAME_STATUS_NOT_FOUND) { info1->status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY; info1->dns_domain_name = talloc_strdup(mem_ctx, dns_name); if (!info1->dns_domain_name) { krb5_free_principal(smb_krb5_context->krb5_context, principal); return WERR_NOMEM; } return WERR_OK; } else if (namestatus != DRSUAPI_DS_NAME_STATUS_OK) { info1->status = namestatus; krb5_free_principal(smb_krb5_context->krb5_context, principal); return WERR_OK; } /* ooh, very nasty playing around in the Principal... */ free(principal->name.name_string.val[0]); principal->name.name_string.val[0] = strdup(new_service); if (!principal->name.name_string.val[0]) { krb5_free_principal(smb_krb5_context->krb5_context, principal); return WERR_NOMEM; } /* reform principal */ ret = krb5_unparse_name_flags(smb_krb5_context->krb5_context, principal, KRB5_PRINCIPAL_UNPARSE_NO_REALM, &new_princ); if (ret) { krb5_free_principal(smb_krb5_context->krb5_context, principal); return WERR_NOMEM; } wret = DsCrackNameOneName(sam_ctx, mem_ctx, format_flags, format_offered, format_desired, new_princ, info1); free(new_princ); if (W_ERROR_IS_OK(wret) && (info1->status == DRSUAPI_DS_NAME_STATUS_NOT_FOUND)) { info1->status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY; info1->dns_domain_name = talloc_strdup(mem_ctx, dns_name); if (!info1->dns_domain_name) { wret = WERR_NOMEM; } } krb5_free_principal(smb_krb5_context->krb5_context, principal); return wret; }
krb5_error_code KRB5_CALLCONV krb5_parse_name(krb5_context context, const char *name, krb5_principal *principal_out) { return krb5_parse_name_flags(context, name, 0, principal_out); }
static UINT32 transport_krb5_check_account(rdpTransport* transport, char* username, char* domain, char* passwd) { krb5_error_code ret; krb5_context context = NULL; krb5_principal principal = NULL; char address[256]; krb5_ccache ccache; krb5_init_creds_context ctx = NULL; _snprintf(address, sizeof(address), "%s@%s", username, domain); /* Create a krb5 library context */ if ((ret = krb5_init_context(&context)) != 0) WLog_Print(transport->log, WLOG_ERROR, "krb5_init_context failed with error %d", (int)ret); else if ((ret = krb5_parse_name_flags(context, address, 0, &principal)) != 0) WLog_Print(transport->log, WLOG_ERROR, "krb5_parse_name_flags failed with error %d", (int)ret); /* Find a credential cache with a specified client principal */ else if ((ret = krb5_cc_cache_match(context, principal, &ccache)) != 0) { if ((ret = krb5_cc_default(context, &ccache)) != 0) WLog_Print(transport->log, WLOG_ERROR, "krb5 failed to resolve credentials cache with error %d", (int)ret); } if (ret != KRB5KDC_ERR_NONE) goto out; /* Create a context for acquiring initial credentials */ else if ((ret = krb5_init_creds_init(context, principal, NULL, NULL, 0, NULL, &ctx)) != 0) { WLog_Print(transport->log, WLOG_WARN, "krb5_init_creds_init returned error %d", (int)ret); goto out; } /* Set a password for acquiring initial credentials */ else if ((ret = krb5_init_creds_set_password(context, ctx, passwd)) != 0) { WLog_Print(transport->log, WLOG_WARN, "krb5_init_creds_set_password returned error %d", ret); goto out; } /* Acquire credentials using an initial credential context */ ret = krb5_init_creds_get(context, ctx); out: switch (ret) { case KRB5KDC_ERR_NONE: break; case KRB5_KDC_UNREACH: WLog_Print(transport->log, WLOG_WARN, "krb5_init_creds_get: KDC unreachable"); ret = FREERDP_ERROR_CONNECT_KDC_UNREACHABLE; break; case KRB5KRB_AP_ERR_BAD_INTEGRITY: case KRB5KRB_AP_ERR_MODIFIED: case KRB5KDC_ERR_PREAUTH_FAILED: case KRB5_GET_IN_TKT_LOOP: WLog_Print(transport->log, WLOG_WARN, "krb5_init_creds_get: Password incorrect"); ret = FREERDP_ERROR_AUTHENTICATION_FAILED; break; case KRB5KDC_ERR_KEY_EXP: WLog_Print(transport->log, WLOG_WARN, "krb5_init_creds_get: Password has expired"); ret = FREERDP_ERROR_CONNECT_PASSWORD_EXPIRED; break; case KRB5KDC_ERR_CLIENT_REVOKED: WLog_Print(transport->log, WLOG_WARN, "krb5_init_creds_get: Password revoked"); ret = FREERDP_ERROR_CONNECT_CLIENT_REVOKED; break; case KRB5KDC_ERR_POLICY: ret = FREERDP_ERROR_INSUFFICIENT_PRIVILEGES; break; default: WLog_Print(transport->log, WLOG_WARN, "krb5_init_creds_get"); ret = FREERDP_ERROR_CONNECT_TRANSPORT_FAILED; break; } if (ctx) krb5_init_creds_free(context, ctx); krb5_free_context(context); return ret; }
NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx, struct smb_iconv_convenience *iconv_convenience, struct PAC_DATA **pac_data_out, DATA_BLOB blob, krb5_context context, const krb5_keyblock *krbtgt_keyblock, const krb5_keyblock *service_keyblock, krb5_const_principal client_principal, time_t tgs_authtime, krb5_error_code *k5ret) { krb5_error_code ret; NTSTATUS status; enum ndr_err_code ndr_err; struct PAC_SIGNATURE_DATA *srv_sig_ptr = NULL; struct PAC_SIGNATURE_DATA *kdc_sig_ptr = NULL; struct PAC_SIGNATURE_DATA *srv_sig_wipe = NULL; struct PAC_SIGNATURE_DATA *kdc_sig_wipe = NULL; struct PAC_LOGON_INFO *logon_info = NULL; struct PAC_LOGON_NAME *logon_name = NULL; struct PAC_DATA *pac_data; struct PAC_DATA_RAW *pac_data_raw; DATA_BLOB *srv_sig_blob = NULL; DATA_BLOB *kdc_sig_blob = NULL; DATA_BLOB modified_pac_blob; NTTIME tgs_authtime_nttime; krb5_principal client_principal_pac; int i; krb5_clear_error_message(context); if (k5ret) { *k5ret = KRB5_PARSE_MALFORMED; } pac_data = talloc(mem_ctx, struct PAC_DATA); pac_data_raw = talloc(mem_ctx, struct PAC_DATA_RAW); kdc_sig_wipe = talloc(mem_ctx, struct PAC_SIGNATURE_DATA); srv_sig_wipe = talloc(mem_ctx, struct PAC_SIGNATURE_DATA); if (!pac_data_raw || !pac_data || !kdc_sig_wipe || !srv_sig_wipe) { if (k5ret) { *k5ret = ENOMEM; } return NT_STATUS_NO_MEMORY; } ndr_err = ndr_pull_struct_blob(&blob, pac_data, iconv_convenience, pac_data, (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { status = ndr_map_error2ntstatus(ndr_err); DEBUG(0,("can't parse the PAC: %s\n", nt_errstr(status))); return status; } if (pac_data->num_buffers < 4) { /* we need logon_ingo, service_key and kdc_key */ DEBUG(0,("less than 4 PAC buffers\n")); return NT_STATUS_INVALID_PARAMETER; } ndr_err = ndr_pull_struct_blob(&blob, pac_data_raw, iconv_convenience, pac_data_raw, (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA_RAW); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { status = ndr_map_error2ntstatus(ndr_err); DEBUG(0,("can't parse the PAC: %s\n", nt_errstr(status))); return status; } if (pac_data_raw->num_buffers < 4) { /* we need logon_ingo, service_key and kdc_key */ DEBUG(0,("less than 4 PAC buffers\n")); return NT_STATUS_INVALID_PARAMETER; } if (pac_data->num_buffers != pac_data_raw->num_buffers) { /* we need logon_ingo, service_key and kdc_key */ DEBUG(0,("misparse! PAC_DATA has %d buffers while PAC_DATA_RAW has %d\n", pac_data->num_buffers, pac_data_raw->num_buffers)); return NT_STATUS_INVALID_PARAMETER; } for (i=0; i < pac_data->num_buffers; i++) { if (pac_data->buffers[i].type != pac_data_raw->buffers[i].type) { DEBUG(0,("misparse! PAC_DATA buffer %d has type %d while PAC_DATA_RAW has %d\n", i, pac_data->buffers[i].type, pac_data->buffers[i].type)); return NT_STATUS_INVALID_PARAMETER; } switch (pac_data->buffers[i].type) { case PAC_TYPE_LOGON_INFO: if (!pac_data->buffers[i].info) { break; } logon_info = pac_data->buffers[i].info->logon_info.info; break; case PAC_TYPE_SRV_CHECKSUM: if (!pac_data->buffers[i].info) { break; } srv_sig_ptr = &pac_data->buffers[i].info->srv_cksum; srv_sig_blob = &pac_data_raw->buffers[i].info->remaining; break; case PAC_TYPE_KDC_CHECKSUM: if (!pac_data->buffers[i].info) { break; } kdc_sig_ptr = &pac_data->buffers[i].info->kdc_cksum; kdc_sig_blob = &pac_data_raw->buffers[i].info->remaining; break; case PAC_TYPE_LOGON_NAME: logon_name = &pac_data->buffers[i].info->logon_name; break; default: break; } } if (!logon_info) { DEBUG(0,("PAC no logon_info\n")); return NT_STATUS_INVALID_PARAMETER; } if (!logon_name) { DEBUG(0,("PAC no logon_name\n")); return NT_STATUS_INVALID_PARAMETER; } if (!srv_sig_ptr || !srv_sig_blob) { DEBUG(0,("PAC no srv_key\n")); return NT_STATUS_INVALID_PARAMETER; } if (!kdc_sig_ptr || !kdc_sig_blob) { DEBUG(0,("PAC no kdc_key\n")); return NT_STATUS_INVALID_PARAMETER; } /* Find and zero out the signatures, as required by the signing algorithm */ /* We find the data blobs above, now we parse them to get at the exact portion we should zero */ ndr_err = ndr_pull_struct_blob(kdc_sig_blob, kdc_sig_wipe, iconv_convenience, kdc_sig_wipe, (ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { status = ndr_map_error2ntstatus(ndr_err); DEBUG(0,("can't parse the KDC signature: %s\n", nt_errstr(status))); return status; } ndr_err = ndr_pull_struct_blob(srv_sig_blob, srv_sig_wipe, iconv_convenience, srv_sig_wipe, (ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { status = ndr_map_error2ntstatus(ndr_err); DEBUG(0,("can't parse the SRV signature: %s\n", nt_errstr(status))); return status; } /* Now zero the decoded structure */ memset(kdc_sig_wipe->signature.data, '\0', kdc_sig_wipe->signature.length); memset(srv_sig_wipe->signature.data, '\0', srv_sig_wipe->signature.length); /* and reencode, back into the same place it came from */ ndr_err = ndr_push_struct_blob(kdc_sig_blob, pac_data_raw, iconv_convenience, kdc_sig_wipe, (ndr_push_flags_fn_t)ndr_push_PAC_SIGNATURE_DATA); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { status = ndr_map_error2ntstatus(ndr_err); DEBUG(0,("can't repack the KDC signature: %s\n", nt_errstr(status))); return status; } ndr_err = ndr_push_struct_blob(srv_sig_blob, pac_data_raw, iconv_convenience, srv_sig_wipe, (ndr_push_flags_fn_t)ndr_push_PAC_SIGNATURE_DATA); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { status = ndr_map_error2ntstatus(ndr_err); DEBUG(0,("can't repack the SRV signature: %s\n", nt_errstr(status))); return status; } /* push out the whole structure, but now with zero'ed signatures */ ndr_err = ndr_push_struct_blob(&modified_pac_blob, pac_data_raw, iconv_convenience, pac_data_raw, (ndr_push_flags_fn_t)ndr_push_PAC_DATA_RAW); if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { status = ndr_map_error2ntstatus(ndr_err); DEBUG(0,("can't repack the RAW PAC: %s\n", nt_errstr(status))); return status; } /* verify by service_key */ ret = check_pac_checksum(mem_ctx, modified_pac_blob, srv_sig_ptr, context, service_keyblock); if (ret) { DEBUG(1, ("PAC Decode: Failed to verify the service signature: %s\n", smb_get_krb5_error_message(context, ret, mem_ctx))); if (k5ret) { *k5ret = ret; } return NT_STATUS_ACCESS_DENIED; } if (krbtgt_keyblock) { ret = check_pac_checksum(mem_ctx, srv_sig_ptr->signature, kdc_sig_ptr, context, krbtgt_keyblock); if (ret) { DEBUG(1, ("PAC Decode: Failed to verify the KDC signature: %s\n", smb_get_krb5_error_message(context, ret, mem_ctx))); if (k5ret) { *k5ret = ret; } return NT_STATUS_ACCESS_DENIED; } } /* Convert to NT time, so as not to loose accuracy in comparison */ unix_to_nt_time(&tgs_authtime_nttime, tgs_authtime); if (tgs_authtime_nttime != logon_name->logon_time) { DEBUG(2, ("PAC Decode: Logon time mismatch between ticket and PAC!\n")); DEBUG(2, ("PAC Decode: PAC: %s\n", nt_time_string(mem_ctx, logon_name->logon_time))); DEBUG(2, ("PAC Decode: Ticket: %s\n", nt_time_string(mem_ctx, tgs_authtime_nttime))); return NT_STATUS_ACCESS_DENIED; } ret = krb5_parse_name_flags(context, logon_name->account_name, KRB5_PRINCIPAL_PARSE_NO_REALM, &client_principal_pac); if (ret) { DEBUG(2, ("Could not parse name from incoming PAC: [%s]: %s\n", logon_name->account_name, smb_get_krb5_error_message(context, ret, mem_ctx))); if (k5ret) { *k5ret = ret; } return NT_STATUS_INVALID_PARAMETER; } if (!krb5_principal_compare_any_realm(context, client_principal, client_principal_pac)) { DEBUG(2, ("Name in PAC [%s] does not match principal name in ticket\n", logon_name->account_name)); return NT_STATUS_ACCESS_DENIED; } #if 0 if (strcasecmp(logon_info->info3.base.account_name.string, "Administrator")== 0) { file_save("tmp_pac_data-admin.dat",blob.data,blob.length); } #endif DEBUG(3,("Found account name from PAC: %s [%s]\n", logon_info->info3.base.account_name.string, logon_info->info3.base.full_name.string)); *pac_data_out = pac_data; return NT_STATUS_OK; }
int main(int argc, char **argv) { krb5_error_code ret; krb5_context context; krb5_pac pac; krb5_data data; krb5_principal p, p2; ret = krb5_init_context(&context); if (ret) errx(1, "krb5_init_contex"); krb5_enctype_enable(context, ETYPE_DES_CBC_MD5); ret = krb5_parse_name_flags(context, user, KRB5_PRINCIPAL_PARSE_NO_REALM, &p); if (ret) krb5_err(context, 1, ret, "krb5_parse_name"); ret = krb5_pac_parse(context, saved_pac, sizeof(saved_pac), &pac); if (ret) krb5_err(context, 1, ret, "krb5_pac_parse"); ret = krb5_pac_verify(context, pac, authtime, p, &member_keyblock, &kdc_keyblock); if (ret) krb5_err(context, 1, ret, "krb5_pac_verify"); ret = _krb5_pac_sign(context, pac, authtime, p, &member_keyblock, &kdc_keyblock, &data); if (ret) krb5_err(context, 1, ret, "_krb5_pac_sign"); krb5_pac_free(context, pac); ret = krb5_pac_parse(context, data.data, data.length, &pac); krb5_data_free(&data); if (ret) krb5_err(context, 1, ret, "krb5_pac_parse 2"); ret = krb5_pac_verify(context, pac, authtime, p, &member_keyblock, &kdc_keyblock); if (ret) krb5_err(context, 1, ret, "krb5_pac_verify 2"); /* make a copy and try to reproduce it */ { uint32_t *list; size_t len, i; krb5_pac pac2; ret = krb5_pac_init(context, &pac2); if (ret) krb5_err(context, 1, ret, "krb5_pac_init"); /* our two user buffer plus the three "system" buffers */ ret = krb5_pac_get_types(context, pac, &len, &list); if (ret) krb5_err(context, 1, ret, "krb5_pac_get_types"); for (i = 0; i < len; i++) { /* skip server_cksum, privsvr_cksum, and logon_name */ if (list[i] == 6 || list[i] == 7 || list[i] == 10) continue; ret = krb5_pac_get_buffer(context, pac, list[i], &data); if (ret) krb5_err(context, 1, ret, "krb5_pac_get_buffer"); if (list[i] == 1) { if (type_1_length != data.length) krb5_errx(context, 1, "type 1 have wrong length: %lu", (unsigned long)data.length); } else krb5_errx(context, 1, "unknown type %lu", (unsigned long)list[i]); ret = krb5_pac_add_buffer(context, pac2, list[i], &data); if (ret) krb5_err(context, 1, ret, "krb5_pac_add_buffer"); krb5_data_free(&data); } free(list); ret = _krb5_pac_sign(context, pac2, authtime, p, &member_keyblock, &kdc_keyblock, &data); if (ret) krb5_err(context, 1, ret, "_krb5_pac_sign 4"); krb5_pac_free(context, pac2); ret = krb5_pac_parse(context, data.data, data.length, &pac2); krb5_data_free(&data); if (ret) krb5_err(context, 1, ret, "krb5_pac_parse 4"); ret = krb5_pac_verify(context, pac2, authtime, p, &member_keyblock, &kdc_keyblock); if (ret) krb5_err(context, 1, ret, "krb5_pac_verify 4"); krb5_pac_free(context, pac2); } krb5_pac_free(context, pac); /* * check pac from Christian */ ret = krb5_parse_name_flags(context, user2, KRB5_PRINCIPAL_PARSE_NO_REALM, &p2); if (ret) krb5_err(context, 1, ret, "krb5_parse_name"); ret = krb5_pac_parse(context, saved_pac2, sizeof(saved_pac2) -1, &pac); if (ret) krb5_err(context, 1, ret, "krb5_pac_parse"); ret = krb5_pac_verify(context, pac, authtime2, p2, &member_keyblock2, NULL); if (ret) krb5_err(context, 1, ret, "krb5_pac_verify c1"); krb5_pac_free(context, pac); krb5_free_principal(context, p2); /* * Test empty free */ ret = krb5_pac_init(context, &pac); if (ret) krb5_err(context, 1, ret, "krb5_pac_init"); krb5_pac_free(context, pac); /* * Test add remove buffer */ ret = krb5_pac_init(context, &pac); if (ret) krb5_err(context, 1, ret, "krb5_pac_init"); { const krb5_data cdata = { 2, "\x00\x01" } ; ret = krb5_pac_add_buffer(context, pac, 1, &cdata); if (ret) krb5_err(context, 1, ret, "krb5_pac_add_buffer"); } { ret = krb5_pac_get_buffer(context, pac, 1, &data); if (ret) krb5_err(context, 1, ret, "krb5_pac_get_buffer"); if (data.length != 2 || memcmp(data.data, "\x00\x01", 2) != 0) krb5_errx(context, 1, "krb5_pac_get_buffer data not the same"); krb5_data_free(&data); } { const krb5_data cdata = { 2, "\x02\x00" } ; ret = krb5_pac_add_buffer(context, pac, 2, &cdata); if (ret) krb5_err(context, 1, ret, "krb5_pac_add_buffer"); } { ret = krb5_pac_get_buffer(context, pac, 1, &data); if (ret) krb5_err(context, 1, ret, "krb5_pac_get_buffer"); if (data.length != 2 || memcmp(data.data, "\x00\x01", 2) != 0) krb5_errx(context, 1, "krb5_pac_get_buffer data not the same"); krb5_data_free(&data); /* */ ret = krb5_pac_get_buffer(context, pac, 2, &data); if (ret) krb5_err(context, 1, ret, "krb5_pac_get_buffer"); if (data.length != 2 || memcmp(data.data, "\x02\x00", 2) != 0) krb5_errx(context, 1, "krb5_pac_get_buffer data not the same"); krb5_data_free(&data); } ret = _krb5_pac_sign(context, pac, authtime, p, &member_keyblock, &kdc_keyblock, &data); if (ret) krb5_err(context, 1, ret, "_krb5_pac_sign"); krb5_pac_free(context, pac); ret = krb5_pac_parse(context, data.data, data.length, &pac); krb5_data_free(&data); if (ret) krb5_err(context, 1, ret, "krb5_pac_parse 3"); ret = krb5_pac_verify(context, pac, authtime, p, &member_keyblock, &kdc_keyblock); if (ret) krb5_err(context, 1, ret, "krb5_pac_verify 3"); { uint32_t *list; size_t len; /* our two user buffer plus the three "system" buffers */ ret = krb5_pac_get_types(context, pac, &len, &list); if (ret) krb5_err(context, 1, ret, "krb5_pac_get_types"); if (len != 5) krb5_errx(context, 1, "list wrong length"); free(list); } krb5_pac_free(context, pac); krb5_free_principal(context, p); krb5_free_context(context); return 0; }
/* check that the SPN update should be allowed as an override via sam_ctx_system This is only called if the client is not a domain controller or administrator */ static bool writespn_check_spn(struct drsuapi_bind_state *b_state, struct dcesrv_call_state *dce_call, struct ldb_dn *dn, const char *spn) { /* * we only allow SPN updates if: * * 1) they are on the clients own account object * 2) they are of the form SERVICE/dnshostname */ struct dom_sid *user_sid, *sid; TALLOC_CTX *tmp_ctx = talloc_new(dce_call); struct ldb_result *res; const char *attrs[] = { "objectSID", "dNSHostName", NULL }; int ret; krb5_context krb_ctx; krb5_error_code kerr; krb5_principal principal; const krb5_data *component; const char *dns_name, *dnsHostName; /* The service principal name shouldn't be NULL */ if (spn == NULL) { talloc_free(tmp_ctx); return false; } /* get the objectSid of the DN that is being modified, and check it matches the user_sid in their token */ ret = dsdb_search_dn(b_state->sam_ctx, tmp_ctx, &res, dn, attrs, DSDB_SEARCH_ONE_ONLY); if (ret != LDB_SUCCESS) { talloc_free(tmp_ctx); return false; } user_sid = &dce_call->conn->auth_state.session_info->security_token->sids[PRIMARY_USER_SID_INDEX]; sid = samdb_result_dom_sid(tmp_ctx, res->msgs[0], "objectSid"); if (sid == NULL) { talloc_free(tmp_ctx); return false; } dnsHostName = ldb_msg_find_attr_as_string(res->msgs[0], "dNSHostName", NULL); if (dnsHostName == NULL) { talloc_free(tmp_ctx); return false; } if (!dom_sid_equal(sid, user_sid)) { talloc_free(tmp_ctx); return false; } kerr = smb_krb5_init_context_basic(tmp_ctx, dce_call->conn->dce_ctx->lp_ctx, &krb_ctx); if (kerr != 0) { talloc_free(tmp_ctx); return false; } ret = krb5_parse_name_flags(krb_ctx, spn, KRB5_PRINCIPAL_PARSE_NO_REALM, &principal); if (kerr != 0) { krb5_free_context(krb_ctx); talloc_free(tmp_ctx); return false; } if (krb5_princ_size(krb_ctx, principal) != 2) { krb5_free_principal(krb_ctx, principal); krb5_free_context(krb_ctx); talloc_free(tmp_ctx); return false; } component = krb5_princ_component(krb_ctx, principal, 1); dns_name = (const char *)component->data; if (strcasecmp(dns_name, dnsHostName) != 0) { krb5_free_principal(krb_ctx, principal); krb5_free_context(krb_ctx); talloc_free(tmp_ctx); return false; } /* its a simple update on their own account - allow it with * permissions override */ krb5_free_principal(krb_ctx, principal); krb5_free_context(krb_ctx); talloc_free(tmp_ctx); return true; }
WERROR DsCrackNameOneName(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, uint32_t format_flags, uint32_t format_offered, uint32_t format_desired, const char *name, struct drsuapi_DsNameInfo1 *info1) { krb5_error_code ret; const char *domain_filter = NULL; const char *result_filter = NULL; struct ldb_dn *name_dn = NULL; struct smb_krb5_context *smb_krb5_context = NULL; info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; info1->dns_domain_name = NULL; info1->result_name = NULL; if (!name) { return WERR_INVALID_PARAM; } /* TODO: - fill the correct names in all cases! * - handle format_flags */ /* here we need to set the domain_filter and/or the result_filter */ switch (format_offered) { case DRSUAPI_DS_NAME_FORMAT_UNKNOWN: { int i; enum drsuapi_DsNameFormat formats[] = { DRSUAPI_DS_NAME_FORMAT_FQDN_1779, DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL, DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT, DRSUAPI_DS_NAME_FORMAT_CANONICAL, DRSUAPI_DS_NAME_FORMAT_GUID, DRSUAPI_DS_NAME_FORMAT_DISPLAY, DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL, DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY, DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX }; WERROR werr; for (i=0; i < ARRAY_SIZE(formats); i++) { werr = DsCrackNameOneName(sam_ctx, mem_ctx, format_flags, formats[i], format_desired, name, info1); if (!W_ERROR_IS_OK(werr)) { return werr; } if (info1->status != DRSUAPI_DS_NAME_STATUS_NOT_FOUND) { return werr; } } return werr; } case DRSUAPI_DS_NAME_FORMAT_CANONICAL: case DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX: { char *str, *s, *account; if (strlen(name) == 0) { info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; return WERR_OK; } str = talloc_strdup(mem_ctx, name); W_ERROR_HAVE_NO_MEMORY(str); if (format_offered == DRSUAPI_DS_NAME_FORMAT_CANONICAL_EX) { /* Look backwards for the \n, and replace it with / */ s = strrchr(str, '\n'); if (!s) { info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; return WERR_OK; } s[0] = '/'; } s = strchr(str, '/'); if (!s) { /* there must be at least one / */ info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; return WERR_OK; } s[0] = '\0'; s++; domain_filter = talloc_asprintf(mem_ctx, "(&(objectClass=crossRef)(ncName=%s))", ldb_dn_get_linearized(samdb_dns_domain_to_dn(sam_ctx, mem_ctx, str))); W_ERROR_HAVE_NO_MEMORY(domain_filter); /* There may not be anything after the domain component (search for the domain itself) */ if (s[0]) { account = strrchr(s, '/'); if (!account) { account = s; } else { account++; } account = ldb_binary_encode_string(mem_ctx, account); W_ERROR_HAVE_NO_MEMORY(account); result_filter = talloc_asprintf(mem_ctx, "(name=%s)", account); W_ERROR_HAVE_NO_MEMORY(result_filter); } break; } case DRSUAPI_DS_NAME_FORMAT_NT4_ACCOUNT: { char *p; char *domain; const char *account = NULL; domain = talloc_strdup(mem_ctx, name); W_ERROR_HAVE_NO_MEMORY(domain); p = strchr(domain, '\\'); if (!p) { /* invalid input format */ info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; return WERR_OK; } p[0] = '\0'; if (p[1]) { account = &p[1]; } domain_filter = talloc_asprintf(mem_ctx, "(&(&(nETBIOSName=%s)(objectclass=crossRef))(ncName=*))", ldb_binary_encode_string(mem_ctx, domain)); W_ERROR_HAVE_NO_MEMORY(domain_filter); if (account) { result_filter = talloc_asprintf(mem_ctx, "(sAMAccountName=%s)", ldb_binary_encode_string(mem_ctx, account)); W_ERROR_HAVE_NO_MEMORY(result_filter); } talloc_free(domain); break; } /* A LDAP DN as a string */ case DRSUAPI_DS_NAME_FORMAT_FQDN_1779: { domain_filter = NULL; name_dn = ldb_dn_new(mem_ctx, sam_ctx, name); if (! ldb_dn_validate(name_dn)) { info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; return WERR_OK; } break; } /* A GUID as a string */ case DRSUAPI_DS_NAME_FORMAT_GUID: { struct GUID guid; char *ldap_guid; NTSTATUS nt_status; domain_filter = NULL; nt_status = GUID_from_string(name, &guid); if (!NT_STATUS_IS_OK(nt_status)) { info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; return WERR_OK; } ldap_guid = ldap_encode_ndr_GUID(mem_ctx, &guid); if (!ldap_guid) { return WERR_NOMEM; } result_filter = talloc_asprintf(mem_ctx, "(objectGUID=%s)", ldap_guid); W_ERROR_HAVE_NO_MEMORY(result_filter); break; } case DRSUAPI_DS_NAME_FORMAT_DISPLAY: { domain_filter = NULL; result_filter = talloc_asprintf(mem_ctx, "(|(displayName=%s)(samAccountName=%s))", ldb_binary_encode_string(mem_ctx, name), ldb_binary_encode_string(mem_ctx, name)); W_ERROR_HAVE_NO_MEMORY(result_filter); break; } /* A S-1234-5678 style string */ case DRSUAPI_DS_NAME_FORMAT_SID_OR_SID_HISTORY: { struct dom_sid *sid = dom_sid_parse_talloc(mem_ctx, name); char *ldap_sid; domain_filter = NULL; if (!sid) { info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; return WERR_OK; } ldap_sid = ldap_encode_ndr_dom_sid(mem_ctx, sid); if (!ldap_sid) { return WERR_NOMEM; } result_filter = talloc_asprintf(mem_ctx, "(objectSid=%s)", ldap_sid); W_ERROR_HAVE_NO_MEMORY(result_filter); break; } case DRSUAPI_DS_NAME_FORMAT_USER_PRINCIPAL: { krb5_principal principal; char *unparsed_name; ret = smb_krb5_init_context(mem_ctx, ldb_get_event_context(sam_ctx), (struct loadparm_context *)ldb_get_opaque(sam_ctx, "loadparm"), &smb_krb5_context); if (ret) { return WERR_NOMEM; } /* Ensure we reject compleate junk first */ ret = krb5_parse_name(smb_krb5_context->krb5_context, name, &principal); if (ret) { info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; return WERR_OK; } domain_filter = NULL; /* By getting the unparsed name here, we ensure the escaping is correct (and trust the client less) */ ret = krb5_unparse_name(smb_krb5_context->krb5_context, principal, &unparsed_name); if (ret) { krb5_free_principal(smb_krb5_context->krb5_context, principal); return WERR_NOMEM; } krb5_free_principal(smb_krb5_context->krb5_context, principal); /* The ldb_binary_encode_string() here avoid LDAP filter injection attacks */ result_filter = talloc_asprintf(mem_ctx, "(&(objectClass=user)(userPrincipalName=%s))", ldb_binary_encode_string(mem_ctx, unparsed_name)); free(unparsed_name); W_ERROR_HAVE_NO_MEMORY(result_filter); break; } case DRSUAPI_DS_NAME_FORMAT_SERVICE_PRINCIPAL: { krb5_principal principal; char *unparsed_name_short; char *service; ret = smb_krb5_init_context(mem_ctx, ldb_get_event_context(sam_ctx), (struct loadparm_context *)ldb_get_opaque(sam_ctx, "loadparm"), &smb_krb5_context); if (ret) { return WERR_NOMEM; } ret = krb5_parse_name(smb_krb5_context->krb5_context, name, &principal); if (ret == 0 && principal->name.name_string.len < 2) { info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; krb5_free_principal(smb_krb5_context->krb5_context, principal); return WERR_OK; } ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, name, KRB5_PRINCIPAL_PARSE_NO_REALM, &principal); if (ret) { krb5_free_principal(smb_krb5_context->krb5_context, principal); return dns_domain_from_principal(mem_ctx, smb_krb5_context, name, info1); } domain_filter = NULL; ret = krb5_unparse_name_flags(smb_krb5_context->krb5_context, principal, KRB5_PRINCIPAL_UNPARSE_NO_REALM, &unparsed_name_short); if (ret) { krb5_free_principal(smb_krb5_context->krb5_context, principal); return WERR_NOMEM; } service = principal->name.name_string.val[0]; if ((principal->name.name_string.len == 2) && (strcasecmp(service, "host") == 0)) { /* the 'cn' attribute is just the leading part of the name */ char *computer_name; computer_name = talloc_strndup(mem_ctx, principal->name.name_string.val[1], strcspn(principal->name.name_string.val[1], ".")); if (computer_name == NULL) { return WERR_NOMEM; } result_filter = talloc_asprintf(mem_ctx, "(|(&(servicePrincipalName=%s)(objectClass=user))(&(cn=%s)(objectClass=computer)))", ldb_binary_encode_string(mem_ctx, unparsed_name_short), ldb_binary_encode_string(mem_ctx, computer_name)); } else { result_filter = talloc_asprintf(mem_ctx, "(&(servicePrincipalName=%s)(objectClass=user))", ldb_binary_encode_string(mem_ctx, unparsed_name_short)); } krb5_free_principal(smb_krb5_context->krb5_context, principal); free(unparsed_name_short); W_ERROR_HAVE_NO_MEMORY(result_filter); break; } default: { info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; return WERR_OK; } } if (format_flags & DRSUAPI_DS_NAME_FLAG_SYNTACTICAL_ONLY) { return DsCrackNameOneSyntactical(mem_ctx, format_offered, format_desired, name_dn, name, info1); } return DsCrackNameOneFilter(sam_ctx, mem_ctx, smb_krb5_context, format_flags, format_offered, format_desired, name_dn, name, domain_filter, result_filter, info1); }
static WERROR DsCrackNameUPN(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, struct smb_krb5_context *smb_krb5_context, uint32_t format_flags, uint32_t format_offered, uint32_t format_desired, const char *name, struct drsuapi_DsNameInfo1 *info1) { int ldb_ret; WERROR status; const char *domain_filter = NULL; const char *result_filter = NULL; krb5_error_code ret; krb5_principal principal; const char *realm; char *unparsed_name_short; const char *domain_attrs[] = { NULL }; struct ldb_result *domain_res = NULL; /* Prevent recursion */ if (!name) { info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; return WERR_OK; } ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, name, KRB5_PRINCIPAL_PARSE_REQUIRE_REALM, &principal); if (ret) { info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; return WERR_OK; } realm = krb5_principal_get_realm(smb_krb5_context->krb5_context, principal); ldb_ret = ldb_search(sam_ctx, mem_ctx, &domain_res, samdb_partitions_dn(sam_ctx, mem_ctx), LDB_SCOPE_ONELEVEL, domain_attrs, "(&(&(|(&(dnsRoot=%s)(nETBIOSName=*))(nETBIOSName=%s))(objectclass=crossRef))(ncName=*))", ldb_binary_encode_string(mem_ctx, realm), ldb_binary_encode_string(mem_ctx, realm)); if (ldb_ret != LDB_SUCCESS) { DEBUG(2, ("DsCrackNameUPN domain ref search failed: %s", ldb_errstring(sam_ctx))); info1->status = DRSUAPI_DS_NAME_STATUS_RESOLVE_ERROR; return WERR_OK; } switch (domain_res->count) { case 1: break; case 0: return dns_domain_from_principal(mem_ctx, smb_krb5_context, name, info1); default: info1->status = DRSUAPI_DS_NAME_STATUS_NOT_UNIQUE; return WERR_OK; } ret = krb5_unparse_name_flags(smb_krb5_context->krb5_context, principal, KRB5_PRINCIPAL_UNPARSE_NO_REALM, &unparsed_name_short); krb5_free_principal(smb_krb5_context->krb5_context, principal); if (ret) { free(unparsed_name_short); return WERR_NOMEM; } /* This may need to be extended for more userPrincipalName variations */ result_filter = talloc_asprintf(mem_ctx, "(&(objectClass=user)(samAccountName=%s))", ldb_binary_encode_string(mem_ctx, unparsed_name_short)); domain_filter = talloc_asprintf(mem_ctx, "(distinguishedName=%s)", ldb_dn_get_linearized(domain_res->msgs[0]->dn)); if (!result_filter || !domain_filter) { free(unparsed_name_short); return WERR_NOMEM; } status = DsCrackNameOneFilter(sam_ctx, mem_ctx, smb_krb5_context, format_flags, format_offered, format_desired, NULL, unparsed_name_short, domain_filter, result_filter, info1); free(unparsed_name_short); return status; }
static krb5_error_code verify_logonname(krb5_context context, const struct PAC_INFO_BUFFER *logon_name, const krb5_data *data, time_t authtime, krb5_const_principal principal) { krb5_error_code ret; krb5_principal p2; uint32_t time1, time2; krb5_storage *sp; uint16_t len; char *s; sp = krb5_storage_from_readonly_mem((const char *)data->data + logon_name->offset_lo, logon_name->buffersize); if (sp == NULL) return krb5_enomem(context); krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); CHECK(ret, krb5_ret_uint32(sp, &time1), out); CHECK(ret, krb5_ret_uint32(sp, &time2), out); { uint64_t t1, t2; t1 = unix2nttime(authtime); t2 = ((uint64_t)time2 << 32) | time1; if (t1 != t2) { krb5_storage_free(sp); krb5_set_error_message(context, EINVAL, "PAC timestamp mismatch"); return EINVAL; } } CHECK(ret, krb5_ret_uint16(sp, &len), out); if (len == 0) { krb5_storage_free(sp); krb5_set_error_message(context, EINVAL, "PAC logon name length missing"); return EINVAL; } s = malloc(len); if (s == NULL) { krb5_storage_free(sp); return krb5_enomem(context); } ret = krb5_storage_read(sp, s, len); if (ret != len) { krb5_storage_free(sp); krb5_set_error_message(context, EINVAL, "Failed to read PAC logon name"); return EINVAL; } krb5_storage_free(sp); { size_t ucs2len = len / 2; uint16_t *ucs2; size_t u8len; unsigned int flags = WIND_RW_LE; ucs2 = malloc(sizeof(ucs2[0]) * ucs2len); if (ucs2 == NULL) return krb5_enomem(context); ret = wind_ucs2read(s, len, &flags, ucs2, &ucs2len); free(s); if (ret) { free(ucs2); krb5_set_error_message(context, ret, "Failed to convert string to UCS-2"); return ret; } ret = wind_ucs2utf8_length(ucs2, ucs2len, &u8len); if (ret) { free(ucs2); krb5_set_error_message(context, ret, "Failed to count length of UCS-2 string"); return ret; } u8len += 1; /* Add space for NUL */ s = malloc(u8len); if (s == NULL) { free(ucs2); return krb5_enomem(context); } ret = wind_ucs2utf8(ucs2, ucs2len, s, &u8len); free(ucs2); if (ret) { free(s); krb5_set_error_message(context, ret, "Failed to convert to UTF-8"); return ret; } } ret = krb5_parse_name_flags(context, s, KRB5_PRINCIPAL_PARSE_NO_REALM, &p2); free(s); if (ret) return ret; if (krb5_principal_compare_any_realm(context, principal, p2) != TRUE) { ret = EINVAL; krb5_set_error_message(context, ret, "PAC logon name mismatch"); } krb5_free_principal(context, p2); return ret; out: return ret; }
static void test_enterprise(krb5_context context) { krb5_error_code ret; char *unparsed; krb5_principal p; ret = krb5_set_default_realm(context, "SAMBA.ORG"); if (ret) krb5_err(context, 1, ret, "krb5_parse_name"); ret = krb5_parse_name_flags(context, "[email protected]@WIN.SU.SE", KRB5_PRINCIPAL_PARSE_ENTERPRISE, &p); if (ret) krb5_err(context, 1, ret, "krb5_parse_name_flags"); ret = krb5_unparse_name(context, p, &unparsed); if (ret) krb5_err(context, 1, ret, "krb5_unparse_name"); krb5_free_principal(context, p); if (strcmp(unparsed, "lha\\@[email protected]") != 0) krb5_errx(context, 1, "enterprise name failed 1"); free(unparsed); /* * */ ret = krb5_parse_name_flags(context, "lha\\@[email protected]", KRB5_PRINCIPAL_PARSE_ENTERPRISE, &p); if (ret) krb5_err(context, 1, ret, "krb5_parse_name_flags"); ret = krb5_unparse_name(context, p, &unparsed); if (ret) krb5_err(context, 1, ret, "krb5_unparse_name"); krb5_free_principal(context, p); if (strcmp(unparsed, "lha\\@su.se\\@[email protected]") != 0) krb5_errx(context, 1, "enterprise name failed 2: %s", unparsed); free(unparsed); /* * */ ret = krb5_parse_name_flags(context, "lha\\@[email protected]", 0, &p); if (ret) krb5_err(context, 1, ret, "krb5_parse_name_flags"); ret = krb5_unparse_name(context, p, &unparsed); if (ret) krb5_err(context, 1, ret, "krb5_unparse_name"); krb5_free_principal(context, p); if (strcmp(unparsed, "lha\\@[email protected]") != 0) krb5_errx(context, 1, "enterprise name failed 3"); free(unparsed); /* * */ ret = krb5_parse_name_flags(context, "*****@*****.**", KRB5_PRINCIPAL_PARSE_ENTERPRISE, &p); if (ret) krb5_err(context, 1, ret, "krb5_parse_name_flags"); ret = krb5_unparse_name(context, p, &unparsed); if (ret) krb5_err(context, 1, ret, "krb5_unparse_name"); krb5_free_principal(context, p); if (strcmp(unparsed, "lha\\@[email protected]") != 0) krb5_errx(context, 1, "enterprise name failed 2: %s", unparsed); free(unparsed); }
static void test_princ(krb5_context context) { const char *princ = "*****@*****.**"; const char *princ_short = "lha"; const char *noquote; krb5_error_code ret; char *princ_unparsed; char *princ_reformed = NULL; const char *realm; krb5_principal p, p2; ret = krb5_parse_name(context, princ, &p); if (ret) krb5_err(context, 1, ret, "krb5_parse_name"); ret = krb5_unparse_name(context, p, &princ_unparsed); if (ret) krb5_err(context, 1, ret, "krb5_parse_name"); if (strcmp(princ, princ_unparsed)) { krb5_errx(context, 1, "%s != %s", princ, princ_unparsed); } free(princ_unparsed); ret = krb5_unparse_name_flags(context, p, KRB5_PRINCIPAL_UNPARSE_NO_REALM, &princ_unparsed); if (ret) krb5_err(context, 1, ret, "krb5_parse_name"); if (strcmp(princ_short, princ_unparsed)) krb5_errx(context, 1, "%s != %s", princ_short, princ_unparsed); free(princ_unparsed); realm = krb5_principal_get_realm(context, p); if (asprintf(&princ_reformed, "%s@%s", princ_short, realm) < 0 || princ_reformed == NULL) errx(1, "malloc"); ret = krb5_parse_name(context, princ_reformed, &p2); free(princ_reformed); if (ret) krb5_err(context, 1, ret, "krb5_parse_name"); if (!krb5_principal_compare(context, p, p2)) { krb5_errx(context, 1, "p != p2"); } krb5_free_principal(context, p2); ret = krb5_set_default_realm(context, "SU.SE"); if (ret) krb5_err(context, 1, ret, "krb5_parse_name"); ret = krb5_unparse_name_flags(context, p, KRB5_PRINCIPAL_UNPARSE_SHORT, &princ_unparsed); if (ret) krb5_err(context, 1, ret, "krb5_parse_name"); if (strcmp(princ_short, princ_unparsed)) krb5_errx(context, 1, "'%s' != '%s'", princ_short, princ_unparsed); free(princ_unparsed); ret = krb5_parse_name(context, princ_short, &p2); if (ret) krb5_err(context, 1, ret, "krb5_parse_name"); if (!krb5_principal_compare(context, p, p2)) krb5_errx(context, 1, "p != p2"); krb5_free_principal(context, p2); ret = krb5_unparse_name(context, p, &princ_unparsed); if (ret) krb5_err(context, 1, ret, "krb5_parse_name"); if (strcmp(princ, princ_unparsed)) krb5_errx(context, 1, "'%s' != '%s'", princ, princ_unparsed); free(princ_unparsed); ret = krb5_set_default_realm(context, "SAMBA.ORG"); if (ret) krb5_err(context, 1, ret, "krb5_parse_name"); ret = krb5_parse_name(context, princ_short, &p2); if (ret) krb5_err(context, 1, ret, "krb5_parse_name"); if (krb5_principal_compare(context, p, p2)) krb5_errx(context, 1, "p == p2"); if (!krb5_principal_compare_any_realm(context, p, p2)) krb5_errx(context, 1, "(ignoring realms) p != p2"); ret = krb5_unparse_name(context, p2, &princ_unparsed); if (ret) krb5_err(context, 1, ret, "krb5_parse_name"); if (strcmp(princ, princ_unparsed) == 0) krb5_errx(context, 1, "%s == %s", princ, princ_unparsed); free(princ_unparsed); krb5_free_principal(context, p2); ret = krb5_parse_name(context, princ, &p2); if (ret) krb5_err(context, 1, ret, "krb5_parse_name"); if (!krb5_principal_compare(context, p, p2)) krb5_errx(context, 1, "p != p2"); ret = krb5_unparse_name(context, p2, &princ_unparsed); if (ret) krb5_err(context, 1, ret, "krb5_parse_name"); if (strcmp(princ, princ_unparsed)) krb5_errx(context, 1, "'%s' != '%s'", princ, princ_unparsed); free(princ_unparsed); krb5_free_principal(context, p2); ret = krb5_unparse_name_flags(context, p, KRB5_PRINCIPAL_UNPARSE_SHORT, &princ_unparsed); if (ret) krb5_err(context, 1, ret, "krb5_unparse_name_short"); if (strcmp(princ, princ_unparsed) != 0) krb5_errx(context, 1, "'%s' != '%s'", princ, princ_unparsed); free(princ_unparsed); ret = krb5_unparse_name(context, p, &princ_unparsed); if (ret) krb5_err(context, 1, ret, "krb5_unparse_name_short"); if (strcmp(princ, princ_unparsed)) krb5_errx(context, 1, "'%s' != '%s'", princ, princ_unparsed); free(princ_unparsed); ret = krb5_parse_name_flags(context, princ, KRB5_PRINCIPAL_PARSE_NO_REALM, &p2); if (!ret) krb5_err(context, 1, ret, "Should have failed to parse %s a " "short name", princ); ret = krb5_parse_name_flags(context, princ_short, KRB5_PRINCIPAL_PARSE_NO_REALM, &p2); if (ret) krb5_err(context, 1, ret, "krb5_parse_name"); ret = krb5_unparse_name_flags(context, p2, KRB5_PRINCIPAL_UNPARSE_NO_REALM, &princ_unparsed); krb5_free_principal(context, p2); if (ret) krb5_err(context, 1, ret, "krb5_unparse_name_norealm"); if (strcmp(princ_short, princ_unparsed)) krb5_errx(context, 1, "'%s' != '%s'", princ_short, princ_unparsed); free(princ_unparsed); ret = krb5_parse_name_flags(context, princ_short, KRB5_PRINCIPAL_PARSE_REQUIRE_REALM, &p2); if (!ret) krb5_err(context, 1, ret, "Should have failed to parse %s " "because it lacked a realm", princ_short); ret = krb5_parse_name_flags(context, princ, KRB5_PRINCIPAL_PARSE_REQUIRE_REALM, &p2); if (ret) krb5_err(context, 1, ret, "krb5_parse_name"); if (!krb5_principal_compare(context, p, p2)) krb5_errx(context, 1, "p != p2"); ret = krb5_unparse_name_flags(context, p2, KRB5_PRINCIPAL_UNPARSE_NO_REALM, &princ_unparsed); krb5_free_principal(context, p2); if (ret) krb5_err(context, 1, ret, "krb5_unparse_name_norealm"); if (strcmp(princ_short, princ_unparsed)) krb5_errx(context, 1, "'%s' != '%s'", princ_short, princ_unparsed); free(princ_unparsed); krb5_free_principal(context, p); /* test quoting */ princ = "test\\ [email protected]"; noquote = "test [email protected]"; ret = krb5_parse_name_flags(context, princ, 0, &p); if (ret) krb5_err(context, 1, ret, "krb5_parse_name"); ret = krb5_unparse_name_flags(context, p, 0, &princ_unparsed); if (ret) krb5_err(context, 1, ret, "krb5_unparse_name_flags"); if (strcmp(princ, princ_unparsed)) krb5_errx(context, 1, "q '%s' != '%s'", princ, princ_unparsed); free(princ_unparsed); ret = krb5_unparse_name_flags(context, p, KRB5_PRINCIPAL_UNPARSE_DISPLAY, &princ_unparsed); if (ret) krb5_err(context, 1, ret, "krb5_unparse_name_flags"); if (strcmp(noquote, princ_unparsed)) krb5_errx(context, 1, "nq '%s' != '%s'", noquote, princ_unparsed); free(princ_unparsed); krb5_free_principal(context, p); }
int main (int argc, char **argv) { krb5_error_code ret; krb5_context context; krb5_ccache ccache; krb5_principal principal; int optidx = 0; krb5_deltat ticket_life = 0; int parseflags = 0; setprogname (argv[0]); setlocale (LC_ALL, ""); #if defined(HEIMDAL_LOCALEDIR) bindtextdomain ("heimdal_kuser", HEIMDAL_LOCALEDIR); textdomain("heimdal_kuser"); #endif ret = krb5_init_context (&context); if (ret == KRB5_CONFIG_BADFORMAT) errx (1, "krb5_init_context failed to parse configuration file"); else if (ret) errx(1, "krb5_init_context failed: %d", ret); if(getarg(args, sizeof(args) / sizeof(args[0]), argc, argv, &optidx)) usage(1); if (help_flag) usage (0); if(version_flag) { print_version(NULL); exit(0); } argc -= optidx; argv += optidx; if (canonicalize_flag) parseflags |= KRB5_PRINCIPAL_PARSE_ENTERPRISE; if (argv[0]) { ret = krb5_parse_name_flags (context, argv[0], parseflags, &principal); if (ret) krb5_err (context, 1, ret, "krb5_parse_name"); } else { ret = krb5_get_default_principal (context, &principal); if (ret) krb5_err (context, 1, ret, "krb5_get_default_principal"); } if(fcache_version) krb5_set_fcache_version(context, fcache_version); if(renewable_flag == -1) /* this seems somewhat pointless, but whatever */ krb5_appdefault_boolean(context, "kinit", krb5_principal_get_realm(context, principal), "renewable", FALSE, &renewable_flag); #ifndef HEIMDAL_SMALLER if(get_v4_tgt == -1) krb5_appdefault_boolean(context, "kinit", krb5_principal_get_realm(context, principal), "krb4_get_tickets", FALSE, &get_v4_tgt); #endif if(do_afslog == -1) krb5_appdefault_boolean(context, "kinit", krb5_principal_get_realm(context, principal), "afslog", TRUE, &do_afslog); if(cred_cache) ret = krb5_cc_resolve(context, cred_cache, &ccache); else { if(argc > 1) { char s[1024]; ret = krb5_cc_gen_new(context, &krb5_fcc_ops, &ccache); if(ret) krb5_err(context, 1, ret, "creating cred cache"); snprintf(s, sizeof(s), "%s:%s", krb5_cc_get_type(context, ccache), krb5_cc_get_name(context, ccache)); setenv("KRB5CCNAME", s, 1); #ifndef HEIMDAL_SMALLER if (get_v4_tgt) { int fd; if (asprintf(&krb4_cc_name, "%s_XXXXXX", TKT_ROOT) < 0) krb5_errx(context, 1, "out of memory"); if((fd = mkstemp(krb4_cc_name)) >= 0) { close(fd); setenv("KRBTKFILE", krb4_cc_name, 1); } else { free(krb4_cc_name); krb4_cc_name = NULL; } } #endif } else { ret = krb5_cc_cache_match(context, principal, &ccache); if (ret) ret = krb5_cc_default (context, &ccache); } } if (ret) krb5_err (context, 1, ret, N_("resolving credentials cache", "")); if(argc > 1 && k_hasafs ()) k_setpag(); if (lifetime) { int tmp = parse_time (lifetime, "s"); if (tmp < 0) errx (1, N_("unparsable time: %s", ""), lifetime); ticket_life = tmp; } if(addrs_flag == 0 && extra_addresses.num_strings > 0) krb5_errx(context, 1, N_("specifying both extra addresses and " "no addresses makes no sense", "")); { int i; krb5_addresses addresses; memset(&addresses, 0, sizeof(addresses)); for(i = 0; i < extra_addresses.num_strings; i++) { ret = krb5_parse_address(context, extra_addresses.strings[i], &addresses); if (ret == 0) { krb5_add_extra_addresses(context, &addresses); krb5_free_addresses(context, &addresses); } } free_getarg_strings(&extra_addresses); } if(renew_flag || validate_flag) { ret = renew_validate(context, renew_flag, validate_flag, ccache, server_str, ticket_life); exit(ret != 0); } #ifndef HEIMDAL_SMALLER if(!convert_524) #endif get_new_tickets(context, principal, ccache, ticket_life, 1); #ifndef HEIMDAL_SMALLER if(get_v4_tgt || convert_524) do_524init(context, ccache, NULL, server_str); #endif if(do_afslog && k_hasafs()) krb5_afslog(context, ccache, NULL, NULL); if(argc > 1) { struct renew_ctx ctx; time_t timeout; timeout = ticket_lifetime(context, ccache, principal, server_str) / 2; ctx.context = context; ctx.ccache = ccache; ctx.principal = principal; ctx.ticket_life = ticket_life; ret = simple_execvp_timed(argv[1], argv+1, renew_func, &ctx, timeout); #define EX_NOEXEC 126 #define EX_NOTFOUND 127 if(ret == EX_NOEXEC) krb5_warnx(context, N_("permission denied: %s", ""), argv[1]); else if(ret == EX_NOTFOUND) krb5_warnx(context, N_("command not found: %s", ""), argv[1]); krb5_cc_destroy(context, ccache); #ifndef HEIMDAL_SMALLER _krb5_krb_dest_tkt(context, krb4_cc_name); #endif if(k_hasafs()) k_unlog(); } else { krb5_cc_close (context, ccache); ret = 0; } krb5_free_principal(context, principal); krb5_free_context (context); return ret; }
static WERROR DsCrackNameSPNAlias(struct ldb_context *sam_ctx, TALLOC_CTX *mem_ctx, struct smb_krb5_context *smb_krb5_context, uint32_t format_flags, enum drsuapi_DsNameFormat format_offered, enum drsuapi_DsNameFormat format_desired, const char *name, struct drsuapi_DsNameInfo1 *info1) { WERROR wret; krb5_error_code ret; krb5_principal principal; const krb5_data *component; const char *service, *dns_name; char *new_service; char *new_princ; enum drsuapi_DsNameStatus namestatus; /* parse principal */ ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, name, KRB5_PRINCIPAL_PARSE_NO_REALM, &principal); if (ret) { DEBUG(2, ("Could not parse principal: %s: %s\n", name, smb_get_krb5_error_message(smb_krb5_context->krb5_context, ret, mem_ctx))); return WERR_NOMEM; } /* grab cifs/, http/ etc */ /* This is checked for in callers, but be safe */ if (krb5_princ_size(smb_krb5_context->krb5_context, principal) < 2) { info1->status = DRSUAPI_DS_NAME_STATUS_NOT_FOUND; krb5_free_principal(smb_krb5_context->krb5_context, principal); return WERR_OK; } component = krb5_princ_component(smb_krb5_context->krb5_context, principal, 0); service = (const char *)component->data; component = krb5_princ_component(smb_krb5_context->krb5_context, principal, 1); dns_name = (const char *)component->data; /* MAP it */ namestatus = LDB_lookup_spn_alias(smb_krb5_context->krb5_context, sam_ctx, mem_ctx, service, &new_service); if (namestatus == DRSUAPI_DS_NAME_STATUS_NOT_FOUND) { wret = WERR_OK; info1->status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY; info1->dns_domain_name = talloc_strdup(mem_ctx, dns_name); if (!info1->dns_domain_name) { wret = WERR_NOMEM; } krb5_free_principal(smb_krb5_context->krb5_context, principal); return wret; } else if (namestatus != DRSUAPI_DS_NAME_STATUS_OK) { info1->status = namestatus; krb5_free_principal(smb_krb5_context->krb5_context, principal); return WERR_OK; } /* reform principal */ new_princ = talloc_asprintf(mem_ctx, "%s/%s", new_service, dns_name); if (!new_princ) { krb5_free_principal(smb_krb5_context->krb5_context, principal); return WERR_NOMEM; } wret = DsCrackNameOneName(sam_ctx, mem_ctx, format_flags, format_offered, format_desired, new_princ, info1); talloc_free(new_princ); if (W_ERROR_IS_OK(wret) && (info1->status == DRSUAPI_DS_NAME_STATUS_NOT_FOUND)) { info1->status = DRSUAPI_DS_NAME_STATUS_DOMAIN_ONLY; info1->dns_domain_name = talloc_strdup(mem_ctx, dns_name); if (!info1->dns_domain_name) { wret = WERR_NOMEM; } } krb5_free_principal(smb_krb5_context->krb5_context, principal); return wret; }