📄 kerberos.c
字号:
/* Unix SMB/CIFS implementation. kerberos utility library Copyright (C) Andrew Tridgell 2001 Copyright (C) Remus Koos 2001 Copyright (C) Nalin Dahyabhai <nalin@redhat.com> 2004. Copyright (C) Jeremy Allison 2004. 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 the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.*/#include "includes.h"#ifdef HAVE_KRB5#define LIBADS_CCACHE_NAME "MEMORY:libads"/* we use a prompter to avoid a crash bug in the kerberos libs when dealing with empty passwords this prompter is just a string copy ...*/static krb5_error_code kerb_prompter(krb5_context ctx, void *data, const char *name, const char *banner, int num_prompts, krb5_prompt prompts[]){ if (num_prompts == 0) return 0; memset(prompts[0].reply->data, '\0', prompts[0].reply->length); if (prompts[0].reply->length > 0) { if (data) { strncpy(prompts[0].reply->data, data, prompts[0].reply->length-1); prompts[0].reply->length = strlen(prompts[0].reply->data); } else { prompts[0].reply->length = 0; } } return 0;}/* simulate a kinit, putting the tgt in the given cache location. If cache_name == NULL place in default cache location. remus@snapserver.com*/int kerberos_kinit_password(const char *principal, const char *password, int time_offset, time_t *expire_time, const char *cache_name){ krb5_context ctx = NULL; krb5_error_code code = 0; krb5_ccache cc = NULL; krb5_principal me; krb5_creds my_creds; initialize_krb5_error_table(); if ((code = krb5_init_context(&ctx))) return code; if (time_offset != 0) { krb5_set_real_time(ctx, time(NULL) + time_offset, 0); } if ((code = krb5_cc_resolve(ctx, cache_name ? cache_name : krb5_cc_default_name(ctx), &cc))) { krb5_free_context(ctx); return code; } if ((code = krb5_parse_name(ctx, principal, &me))) { krb5_free_context(ctx); return code; } if ((code = krb5_get_init_creds_password(ctx, &my_creds, me, CONST_DISCARD(char *,password), kerb_prompter, NULL, 0, NULL, NULL))) { krb5_free_principal(ctx, me); krb5_free_context(ctx); return code; } if ((code = krb5_cc_initialize(ctx, cc, me))) { krb5_free_cred_contents(ctx, &my_creds); krb5_free_principal(ctx, me); krb5_free_context(ctx); return code; } if ((code = krb5_cc_store_cred(ctx, cc, &my_creds))) { krb5_cc_close(ctx, cc); krb5_free_cred_contents(ctx, &my_creds); krb5_free_principal(ctx, me); krb5_free_context(ctx); return code; } if (expire_time) *expire_time = (time_t) my_creds.times.endtime; krb5_cc_close(ctx, cc); krb5_free_cred_contents(ctx, &my_creds); krb5_free_principal(ctx, me); krb5_free_context(ctx); return 0;}/* run kinit to setup our ccache */int ads_kinit_password(ADS_STRUCT *ads){ char *s; int ret; const char *account_name; fstring acct_name; if ( IS_DC ) { /* this will end up getting a ticket for DOMAIN@RUSTED.REA.LM */ account_name = lp_workgroup(); } else { /* always use the sAMAccountName for security = domain */ /* global_myname()$@REA.LM */ if ( lp_security() == SEC_DOMAIN ) { fstr_sprintf( acct_name, "%s$", global_myname() ); account_name = acct_name; } else /* This looks like host/global_myname()@REA.LM */ account_name = ads->auth.user_name; } if (asprintf(&s, "%s@%s", account_name, ads->auth.realm) == -1) { return KRB5_CC_NOMEM; } if (!ads->auth.password) { return KRB5_LIBOS_CANTREADPWD; } ret = kerberos_kinit_password(s, ads->auth.password, ads->auth.time_offset, &ads->auth.expire, NULL); if (ret) { DEBUG(0,("kerberos_kinit_password %s failed: %s\n", s, error_message(ret))); } free(s); return ret;}int ads_kdestroy(const char *cc_name){ krb5_error_code code; krb5_context ctx = NULL; krb5_ccache cc = NULL; initialize_krb5_error_table(); if ((code = krb5_init_context (&ctx))) { DEBUG(3, ("ads_kdestroy: kdb5_init_context failed: %s\n", error_message(code))); return code; } if (!cc_name) { if ((code = krb5_cc_default(ctx, &cc))) { krb5_free_context(ctx); return code; } } else { if ((code = krb5_cc_resolve(ctx, cc_name, &cc))) { DEBUG(3, ("ads_kdestroy: krb5_cc_resolve failed: %s\n", error_message(code))); krb5_free_context(ctx); return code; } } if ((code = krb5_cc_destroy (ctx, cc))) { DEBUG(3, ("ads_kdestroy: krb5_cc_destroy failed: %s\n", error_message(code))); } krb5_free_context (ctx); return code;}/************************************************************************ Routine to fetch the salting principal for a service. Active Directory may use a non-obvious principal name to generate the salt when it determines the key to use for encrypting tickets for a service, and hopefully we detected that when we joined the domain. ************************************************************************/static char *kerberos_secrets_fetch_salting_principal(const char *service, int enctype){ char *key = NULL; char *ret = NULL; asprintf(&key, "%s/%s/enctype=%d", SECRETS_SALTING_PRINCIPAL, service, enctype); if (!key) { return NULL; } ret = (char *)secrets_fetch(key, NULL); SAFE_FREE(key); return ret;}/************************************************************************ Routine to get the salting principal for this service. Active Directory may use a non-obvious principal name to generate the salt when it determines the key to use for encrypting tickets for a service, and hopefully we detected that when we joined the domain. Caller must free if return is not null. ************************************************************************/krb5_principal kerberos_fetch_salt_princ_for_host_princ(krb5_context context, krb5_principal host_princ, int enctype){ char *unparsed_name = NULL, *salt_princ_s = NULL; krb5_principal ret_princ = NULL; if (krb5_unparse_name(context, host_princ, &unparsed_name) != 0) { return (krb5_principal)NULL; } if ((salt_princ_s = kerberos_secrets_fetch_salting_principal(unparsed_name, enctype)) == NULL) { krb5_free_unparsed_name(context, unparsed_name); return (krb5_principal)NULL; } if (krb5_parse_name(context, salt_princ_s, &ret_princ) != 0) { krb5_free_unparsed_name(context, unparsed_name); SAFE_FREE(salt_princ_s); return (krb5_principal)NULL; } krb5_free_unparsed_name(context, unparsed_name); SAFE_FREE(salt_princ_s); return ret_princ;}/************************************************************************ Routine to set the salting principal for this service. Active Directory may use a non-obvious principal name to generate the salt when it determines the key to use for encrypting tickets for a service, and hopefully we detected that when we joined the domain. Setting principal to NULL deletes this entry. ************************************************************************/BOOL kerberos_secrets_store_salting_principal(const char *service, int enctype, const char *principal){ char *key = NULL; BOOL ret = False; krb5_context context = NULL; krb5_principal princ = NULL; char *princ_s = NULL; char *unparsed_name = NULL; krb5_init_context(&context); if (!context) { return False; } if (strchr_m(service, '@')) { asprintf(&princ_s, "%s", service); } else { asprintf(&princ_s, "%s@%s", service, lp_realm()); } if (krb5_parse_name(context, princ_s, &princ) != 0) { goto out; } if (krb5_unparse_name(context, princ, &unparsed_name) != 0) { goto out; } asprintf(&key, "%s/%s/enctype=%d", SECRETS_SALTING_PRINCIPAL, unparsed_name, enctype); if (!key) { goto out; } if ((principal != NULL) && (strlen(principal) > 0)) { ret = secrets_store(key, principal, strlen(principal) + 1); } else { ret = secrets_delete(key); } out: SAFE_FREE(key); SAFE_FREE(princ_s); if (unparsed_name) { krb5_free_unparsed_name(context, unparsed_name); } if (context) { krb5_free_context(context); } return ret;}/************************************************************************ Routine to get initial credentials as a service ticket for the local machine. Returns a buffer initialized with krb5_mk_req_extended. ************************************************************************/static krb5_error_code get_service_ticket(krb5_context ctx, krb5_ccache ccache, const char *service_principal, int enctype, krb5_data *p_outbuf){ krb5_creds creds, *new_creds = NULL; char *service_s = NULL; char *machine_account = NULL, *password = NULL; krb5_data in_data; krb5_auth_context auth_context = NULL; krb5_error_code err = 0; ZERO_STRUCT(creds); asprintf(&machine_account, "%s$@%s", global_myname(), lp_realm()); if (machine_account == NULL) { goto out; } password = secrets_fetch_machine_password(lp_workgroup(), NULL, NULL); if (password == NULL) { goto out; } if ((err = kerberos_kinit_password(machine_account, password, 0, NULL, LIBADS_CCACHE_NAME)) != 0) { DEBUG(0,("get_service_ticket: kerberos_kinit_password %s@%s failed: %s\n", machine_account, lp_realm(), error_message(err))); goto out; } /* Ok - the above call has gotten a TGT. Now we need to get a service ticket to ourselves. */ /* Set up the enctype and client and server principal fields for krb5_get_credentials. */ kerberos_set_creds_enctype(&creds, enctype); if ((err = krb5_cc_get_principal(ctx, ccache, &creds.client))) { DEBUG(3, ("get_service_ticket: krb5_cc_get_principal failed: %s\n", error_message(err))); goto out; } if (strchr_m(service_principal, '@')) { asprintf(&service_s, "%s", service_principal); } else { asprintf(&service_s, "%s@%s", service_principal, lp_realm()); } if ((err = krb5_parse_name(ctx, service_s, &creds.server))) { DEBUG(0,("get_service_ticket: krb5_parse_name %s failed: %s\n", service_s, error_message(err))); goto out; } if ((err = krb5_get_credentials(ctx, 0, ccache, &creds, &new_creds))) { DEBUG(5,("get_service_ticket: krb5_get_credentials for %s enctype %d failed: %s\n", service_s, enctype, error_message(err))); goto out; } memset(&in_data, '\0', sizeof(in_data)); if ((err = krb5_mk_req_extended(ctx, &auth_context, 0, &in_data, new_creds, p_outbuf)) != 0) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -