Esempio n. 1
0
static int create_keytab(TALLOC_CTX *parent_ctx,
			 struct cli_credentials *machine_account,
			 struct smb_krb5_context *smb_krb5_context,
			 const char **enctype_strings,
			 krb5_keytab keytab,
			 BOOL add_old) 
{
	krb5_error_code ret;
	const char *password_s;
	const char *old_secret;
	int kvno;
	krb5_principal salt_princ;
	krb5_principal princ;
	const char *princ_string;

	TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
	if (!mem_ctx) {
		return ENOMEM;
	}

	princ_string = cli_credentials_get_principal(machine_account, mem_ctx);
	/* Get the principal we will store the new keytab entries under */
	ret = principal_from_credentials(mem_ctx, machine_account, smb_krb5_context, &princ);
	if (ret) {
		DEBUG(1,("create_keytab: makeing krb5 principal failed (%s)\n",
			 smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
						    ret, mem_ctx)));
		talloc_free(mem_ctx);
		return ret;
	}

	/* The salt used to generate these entries may be different however, fetch that */
	ret = salt_principal_from_credentials(mem_ctx, machine_account, 
					      smb_krb5_context, 
					      &salt_princ);
	if (ret) {
		DEBUG(1,("create_keytab: makeing salt principal failed (%s)\n",
			 smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
						    ret, mem_ctx)));
		talloc_free(mem_ctx);
		return ret;
	}

	/* Finally, do the dance to get the password to put in the entry */
	password_s = cli_credentials_get_password(machine_account);
	if (!password_s) {
		krb5_keytab_entry entry;
		const struct samr_Password *mach_pwd;

		if (!str_list_check(enctype_strings, "arcfour-hmac-md5")) {
			DEBUG(1, ("Asked to create keytab, but with only an NT hash supplied, "
				  "but not listing arcfour-hmac-md5 as an enc type to include in the keytab!\n"));
			talloc_free(mem_ctx);
			return EINVAL;
		}

		/* If we don't have the plaintext password, try for
		 * the MD4 password hash */
		mach_pwd = cli_credentials_get_nt_hash(machine_account, mem_ctx);
		if (!mach_pwd) {
			/* OK, nothing to do here */
			talloc_free(mem_ctx);
			return 0;
		}
		ret = krb5_keyblock_init(smb_krb5_context->krb5_context,
					 ETYPE_ARCFOUR_HMAC_MD5,
					 mach_pwd->hash, sizeof(mach_pwd->hash), 
					 &entry.keyblock);
		if (ret) {
			DEBUG(1, ("create_keytab: krb5_keyblock_init failed: %s\n",
				  smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
							     ret, mem_ctx)));
			talloc_free(mem_ctx);
			return ret;
		}

		entry.principal = princ;
		entry.vno       = cli_credentials_get_kvno(machine_account);
		ret = krb5_kt_add_entry(smb_krb5_context->krb5_context, keytab, &entry);
		if (ret) {
			DEBUG(1, ("Failed to add ARCFOUR_HMAC (only) entry for %s to keytab: %s",
				  cli_credentials_get_principal(machine_account, mem_ctx), 
				  smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
							     ret, mem_ctx)));
			talloc_free(mem_ctx);
			krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
			return ret;
		}
		
		DEBUG(5, ("Added %s(kvno %d) to keytab (arcfour-hmac-md5)\n", 
			  cli_credentials_get_principal(machine_account, mem_ctx),
			  cli_credentials_get_kvno(machine_account)));

		krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);

		/* Can't go any further, we only have this one key */
		talloc_free(mem_ctx);
		return 0;
	}
	
	kvno = cli_credentials_get_kvno(machine_account);
	/* good, we actually have the real plaintext */
	ret = keytab_add_keys(mem_ctx, princ_string, princ, salt_princ, 
			      kvno, password_s, smb_krb5_context, 
			      enctype_strings, keytab);
	if (!ret) {
		talloc_free(mem_ctx);
		return ret;
	}

	if (!add_old || kvno == 0) {
		talloc_free(mem_ctx);
		return 0;
	}

	old_secret = cli_credentials_get_old_password(machine_account);
	if (!old_secret) {
		talloc_free(mem_ctx);
		return 0;
	}
	
	ret = keytab_add_keys(mem_ctx, princ_string, princ, salt_princ, 
			      kvno - 1, old_secret, smb_krb5_context, 
			      enctype_strings, keytab);
	if (!ret) {
		talloc_free(mem_ctx);
		return ret;
	}

	talloc_free(mem_ctx);
	return 0;
}
Esempio n. 2
0
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;
}
Esempio n. 3
0
krb5_error_code smb_krb5_init_context(void *parent_ctx,
				      struct tevent_context *ev,
				      struct loadparm_context *lp_ctx,
				       struct smb_krb5_context **smb_krb5_context)
{
	krb5_error_code ret;
	TALLOC_CTX *tmp_ctx;

	initialize_krb5_error_table();

	tmp_ctx = talloc_new(parent_ctx);
	*smb_krb5_context = talloc(tmp_ctx, struct smb_krb5_context);

	if (!*smb_krb5_context || !tmp_ctx) {
		talloc_free(tmp_ctx);
		return ENOMEM;
	}

	ret = smb_krb5_init_context_basic(tmp_ctx, ev, lp_ctx,
					  &(*smb_krb5_context)->krb5_context);
	if (ret) {
		DEBUG(1,("smb_krb5_context_init_basic failed (%s)\n",
			 error_message(ret)));
		talloc_free(tmp_ctx);
		return ret;
	}

	/* TODO: Should we have a different name here? */
	ret = krb5_initlog((*smb_krb5_context)->krb5_context, "Samba", &(*smb_krb5_context)->logf);

	if (ret) {
		DEBUG(1,("krb5_initlog failed (%s)\n",
			 smb_get_krb5_error_message((*smb_krb5_context)->krb5_context, ret, tmp_ctx)));
		krb5_free_context((*smb_krb5_context)->krb5_context);
		talloc_free(tmp_ctx);
		return ret;
	}

	talloc_set_destructor(*smb_krb5_context, smb_krb5_context_destroy);

	ret = krb5_addlog_func((*smb_krb5_context)->krb5_context, (*smb_krb5_context)->logf, 0 /* min */, -1 /* max */,
			       smb_krb5_debug_wrapper, smb_krb5_debug_close, NULL);
	if (ret) {
		DEBUG(1,("krb5_addlog_func failed (%s)\n",
			 smb_get_krb5_error_message((*smb_krb5_context)->krb5_context, ret, tmp_ctx)));
		talloc_free(tmp_ctx);
		return ret;
	}
	krb5_set_warn_dest((*smb_krb5_context)->krb5_context, (*smb_krb5_context)->logf);

	/* Set use of our socket lib */
	ret = krb5_set_send_to_kdc_func((*smb_krb5_context)->krb5_context,
					smb_krb5_send_and_recv_func,
					ev);
	if (ret) {
		DEBUG(1,("krb5_set_send_recv_func failed (%s)\n",
			 smb_get_krb5_error_message((*smb_krb5_context)->krb5_context, ret, tmp_ctx)));
		talloc_free(tmp_ctx);
		return ret;
	}

	talloc_steal(parent_ctx, *smb_krb5_context);
	talloc_free(tmp_ctx);

	/* Set options in kerberos */

	krb5_set_dns_canonicalize_hostname((*smb_krb5_context)->krb5_context,
					   lp_parm_bool(lp_ctx, NULL, "krb5", "set_dns_canonicalize", false));

	return 0;
}
Esempio n. 4
0
static NTSTATUS gensec_krb5_session_info(struct gensec_security *gensec_security,
					 TALLOC_CTX *mem_ctx_out,
					 struct auth_session_info **_session_info) 
{
	NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
	struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
	krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
	struct auth_user_info_dc *user_info_dc = NULL;
	struct auth_session_info *session_info = NULL;
	struct PAC_LOGON_INFO *logon_info;

	krb5_principal client_principal;
	char *principal_string;
	
	DATA_BLOB pac;
	krb5_data pac_data;

	krb5_error_code ret;

	TALLOC_CTX *mem_ctx = talloc_new(mem_ctx_out);
	if (!mem_ctx) {
		return NT_STATUS_NO_MEMORY;
	}
	
	ret = krb5_ticket_get_client(context, gensec_krb5_state->ticket, &client_principal);
	if (ret) {
		DEBUG(5, ("krb5_ticket_get_client failed to get cleint principal: %s\n", 
			  smb_get_krb5_error_message(context, 
						     ret, mem_ctx)));
		talloc_free(mem_ctx);
		return NT_STATUS_NO_MEMORY;
	}
	
	ret = krb5_unparse_name(gensec_krb5_state->smb_krb5_context->krb5_context, 
				client_principal, &principal_string);
	if (ret) {
		DEBUG(1, ("Unable to parse client principal: %s\n",
			  smb_get_krb5_error_message(context, 
						     ret, mem_ctx)));
		krb5_free_principal(context, client_principal);
		talloc_free(mem_ctx);
		return NT_STATUS_NO_MEMORY;
	}

	ret = krb5_ticket_get_authorization_data_type(context, gensec_krb5_state->ticket, 
						      KRB5_AUTHDATA_WIN2K_PAC, 
						      &pac_data);
	
	if (ret && gensec_setting_bool(gensec_security->settings, "gensec", "require_pac", false)) {
		DEBUG(1, ("Unable to find PAC in ticket from %s, failing to allow access: %s \n",
			  principal_string,
			  smb_get_krb5_error_message(context, 
						     ret, mem_ctx)));
		free(principal_string);
		krb5_free_principal(context, client_principal);
		talloc_free(mem_ctx);
		return NT_STATUS_ACCESS_DENIED;
	} else if (ret) {
		/* NO pac */
		DEBUG(5, ("krb5_ticket_get_authorization_data_type failed to find PAC: %s\n", 
			  smb_get_krb5_error_message(context, 
						     ret, mem_ctx)));
		if (gensec_security->auth_context && 
		    !gensec_setting_bool(gensec_security->settings, "gensec", "require_pac", false)) {
			DEBUG(1, ("Unable to find PAC for %s, resorting to local user lookup: %s",
				  principal_string, smb_get_krb5_error_message(context, 
						     ret, mem_ctx)));
			nt_status = gensec_security->auth_context->get_user_info_dc_principal(mem_ctx,
											     gensec_security->auth_context, 
											     principal_string,
											     NULL, &user_info_dc);
			if (!NT_STATUS_IS_OK(nt_status)) {
				free(principal_string);
				krb5_free_principal(context, client_principal);
				talloc_free(mem_ctx);
				return nt_status;
			}
		} else {
			DEBUG(1, ("Unable to find PAC in ticket from %s, failing to allow access\n",
				  principal_string));
			free(principal_string);
			krb5_free_principal(context, client_principal);
			talloc_free(mem_ctx);
			return NT_STATUS_ACCESS_DENIED;
		}
	} else {
		/* Found pac */
		union netr_Validation validation;

		pac = data_blob_talloc(mem_ctx, pac_data.data, pac_data.length);
		if (!pac.data) {
			free(principal_string);
			krb5_free_principal(context, client_principal);
			talloc_free(mem_ctx);
			return NT_STATUS_NO_MEMORY;
		}

		/* decode and verify the pac */
		nt_status = kerberos_pac_logon_info(gensec_krb5_state, 
						    pac,
						    gensec_krb5_state->smb_krb5_context->krb5_context,
						    NULL, gensec_krb5_state->keyblock,
						    client_principal,
						    gensec_krb5_state->ticket->ticket.authtime, &logon_info);

		if (!NT_STATUS_IS_OK(nt_status)) {
			free(principal_string);
			krb5_free_principal(context, client_principal);
			talloc_free(mem_ctx);
			return nt_status;
		}

		validation.sam3 = &logon_info->info3;
		nt_status = make_user_info_dc_netlogon_validation(mem_ctx,
								 NULL,
								 3, &validation,
								  true, /* This user was authenticated */
								 &user_info_dc);
		if (!NT_STATUS_IS_OK(nt_status)) {
			free(principal_string);
			krb5_free_principal(context, client_principal);
			talloc_free(mem_ctx);
			return nt_status;
		}
	}

	free(principal_string);
	krb5_free_principal(context, client_principal);

	/* references the user_info_dc into the session_info */
	nt_status = gensec_generate_session_info(mem_ctx, gensec_security, user_info_dc, &session_info);

	if (!NT_STATUS_IS_OK(nt_status)) {
		talloc_free(mem_ctx);
		return nt_status;
	}

	nt_status = gensec_krb5_session_key(gensec_security, session_info, &session_info->session_key);

	if (!NT_STATUS_IS_OK(nt_status)) {
		talloc_free(mem_ctx);
		return nt_status;
	}

	*_session_info = talloc_steal(mem_ctx_out, session_info);

	talloc_free(mem_ctx);
	return NT_STATUS_OK;
}
krb5_error_code
smb_krb5_init_context_basic(TALLOC_CTX *tmp_ctx,
			    struct loadparm_context *lp_ctx,
			    krb5_context *_krb5_context)
{
	krb5_error_code ret;
#ifdef SAMBA4_USES_HEIMDAL
	char **config_files;
	const char *config_file, *realm;
#endif
	krb5_context krb5_ctx;

	initialize_krb5_error_table();

	ret = krb5_init_context(&krb5_ctx);
	if (ret) {
		DEBUG(1,("krb5_init_context failed (%s)\n",
			 error_message(ret)));
		return ret;
	}

	/* The MIT Kerberos build relies on using the system krb5.conf file.
	 * If you really want to use another file please set KRB5_CONFIG
	 * accordingly. */
#ifdef SAMBA4_USES_HEIMDAL
	config_file = lpcfg_config_path(tmp_ctx, lp_ctx, "krb5.conf");
	if (!config_file) {
		krb5_free_context(krb5_ctx);
		return ENOMEM;
	}

	/* Use our local krb5.conf file by default */
	ret = krb5_prepend_config_files_default(config_file, &config_files);
	if (ret) {
		DEBUG(1,("krb5_prepend_config_files_default failed (%s)\n",
			 smb_get_krb5_error_message(krb5_ctx, ret, tmp_ctx)));
		krb5_free_context(krb5_ctx);
		return ret;
	}

	ret = krb5_set_config_files(krb5_ctx, config_files);
	krb5_free_config_files(config_files);
	if (ret) {
		DEBUG(1,("krb5_set_config_files failed (%s)\n",
			 smb_get_krb5_error_message(krb5_ctx, ret, tmp_ctx)));
		krb5_free_context(krb5_ctx);
		return ret;
	}

	realm = lpcfg_realm(lp_ctx);
	if (realm != NULL) {
		ret = krb5_set_default_realm(krb5_ctx, realm);
		if (ret) {
			DEBUG(1,("krb5_set_default_realm failed (%s)\n",
				 smb_get_krb5_error_message(krb5_ctx, ret, tmp_ctx)));
			krb5_free_context(krb5_ctx);
			return ret;
		}
	}
#endif
	*_krb5_context = krb5_ctx;
	return 0;
}
Esempio n. 6
0
krb5_error_code smb_krb5_update_keytab(TALLOC_CTX *parent_ctx,
                                       krb5_context context,
                                       const char *keytab_name,
                                       const char *samAccountName,
                                       const char *realm,
                                       const char **SPNs,
                                       int num_SPNs,
                                       const char *saltPrincipal,
                                       const char *new_secret,
                                       const char *old_secret,
                                       int kvno,
                                       uint32_t supp_enctypes,
                                       bool delete_all_kvno,
                                       krb5_keytab *_keytab,
                                       const char **error_string)
{
    krb5_keytab keytab;
    krb5_error_code ret;
    bool found_previous;
    TALLOC_CTX *tmp_ctx;
    krb5_principal *principals = NULL;

    if (keytab_name == NULL) {
        return ENOENT;
    }

    ret = krb5_kt_resolve(context, keytab_name, &keytab);
    if (ret) {
        *error_string = smb_get_krb5_error_message(context,
                        ret, parent_ctx);
        return ret;
    }

    DEBUG(5, ("Opened keytab %s\n", keytab_name));

    tmp_ctx = talloc_new(parent_ctx);
    if (!tmp_ctx) {
        return ENOMEM;
    }

    /* Get the principal we will store the new keytab entries under */
    ret = principals_from_list(tmp_ctx,
                               samAccountName, realm, SPNs, num_SPNs,
                               context, &principals, error_string);

    if (ret != 0) {
        *error_string = talloc_asprintf(parent_ctx,
                                        "Failed to load principals from ldb message: %s\n",
                                        *error_string);
        goto done;
    }

    ret = remove_old_entries(tmp_ctx, kvno, principals, delete_all_kvno,
                             context, keytab, &found_previous, error_string);
    if (ret != 0) {
        *error_string = talloc_asprintf(parent_ctx,
                                        "Failed to remove old principals from keytab: %s\n",
                                        *error_string);
        goto done;
    }

    if (!delete_all_kvno) {
        /* Create a new keytab.  If during the cleanout we found
         * entires for kvno -1, then don't try and duplicate them.
         * Otherwise, add kvno, and kvno -1 */

        ret = create_keytab(tmp_ctx,
                            samAccountName, realm, saltPrincipal,
                            kvno, new_secret, old_secret,
                            supp_enctypes, principals,
                            context, keytab,
                            found_previous ? false : true,
                            error_string);
        if (ret) {
            talloc_steal(parent_ctx, *error_string);
        }
    }

    if (ret == 0 && _keytab != NULL) {
        /* caller wants the keytab handle back */
        *_keytab = keytab;
    }

done:
    keytab_principals_free(context, principals);
    if (ret != 0 || _keytab == NULL) {
        krb5_kt_close(context, keytab);
    }
    talloc_free(tmp_ctx);
    return ret;
}
Esempio n. 7
0
static NTSTATUS gensec_krb5_common_client_start(struct gensec_security *gensec_security, bool gssapi)
{
	struct gensec_krb5_state *gensec_krb5_state;
	krb5_error_code ret;
	NTSTATUS nt_status;
	struct ccache_container *ccache_container;
	const char *hostname;
	const char *error_string;
	const char *principal;
	krb5_data in_data;
	struct tevent_context *previous_ev;

	hostname = gensec_get_target_hostname(gensec_security);
	if (!hostname) {
		DEBUG(1, ("Could not determine hostname for target computer, cannot use kerberos\n"));
		return NT_STATUS_INVALID_PARAMETER;
	}
	if (is_ipaddress(hostname)) {
		DEBUG(2, ("Cannot do krb5 to an IP address"));
		return NT_STATUS_INVALID_PARAMETER;
	}
	if (strcmp(hostname, "localhost") == 0) {
		DEBUG(2, ("krb5 to 'localhost' does not make sense"));
		return NT_STATUS_INVALID_PARAMETER;
	}
			
	nt_status = gensec_krb5_start(gensec_security, gssapi);
	if (!NT_STATUS_IS_OK(nt_status)) {
		return nt_status;
	}

	gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
	gensec_krb5_state->state_position = GENSEC_KRB5_CLIENT_START;
	gensec_krb5_state->ap_req_options = AP_OPTS_USE_SUBKEY;

	if (gensec_krb5_state->gssapi) {
		/* The Fake GSSAPI modal emulates Samba3, which does not do mutual authentication */
		if (gensec_setting_bool(gensec_security->settings, "gensec_fake_gssapi_krb5", "mutual", false)) {
			gensec_krb5_state->ap_req_options |= AP_OPTS_MUTUAL_REQUIRED;
		}
	} else {
		/* The wrapping for KPASSWD (a user of the raw KRB5 API) should be mutually authenticated */
		if (gensec_setting_bool(gensec_security->settings, "gensec_krb5", "mutual", true)) {
			gensec_krb5_state->ap_req_options |= AP_OPTS_MUTUAL_REQUIRED;
		}
	}

	principal = gensec_get_target_principal(gensec_security);

	ret = cli_credentials_get_ccache(gensec_get_credentials(gensec_security), 
				         gensec_security->event_ctx, 
					 gensec_security->settings->lp_ctx, &ccache_container, &error_string);
	switch (ret) {
	case 0:
		break;
	case KRB5KDC_ERR_PREAUTH_FAILED:
	case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN:
		return NT_STATUS_LOGON_FAILURE;
	case KRB5_KDC_UNREACH:
		DEBUG(3, ("Cannot reach a KDC we require to contact %s: %s\n", principal, error_string));
		return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
	case KRB5_CC_NOTFOUND:
	case KRB5_CC_END:
		DEBUG(3, ("Error preparing credentials we require to contact %s : %s\n", principal, error_string));
		return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
	default:
		DEBUG(1, ("gensec_krb5_start: Aquiring initiator credentials failed: %s\n", error_string));
		return NT_STATUS_UNSUCCESSFUL;
	}
	in_data.length = 0;
	
	/* Do this every time, in case we have weird recursive issues here */
	ret = smb_krb5_context_set_event_ctx(gensec_krb5_state->smb_krb5_context, gensec_security->event_ctx, &previous_ev);
	if (ret != 0) {
		DEBUG(1, ("gensec_krb5_start: Setting event context failed\n"));
		return NT_STATUS_NO_MEMORY;
	}
	if (principal) {
		krb5_principal target_principal;
		ret = krb5_parse_name(gensec_krb5_state->smb_krb5_context->krb5_context, principal,
				      &target_principal);
		if (ret == 0) {
			ret = krb5_mk_req_exact(gensec_krb5_state->smb_krb5_context->krb5_context, 
						&gensec_krb5_state->auth_context,
						gensec_krb5_state->ap_req_options, 
						target_principal,
						&in_data, ccache_container->ccache, 
						&gensec_krb5_state->enc_ticket);
			krb5_free_principal(gensec_krb5_state->smb_krb5_context->krb5_context, 
					    target_principal);
		}
	} else {
		ret = krb5_mk_req(gensec_krb5_state->smb_krb5_context->krb5_context, 
				  &gensec_krb5_state->auth_context,
				  gensec_krb5_state->ap_req_options,
				  gensec_get_target_service(gensec_security),
				  hostname,
				  &in_data, ccache_container->ccache, 
				  &gensec_krb5_state->enc_ticket);
	}

	smb_krb5_context_remove_event_ctx(gensec_krb5_state->smb_krb5_context, previous_ev, gensec_security->event_ctx);

	switch (ret) {
	case 0:
		return NT_STATUS_OK;
	case KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN:
		DEBUG(3, ("Server [%s] is not registered with our KDC: %s\n", 
			  hostname, smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
		return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
	case KRB5_KDC_UNREACH:
		DEBUG(3, ("Cannot reach a KDC we require to contact host [%s]: %s\n",
			  hostname, smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
		return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
	case KRB5KDC_ERR_PREAUTH_FAILED:
	case KRB5KRB_AP_ERR_TKT_EXPIRED:
	case KRB5_CC_END:
		/* Too much clock skew - we will need to kinit to re-skew the clock */
	case KRB5KRB_AP_ERR_SKEW:
	case KRB5_KDCREP_SKEW:
	{
		DEBUG(3, ("kerberos (mk_req) failed: %s\n", 
			  smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
		/*fall through*/
	}
	
	/* just don't print a message for these really ordinary messages */
	case KRB5_FCC_NOFILE:
	case KRB5_CC_NOTFOUND:
	case ENOENT:
		
		return NT_STATUS_UNSUCCESSFUL;
		break;
		
	default:
		DEBUG(0, ("kerberos: %s\n", 
			  smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
		return NT_STATUS_UNSUCCESSFUL;
	}
}
Esempio n. 8
0
File: pac.c Progetto: sprymak/samba
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_user_info_dc *user_info_dc;
	struct auth_user_info_dc *user_info_dc_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_user_info_dc(mem_ctx, lpcfg_netbios_name(tctx->lp_ctx), &user_info_dc);

	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_user_info_dc");
	}

	ret = krb5_parse_name_flags(smb_krb5_context->krb5_context, 
				    user_info_dc->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, 
				  user_info_dc,
				  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, 
					tmp_blob,
					smb_krb5_context->krb5_context,
					&krbtgt_keyblock,
					&server_keyblock,
					client_principal, 
					logon_time,
 					&pac_data);

	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_user_info_dc(mem_ctx,
						     tmp_blob, 
						     smb_krb5_context->krb5_context,
						      &user_info_dc_out, NULL, NULL);

	/* The user's SID is the first element in the list */
	if (!dom_sid_equal(user_info_dc->sids,
			   user_info_dc_out->sids)) {
		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, user_info_dc->sids),
					     dom_sid_string(mem_ctx, user_info_dc_out->sids)));
	}
	talloc_free(user_info_dc_out);

	/* Now check that we can read it back (yet again) */
	nt_status = kerberos_pac_logon_info(mem_ctx, 
					    tmp_blob,
					    smb_krb5_context->krb5_context,
					    &krbtgt_keyblock,
					    &server_keyblock,
					    client_principal, 
					    logon_time, 
					    &logon_info);
	
	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_user_info_dc_netlogon_validation(mem_ctx,
							 "",
							 3, &validation,
							  true, /* This user was authenticated */
						 &user_info_dc_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(user_info_dc->sids,
			   user_info_dc_out->sids)) {
		torture_fail(tctx,  
			     talloc_asprintf(tctx, 
					     "(self test) PAC Decode resulted in *different* domain SID: %s != %s",
					     dom_sid_string(mem_ctx, user_info_dc->sids),
					     dom_sid_string(mem_ctx, user_info_dc_out->sids)));
	}
	return true;
}
Esempio n. 9
0
_PUBLIC_ int cli_credentials_set_ccache(struct cli_credentials *cred, 
					struct loadparm_context *lp_ctx,
					const char *name,
					enum credentials_obtained obtained,
					const char **error_string)
{
	krb5_error_code ret;
	krb5_principal princ;
	struct ccache_container *ccc;
	if (cred->ccache_obtained > obtained) {
		return 0;
	}

	ccc = talloc(cred, struct ccache_container);
	if (!ccc) {
		(*error_string) = error_message(ENOMEM);
		return ENOMEM;
	}

	ret = cli_credentials_get_krb5_context(cred, lp_ctx,
					       &ccc->smb_krb5_context);
	if (ret) {
		(*error_string) = error_message(ret);
		talloc_free(ccc);
		return ret;
	}
	if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
		talloc_free(ccc);
		(*error_string) = error_message(ENOMEM);
		return ENOMEM;
	}

	if (name) {
		ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, name, &ccc->ccache);
		if (ret) {
			(*error_string) = talloc_asprintf(cred, "failed to read krb5 ccache: %s: %s\n",
							  name,
							  smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
										     ret, ccc));
			talloc_free(ccc);
			return ret;
		}
	} else {
		ret = krb5_cc_default(ccc->smb_krb5_context->krb5_context, &ccc->ccache);
		if (ret) {
			(*error_string) = talloc_asprintf(cred, "failed to read default krb5 ccache: %s\n",
							  smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
										     ret, ccc));
			talloc_free(ccc);
			return ret;
		}
	}

	talloc_set_destructor(ccc, free_dccache);

	ret = krb5_cc_get_principal(ccc->smb_krb5_context->krb5_context, ccc->ccache, &princ);

	if (ret == 0) {
		krb5_free_principal(ccc->smb_krb5_context->krb5_context, princ);
		ret = cli_credentials_set_from_ccache(cred, ccc, obtained, error_string);

		if (ret) {
			(*error_string) = error_message(ret);
			return ret;
		}

		cred->ccache = ccc;
		cred->ccache_obtained = obtained;
		talloc_steal(cred, ccc);

		cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
		return 0;
	}
	return 0;
}
Esempio n. 10
0
 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;
}
Esempio n. 11
0
File: pac.c Progetto: sprymak/samba
/* Check with a known 'well formed' PAC, from my test server */
static bool torture_pac_saved_check(struct torture_context *tctx)
{
	NTSTATUS nt_status;
	enum ndr_err_code ndr_err;
	DATA_BLOB tmp_blob, validate_blob;
	struct PAC_DATA *pac_data, pac_data2;
	struct PAC_LOGON_INFO *logon_info;
	union netr_Validation validation;
	const char *pac_file, *pac_kdc_key, *pac_member_key;
	struct auth_user_info_dc *user_info_dc_out;

	krb5_keyblock server_keyblock;
	krb5_keyblock krbtgt_keyblock, *krbtgt_keyblock_p;
	struct samr_Password *krbtgt_bytes, *krbsrv_bytes;
	
	krb5_error_code ret;
	struct smb_krb5_context *smb_krb5_context;

	const char *principal_string;
	char *broken_principal_string;
	krb5_principal client_principal;
	const char *authtime_string;
	time_t authtime;
	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");

	pac_kdc_key = torture_setting_string(tctx, "pac_kdc_key", 
					     "B286757148AF7FD252C53603A150B7E7");

	pac_member_key = torture_setting_string(tctx, "pac_member_key", 
						"D217FAEAE5E6B5F95CCC94077AB8A5FC");

	torture_comment(tctx, "Using pac_kdc_key '%s'\n", pac_kdc_key);
	torture_comment(tctx, "Using pac_member_key '%s'\n", pac_member_key);

	/* The krbtgt key in use when the above PAC was generated.
	 * This is an arcfour-hmac-md5 key, extracted with our 'net
	 * samdump' tool. */
	if (*pac_kdc_key == 0) {
		krbtgt_bytes = NULL;
	} else {
		krbtgt_bytes = smbpasswd_gethexpwd(mem_ctx, pac_kdc_key);
		if (!krbtgt_bytes) {
			torture_fail(tctx, "(saved test) Could not interpret krbtgt key");
		}
	}

	krbsrv_bytes = smbpasswd_gethexpwd(mem_ctx, pac_member_key);
	if (!krbsrv_bytes) {
		torture_fail(tctx, "(saved test) Could not interpret krbsrv key");
	}

	ret = krb5_keyblock_init(smb_krb5_context->krb5_context,
				 ENCTYPE_ARCFOUR_HMAC,
				 krbsrv_bytes->hash, sizeof(krbsrv_bytes->hash),
				 &server_keyblock);
	torture_assert(tctx, !ret,
		       talloc_asprintf(tctx,
				       "(saved test) Server Keyblock encoding failed: %s", 
				       smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
								  ret, mem_ctx)));

	if (krbtgt_bytes) {
		ret = krb5_keyblock_init(smb_krb5_context->krb5_context,
					 ENCTYPE_ARCFOUR_HMAC,
					 krbtgt_bytes->hash, sizeof(krbtgt_bytes->hash),
					 &krbtgt_keyblock);
		if (ret) {
			krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
						    &server_keyblock);
			torture_fail(tctx, 
				     talloc_asprintf(tctx, 
						     "(saved test) Server Keyblock encoding failed: %s", 
						     smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
										ret, mem_ctx)));
		}
		krbtgt_keyblock_p = &krbtgt_keyblock;
	} else {
		krbtgt_keyblock_p = NULL;
	}

	pac_file = torture_setting_string(tctx, "pac_file", NULL);
	if (pac_file) {
		tmp_blob.data = (uint8_t *)file_load(pac_file, &tmp_blob.length, 0, mem_ctx);
		torture_comment(tctx, "(saved test) Loaded pac of size %ld from %s\n", (long)tmp_blob.length, pac_file);
	} else {
		tmp_blob = data_blob_talloc(mem_ctx, saved_pac, sizeof(saved_pac));
	}
	
	dump_data(10,tmp_blob.data,tmp_blob.length);

	principal_string = torture_setting_string(tctx, "pac_client_principal", 
						  "[email protected]");

	authtime_string = torture_setting_string(tctx, "pac_authtime", "1120440609");
	authtime = strtoull(authtime_string, NULL, 0);

	ret = krb5_parse_name(smb_krb5_context->krb5_context, principal_string, 
			      &client_principal);
	if (ret) {
		krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
					    krbtgt_keyblock_p);
		krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
					    &server_keyblock);
		torture_fail(tctx,  
			     talloc_asprintf(tctx, 
					     "(saved test) parsing of client principal [%s] failed: %s", 
					     principal_string, 
					     smb_get_krb5_error_message(smb_krb5_context->krb5_context, ret, mem_ctx)));
	}

	/* Decode and verify the signaure on the PAC */
	nt_status = kerberos_decode_pac(mem_ctx, 
					tmp_blob,
					smb_krb5_context->krb5_context,
					krbtgt_keyblock_p,
					&server_keyblock, 
					client_principal, authtime, &pac_data);
	if (!NT_STATUS_IS_OK(nt_status)) {
		krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
					    krbtgt_keyblock_p);
		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, 
						   "(saved 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_user_info_dc(mem_ctx,
						     tmp_blob, 
						     smb_krb5_context->krb5_context,
						      &user_info_dc_out,
						      NULL, NULL);

	if (!NT_STATUS_IS_OK(nt_status)) {
		krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
					    krbtgt_keyblock_p);
		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, 
						   "(saved test) Heimdal PAC decoding failed: %s", 
						   nt_errstr(nt_status)));
	}

	if (!pac_file &&
	    !dom_sid_equal(dom_sid_parse_talloc(mem_ctx, 
						"S-1-5-21-3048156945-3961193616-3706469200-1005"), 
			   user_info_dc_out->sids)) {
		krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
					    krbtgt_keyblock_p);
		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, 
					     "(saved test) Heimdal PAC Decode resulted in *different* domain SID: %s != %s",
					     "S-1-5-21-3048156945-3961193616-3706469200-1005", 
					     dom_sid_string(mem_ctx, user_info_dc_out->sids)));
	}

	talloc_free(user_info_dc_out);

	/* Parse the PAC again, for the logon info this time (using Samba4's parsing) */
	nt_status = kerberos_pac_logon_info(mem_ctx, 
					    tmp_blob,
					    smb_krb5_context->krb5_context,
					    krbtgt_keyblock_p,
					    &server_keyblock,
					    client_principal, authtime, &logon_info);

	if (!NT_STATUS_IS_OK(nt_status)) {
		krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
					    krbtgt_keyblock_p);
		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, 
					     "(saved test) PAC decoding (for logon info) failed: %s", 
					     nt_errstr(nt_status)));
	}

	validation.sam3 = &logon_info->info3;
	nt_status = make_user_info_dc_netlogon_validation(mem_ctx,
							 "",
							 3, &validation,
							  true, /* This user was authenticated */
							 &user_info_dc_out);
	if (!NT_STATUS_IS_OK(nt_status)) {
		krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
					    krbtgt_keyblock_p);
		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, 
					     "(saved test) PAC decoding (make server info) failed: %s", 
					     nt_errstr(nt_status)));
	}

	if (!pac_file &&
	    !dom_sid_equal(dom_sid_parse_talloc(mem_ctx, 
						"S-1-5-21-3048156945-3961193616-3706469200-1005"), 
			   user_info_dc_out->sids)) {
		krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
					    krbtgt_keyblock_p);
		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, 
					     "(saved test) PAC Decode resulted in *different* domain SID: %s != %s",
					     "S-1-5-21-3048156945-3961193616-3706469200-1005", 
					     dom_sid_string(mem_ctx, user_info_dc_out->sids)));
	}

	if (krbtgt_bytes == NULL) {
		torture_comment(tctx, "skipping PAC encoding tests as non kdc key\n");
		krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
					    &server_keyblock);
		krb5_free_principal(smb_krb5_context->krb5_context, client_principal);
		return true;
	}

	ret = kerberos_encode_pac(mem_ctx, 
				  pac_data,
				  smb_krb5_context->krb5_context,
				  krbtgt_keyblock_p,
				  &server_keyblock,
				  &validate_blob);

	if (ret != 0) {
		krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
					    krbtgt_keyblock_p);
		krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
					    &server_keyblock);
		krb5_free_principal(smb_krb5_context->krb5_context, client_principal);

		torture_fail(tctx, "(saved test) PAC push failed");
	}

	dump_data(10, validate_blob.data, validate_blob.length);

	/* compare both the length and the data bytes after a
	 * pull/push cycle.  This ensures we use the exact same
	 * pointer, padding etc algorithms as win2k3.
	 */
	if (tmp_blob.length != validate_blob.length) {
		krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
					    krbtgt_keyblock_p);
		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, 
					     "(saved test) PAC push failed: original buffer length[%u] != created buffer length[%u]",
					     (unsigned)tmp_blob.length, (unsigned)validate_blob.length));
	}

	if (memcmp(tmp_blob.data, validate_blob.data, tmp_blob.length) != 0) {
		krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
					    krbtgt_keyblock_p);
		krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
					    &server_keyblock);
		krb5_free_principal(smb_krb5_context->krb5_context, client_principal);

		DEBUG(0, ("tmp_data:\n"));
		dump_data(0, tmp_blob.data, tmp_blob.length);
		DEBUG(0, ("validate_blob:\n"));
		dump_data(0, validate_blob.data, validate_blob.length);

		torture_fail(tctx, talloc_asprintf(tctx, "(saved test) PAC push failed: length[%u] matches, but data does not", (unsigned)tmp_blob.length));
	}

	ret = kerberos_create_pac(mem_ctx, 
				  user_info_dc_out,
				  smb_krb5_context->krb5_context,
				  krbtgt_keyblock_p,
				  &server_keyblock,
				  client_principal, authtime,
				  &validate_blob);

	if (ret != 0) {
		krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
					    krbtgt_keyblock_p);
		krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
					    &server_keyblock);
		krb5_free_principal(smb_krb5_context->krb5_context, client_principal);

		torture_fail(tctx, "(saved test) regnerated PAC create failed");
	}

	dump_data(10,validate_blob.data,validate_blob.length);

	/* compare both the length and the data bytes after a
	 * pull/push cycle.  This ensures we use the exact same
	 * pointer, padding etc algorithms as win2k3.
	 */
	if (tmp_blob.length != validate_blob.length) {
		ndr_err = ndr_pull_struct_blob(&validate_blob, mem_ctx, 
					       &pac_data2,
					       (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA);
		nt_status = ndr_map_error2ntstatus(ndr_err);
		torture_assert_ntstatus_ok(tctx, nt_status, "can't parse the PAC");
		
		NDR_PRINT_DEBUG(PAC_DATA, pac_data);

		NDR_PRINT_DEBUG(PAC_DATA, &pac_data2);

		krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
					    krbtgt_keyblock_p);
		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, 
						   "(saved test) PAC regenerate failed: original buffer length[%u] != created buffer length[%u]",
						   (unsigned)tmp_blob.length, (unsigned)validate_blob.length));
	}

	if (memcmp(tmp_blob.data, validate_blob.data, tmp_blob.length) != 0) {
		ndr_err = ndr_pull_struct_blob(&validate_blob, mem_ctx, 
					       &pac_data2,
					       (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA);
		nt_status = ndr_map_error2ntstatus(ndr_err);
		torture_assert_ntstatus_ok(tctx, nt_status, "can't parse the PAC");
		
		NDR_PRINT_DEBUG(PAC_DATA, pac_data);

		NDR_PRINT_DEBUG(PAC_DATA, &pac_data2);

		krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
					    krbtgt_keyblock_p);
		krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
					    &server_keyblock);
		krb5_free_principal(smb_krb5_context->krb5_context, client_principal);

		DEBUG(0, ("tmp_data:\n"));
		dump_data(0, tmp_blob.data, tmp_blob.length);
		DEBUG(0, ("validate_blob:\n"));
		dump_data(0, validate_blob.data, validate_blob.length);

		torture_fail(tctx, talloc_asprintf(tctx, 
						   "(saved test) PAC regenerate failed: length[%u] matches, but data does not", (unsigned)tmp_blob.length));
	}

	/* Break the auth time, to ensure we check this vital detail (not setting this caused all the pain in the first place... */
	nt_status = kerberos_decode_pac(mem_ctx, 
					tmp_blob,
					smb_krb5_context->krb5_context,
					krbtgt_keyblock_p,
					&server_keyblock,
					client_principal, 
					authtime + 1, &pac_data);
	if (NT_STATUS_IS_OK(nt_status)) {

		krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
					    krbtgt_keyblock_p);
		krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
					    &server_keyblock);
		krb5_free_principal(smb_krb5_context->krb5_context, client_principal);
		torture_fail(tctx, "(saved test) PAC decoding DID NOT fail on broken auth time (time + 1)");
	}

	/* Break the client principal */
	krb5_free_principal(smb_krb5_context->krb5_context, client_principal);

	broken_principal_string = talloc_strdup(mem_ctx, principal_string);
	broken_principal_string[0]++;

	ret = krb5_parse_name(smb_krb5_context->krb5_context,
			      broken_principal_string, &client_principal);
	if (ret) {

		krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
					    krbtgt_keyblock_p);
		krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
					    &server_keyblock);
		torture_fail(tctx, talloc_asprintf(tctx, 
						   "(saved test) parsing of broken client principal failed: %s", 
						   smb_get_krb5_error_message(smb_krb5_context->krb5_context, ret, mem_ctx)));
	}

	nt_status = kerberos_decode_pac(mem_ctx, 
					tmp_blob,
					smb_krb5_context->krb5_context,
					krbtgt_keyblock_p,
					&server_keyblock,
					client_principal, 
					authtime, &pac_data);
	if (NT_STATUS_IS_OK(nt_status)) {
		krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
					    krbtgt_keyblock_p);
		krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
					    &server_keyblock);
		torture_fail(tctx, "(saved test) PAC decoding DID NOT fail on modified principal");
	}

	/* Finally...  Bugger up the signature, and check we fail the checksum */
	tmp_blob.data[tmp_blob.length - 2]++;

	nt_status = kerberos_decode_pac(mem_ctx, 
					tmp_blob,
					smb_krb5_context->krb5_context,
					krbtgt_keyblock_p,
					&server_keyblock,
					client_principal, 
					authtime,
					&pac_data);
	if (NT_STATUS_IS_OK(nt_status)) {
		krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
					    krbtgt_keyblock_p);
		krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
					    &server_keyblock);
		torture_fail(tctx, "(saved test) PAC decoding DID NOT fail on broken checksum");
	}

	krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
				    krbtgt_keyblock_p);
	krb5_free_keyblock_contents(smb_krb5_context->krb5_context, 
				    &server_keyblock);
	return true;
}
Esempio n. 12
0
static NTSTATUS gensec_krb5_session_info(struct gensec_security *gensec_security,
					 TALLOC_CTX *mem_ctx,
					 struct auth_session_info **_session_info) 
{
	NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
	struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
	krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context;
	struct auth_session_info *session_info = NULL;

	krb5_principal client_principal;
	char *principal_string;
	
	DATA_BLOB pac_blob, *pac_blob_ptr = NULL;
	krb5_data pac_data;

	krb5_error_code ret;

	TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
	if (!tmp_ctx) {
		return NT_STATUS_NO_MEMORY;
	}
	
	ret = krb5_ticket_get_client(context, gensec_krb5_state->ticket, &client_principal);
	if (ret) {
		DEBUG(5, ("krb5_ticket_get_client failed to get cleint principal: %s\n", 
			  smb_get_krb5_error_message(context, 
						     ret, tmp_ctx)));
		talloc_free(tmp_ctx);
		return NT_STATUS_NO_MEMORY;
	}
	
	ret = krb5_unparse_name(gensec_krb5_state->smb_krb5_context->krb5_context, 
				client_principal, &principal_string);
	if (ret) {
		DEBUG(1, ("Unable to parse client principal: %s\n",
			  smb_get_krb5_error_message(context, 
						     ret, tmp_ctx)));
		krb5_free_principal(context, client_principal);
		talloc_free(tmp_ctx);
		return NT_STATUS_NO_MEMORY;
	}

	ret = krb5_ticket_get_authorization_data_type(context, gensec_krb5_state->ticket, 
						      KRB5_AUTHDATA_WIN2K_PAC, 
						      &pac_data);
	
	if (ret) {
		/* NO pac */
		DEBUG(5, ("krb5_ticket_get_authorization_data_type failed to find PAC: %s\n", 
			  smb_get_krb5_error_message(context, 
						     ret, tmp_ctx)));
	} else {
		/* Found pac */
		pac_blob = data_blob_talloc(tmp_ctx, pac_data.data, pac_data.length);
		if (!pac_blob.data) {
			free(principal_string);
			krb5_free_principal(context, client_principal);
			talloc_free(tmp_ctx);
			return NT_STATUS_NO_MEMORY;
		}

		/* decode and verify the pac */
		nt_status = kerberos_decode_pac(gensec_krb5_state,
						pac_blob,
						gensec_krb5_state->smb_krb5_context->krb5_context,
						NULL, gensec_krb5_state->keyblock,
						client_principal,
						gensec_krb5_state->ticket->ticket.authtime, NULL);

		if (!NT_STATUS_IS_OK(nt_status)) {
			free(principal_string);
			krb5_free_principal(context, client_principal);
			talloc_free(tmp_ctx);
			return nt_status;
		}

		pac_blob_ptr = &pac_blob;
	}

	nt_status = gensec_generate_session_info_pac(tmp_ctx,
						     gensec_security,
						     gensec_krb5_state->smb_krb5_context,
						     pac_blob_ptr, principal_string,
						     gensec_get_remote_address(gensec_security),
						     &session_info);

	free(principal_string);
	krb5_free_principal(context, client_principal);

	if (!NT_STATUS_IS_OK(nt_status)) {
		talloc_free(tmp_ctx);
		return nt_status;
	}

	nt_status = gensec_krb5_session_key(gensec_security, session_info, &session_info->session_key);

	if (!NT_STATUS_IS_OK(nt_status)) {
		talloc_free(tmp_ctx);
		return nt_status;
	}

	*_session_info = talloc_steal(mem_ctx, session_info);

	talloc_free(tmp_ctx);
	return NT_STATUS_OK;
}
Esempio n. 13
0
static NTSTATUS gensec_krb5_common_client_creds(struct gensec_security *gensec_security,
						struct tevent_context *ev,
						bool gssapi)
{
	struct gensec_krb5_state *gensec_krb5_state;
	krb5_error_code ret;
	struct ccache_container *ccache_container;
	const char *error_string;
	const char *principal;
	const char *hostname;
	krb5_data in_data;
	struct tevent_context *previous_ev;

	gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;

	principal = gensec_get_target_principal(gensec_security);
	hostname = gensec_get_target_hostname(gensec_security);

	ret = cli_credentials_get_ccache(gensec_get_credentials(gensec_security), 
				         ev,
					 gensec_security->settings->lp_ctx, &ccache_container, &error_string);
	switch (ret) {
	case 0:
		break;
	case KRB5KDC_ERR_PREAUTH_FAILED:
	case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN:
		return NT_STATUS_LOGON_FAILURE;
	case KRB5_KDC_UNREACH:
		DEBUG(3, ("Cannot reach a KDC we require to contact %s: %s\n", principal, error_string));
		return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
	case KRB5_CC_NOTFOUND:
	case KRB5_CC_END:
		DEBUG(3, ("Error preparing credentials we require to contact %s : %s\n", principal, error_string));
		return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
	default:
		DEBUG(1, ("gensec_krb5_start: Aquiring initiator credentials failed: %s\n", error_string));
		return NT_STATUS_UNSUCCESSFUL;
	}
	in_data.length = 0;
	
	/* Do this every time, in case we have weird recursive issues here */
	ret = smb_krb5_context_set_event_ctx(gensec_krb5_state->smb_krb5_context, ev, &previous_ev);
	if (ret != 0) {
		DEBUG(1, ("gensec_krb5_start: Setting event context failed\n"));
		return NT_STATUS_NO_MEMORY;
	}
	if (principal) {
		krb5_principal target_principal;
		ret = krb5_parse_name(gensec_krb5_state->smb_krb5_context->krb5_context, principal,
				      &target_principal);
		if (ret == 0) {
			ret = krb5_mk_req_exact(gensec_krb5_state->smb_krb5_context->krb5_context, 
						&gensec_krb5_state->auth_context,
						gensec_krb5_state->ap_req_options, 
						target_principal,
						&in_data, ccache_container->ccache, 
						&gensec_krb5_state->enc_ticket);
			krb5_free_principal(gensec_krb5_state->smb_krb5_context->krb5_context, 
					    target_principal);
		}
	} else {
		ret = krb5_mk_req(gensec_krb5_state->smb_krb5_context->krb5_context, 
				  &gensec_krb5_state->auth_context,
				  gensec_krb5_state->ap_req_options,
				  gensec_get_target_service(gensec_security),
				  hostname,
				  &in_data, ccache_container->ccache, 
				  &gensec_krb5_state->enc_ticket);
	}

	smb_krb5_context_remove_event_ctx(gensec_krb5_state->smb_krb5_context, previous_ev, ev);

	switch (ret) {
	case 0:
		return NT_STATUS_OK;
	case KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN:
		DEBUG(3, ("Server [%s] is not registered with our KDC: %s\n", 
			  hostname, smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
		return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
	case KRB5_KDC_UNREACH:
		DEBUG(3, ("Cannot reach a KDC we require to contact host [%s]: %s\n",
			  hostname, smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
		return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */
	case KRB5KDC_ERR_PREAUTH_FAILED:
	case KRB5KRB_AP_ERR_TKT_EXPIRED:
	case KRB5_CC_END:
		/* Too much clock skew - we will need to kinit to re-skew the clock */
	case KRB5KRB_AP_ERR_SKEW:
	case KRB5_KDCREP_SKEW:
	{
		DEBUG(3, ("kerberos (mk_req) failed: %s\n", 
			  smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
		/*fall through*/
	}
	
	/* just don't print a message for these really ordinary messages */
	case KRB5_FCC_NOFILE:
	case KRB5_CC_NOTFOUND:
	case ENOENT:
		
		return NT_STATUS_UNSUCCESSFUL;
		break;
		
	default:
		DEBUG(0, ("kerberos: %s\n", 
			  smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state)));
		return NT_STATUS_UNSUCCESSFUL;
	}
}
Esempio n. 14
0
static krb5_error_code remove_old_entries(TALLOC_CTX *parent_ctx,
					  struct cli_credentials *machine_account,
					  struct smb_krb5_context *smb_krb5_context,
					  krb5_keytab keytab, BOOL *found_previous)
{
	krb5_error_code ret, ret2;
	krb5_kt_cursor cursor;
	krb5_principal princ;
	int kvno;
	TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
	const char *princ_string;
	if (!mem_ctx) {
		return ENOMEM;
	}

	*found_previous = False;
	princ_string = cli_credentials_get_principal(machine_account, mem_ctx);

	/* Get the principal we will store the new keytab entries under */
	ret = principal_from_credentials(mem_ctx, machine_account, smb_krb5_context, &princ);
	if (ret) {
		DEBUG(1,("update_keytab: makeing krb5 principal failed (%s)\n",
			 smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
						    ret, mem_ctx)));
		talloc_free(mem_ctx);
		return ret;
	}

	kvno = cli_credentials_get_kvno(machine_account);

	/* for each entry in the keytab */
	ret = krb5_kt_start_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);
	switch (ret) {
	case 0:
		break;
	case HEIM_ERR_OPNOTSUPP:
	case ENOENT:
	case KRB5_KT_END:
		/* no point enumerating if there isn't anything here */
		talloc_free(mem_ctx);
		return 0;
	default:
		DEBUG(1,("failed to open keytab for read of old entries: %s\n",
			 smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
						    ret, mem_ctx)));
		talloc_free(mem_ctx);
		return ret;
	}

	while (!ret) {
		krb5_keytab_entry entry;
		ret = krb5_kt_next_entry(smb_krb5_context->krb5_context, keytab, &entry, &cursor);
		if (ret) {
			break;
		}
		/* if it matches our principal */
		if (!krb5_kt_compare(smb_krb5_context->krb5_context, &entry, princ, 0, 0)) {
			/* Free the entry, it wasn't the one we were looking for anyway */
			krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
			continue;
		}

		/* delete it, if it is not kvno -1 */
		if (entry.vno != (kvno - 1 )) {
			/* Release the enumeration.  We are going to
			 * have to start this from the top again,
			 * because deletes during enumeration may not
			 * always be consistant.
			 *
			 * Also, the enumeration locks a FILE: keytab
			 */
		
			krb5_kt_end_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);

			ret = krb5_kt_remove_entry(smb_krb5_context->krb5_context, keytab, &entry);
			krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);

			/* Deleted: Restart from the top */
			ret2 = krb5_kt_start_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);
			if (ret2) {
				krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
				DEBUG(1,("failed to restart enumeration of keytab: %s\n",
					 smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
								    ret, mem_ctx)));
				
				talloc_free(mem_ctx);
				return ret2;
			}

			if (ret) {
				break;
			}
			
		} else {
			*found_previous = True;
		}
		
		/* Free the entry, we don't need it any more */
		krb5_kt_free_entry(smb_krb5_context->krb5_context, &entry);
		
		
	}
	krb5_kt_end_seq_get(smb_krb5_context->krb5_context, keytab, &cursor);

	switch (ret) {
	case 0:
		break;
	case ENOENT:
	case KRB5_KT_END:
		ret = 0;
		break;
	default:
		DEBUG(1,("failed in deleting old entries for principal: %s: %s\n",
			 princ_string, 
			 smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
						    ret, mem_ctx)));
	}
	talloc_free(mem_ctx);
	return ret;
}
Esempio n. 15
0
static krb5_error_code remove_old_entries(TALLOC_CTX *parent_ctx,
        int kvno,
        krb5_principal *principals,
        bool delete_all_kvno,
        krb5_context context,
        krb5_keytab keytab,
        bool *found_previous,
        const char **error_string)
{
    krb5_error_code ret, ret2;
    krb5_kt_cursor cursor;
    TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);

    if (!mem_ctx) {
        return ENOMEM;
    }

    *found_previous = false;

    /* for each entry in the keytab */
    ret = krb5_kt_start_seq_get(context, keytab, &cursor);
    switch (ret) {
    case 0:
        break;
#ifdef HEIM_ERR_OPNOTSUPP
    case HEIM_ERR_OPNOTSUPP:
#endif
    case ENOENT:
    case KRB5_KT_END:
        /* no point enumerating if there isn't anything here */
        talloc_free(mem_ctx);
        return 0;
    default:
        *error_string = talloc_asprintf(parent_ctx,
                                        "failed to open keytab for read of old entries: %s\n",
                                        smb_get_krb5_error_message(context, ret, mem_ctx));
        talloc_free(mem_ctx);
        return ret;
    }

    while (!ret) {
        unsigned int i;
        bool matched = false;
        krb5_keytab_entry entry;
        ret = krb5_kt_next_entry(context, keytab, &entry, &cursor);
        if (ret) {
            break;
        }
        for (i = 0; principals[i]; i++) {
            /* if it matches our principal */
            if (smb_krb5_kt_compare(context, &entry,
                                    principals[i], 0, 0)) {
                matched = true;
                break;
            }
        }

        if (!matched) {
            /* Free the entry,
             * it wasn't the one we were looking for anyway */
            krb5_kt_free_entry(context, &entry);
            continue;
        }

        /* delete it, if it is not kvno -1 */
        if (entry.vno != (kvno - 1 )) {
            /* Release the enumeration.  We are going to
             * have to start this from the top again,
             * because deletes during enumeration may not
             * always be consistent.
             *
             * Also, the enumeration locks a FILE: keytab
             */

            krb5_kt_end_seq_get(context, keytab, &cursor);

            ret = krb5_kt_remove_entry(context, keytab, &entry);
            krb5_kt_free_entry(context, &entry);

            /* Deleted: Restart from the top */
            ret2 = krb5_kt_start_seq_get(context, keytab, &cursor);
            if (ret2) {
                krb5_kt_free_entry(context, &entry);
                DEBUG(1, ("failed to restart enumeration of keytab: %s\n",
                          smb_get_krb5_error_message(context,
                                                     ret, mem_ctx)));

                talloc_free(mem_ctx);
                return ret2;
            }

            if (ret) {
                break;
            }

        } else {
            *found_previous = true;
        }

        /* Free the entry, we don't need it any more */
        krb5_kt_free_entry(context, &entry);
    }
    krb5_kt_end_seq_get(context, keytab, &cursor);

    switch (ret) {
    case 0:
        break;
    case ENOENT:
    case KRB5_KT_END:
        ret = 0;
        break;
    default:
        *error_string = talloc_asprintf(parent_ctx,
                                        "failed in deleting old entries for principal: %s\n",
                                        smb_get_krb5_error_message(context, ret, mem_ctx));
    }
    talloc_free(mem_ctx);
    return ret;
}
Esempio n. 16
0
static int cli_credentials_new_ccache(struct cli_credentials *cred, 
				      struct loadparm_context *lp_ctx,
				      char *ccache_name,
				      struct ccache_container **_ccc,
				      const char **error_string)
{
	bool must_free_cc_name = false;
	krb5_error_code ret;
	struct ccache_container *ccc = talloc(cred, struct ccache_container);
	if (!ccc) {
		return ENOMEM;
	}

	ret = cli_credentials_get_krb5_context(cred, lp_ctx,
					       &ccc->smb_krb5_context);
	if (ret) {
		talloc_free(ccc);
		(*error_string) = talloc_asprintf(cred, "Failed to get krb5_context: %s",
						  error_message(ret));
		return ret;
	}
	if (!talloc_reference(ccc, ccc->smb_krb5_context)) {
		talloc_free(ccc);
		(*error_string) = strerror(ENOMEM);
		return ENOMEM;
	}

	if (!ccache_name) {
		must_free_cc_name = true;

		if (lpcfg_parm_bool(lp_ctx, NULL, "credentials", "krb5_cc_file", false)) {
			ccache_name = talloc_asprintf(ccc, "FILE:/tmp/krb5_cc_samba_%u_%p", 
						      (unsigned int)getpid(), ccc);
		} else {
			ccache_name = talloc_asprintf(ccc, "MEMORY:%p", 
						      ccc);
		}

		if (!ccache_name) {
			talloc_free(ccc);
			(*error_string) = strerror(ENOMEM);
			return ENOMEM;
		}
	}

	ret = krb5_cc_resolve(ccc->smb_krb5_context->krb5_context, ccache_name, 
			      &ccc->ccache);
	if (ret) {
		(*error_string) = talloc_asprintf(cred, "failed to resolve a krb5 ccache (%s): %s\n",
						  ccache_name,
						  smb_get_krb5_error_message(ccc->smb_krb5_context->krb5_context,
									     ret, ccc));
		talloc_free(ccache_name);
		talloc_free(ccc);
		return ret;
	}

	if (strncasecmp(ccache_name, "MEMORY:", 7) == 0) {
		talloc_set_destructor(ccc, free_mccache);
	} else {
		talloc_set_destructor(ccc, free_dccache);
	}

	if (must_free_cc_name) {
		talloc_free(ccache_name);
	}

	*_ccc = ccc;

	return 0;
}
Esempio n. 17
0
static krb5_error_code principals_from_list(TALLOC_CTX *parent_ctx,
        const char *samAccountName,
        const char *realm,
        const char **SPNs, int num_SPNs,
        krb5_context context,
        krb5_principal **principals_out,
        const char **error_string)
{
    unsigned int i;
    krb5_error_code ret;
    char *upper_realm;
    TALLOC_CTX *tmp_ctx;
    krb5_principal *principals = NULL;
    tmp_ctx = talloc_new(parent_ctx);
    if (!tmp_ctx) {
        *error_string = "Cannot allocate tmp_ctx";
        return ENOMEM;
    }

    if (!realm) {
        *error_string = "Cannot make principal without a realm";
        ret = EINVAL;
        goto done;
    }

    upper_realm = strupper_talloc(tmp_ctx, realm);
    if (!upper_realm) {
        *error_string = "Cannot allocate full upper case realm";
        ret = ENOMEM;
        goto done;
    }

    principals = talloc_zero_array(tmp_ctx, krb5_principal,
                                   num_SPNs ? (num_SPNs + 2) : 2);

    for (i = 0; num_SPNs && i < num_SPNs; i++) {
        ret = krb5_parse_name(context, SPNs[i], &principals[i]);

        if (ret) {
            *error_string = smb_get_krb5_error_message(context, ret,
                            parent_ctx);
            goto done;
        }
    }

    if (samAccountName) {
        ret = smb_krb5_make_principal(context, &principals[i],
                                      upper_realm, samAccountName,
                                      NULL);
        if (ret) {
            *error_string = smb_get_krb5_error_message(context, ret,
                            parent_ctx);
            goto done;
        }
    }

done:
    if (ret) {
        keytab_principals_free(context, principals);
    } else {
        *principals_out = talloc_steal(parent_ctx, principals);
    }
    talloc_free(tmp_ctx);
    return ret;
}
Esempio n. 18
0
_PUBLIC_ int cli_credentials_get_named_ccache(struct cli_credentials *cred, 
					      struct tevent_context *event_ctx,
					      struct loadparm_context *lp_ctx,
					      char *ccache_name,
					      struct ccache_container **ccc,
					      const char **error_string)
{
	krb5_error_code ret;
	enum credentials_obtained obtained;
	
	if (cred->machine_account_pending) {
		cli_credentials_set_machine_account(cred, lp_ctx);
	}

	if (cred->ccache_obtained >= cred->ccache_threshold && 
	    cred->ccache_obtained > CRED_UNINITIALISED) {
		time_t lifetime;
		bool expired = false;
		ret = smb_krb5_cc_get_lifetime(cred->ccache->smb_krb5_context->krb5_context,
					       cred->ccache->ccache, &lifetime);
		if (ret == KRB5_CC_END) {
			/* If we have a particular ccache set, without
			 * an initial ticket, then assume there is a
			 * good reason */
		} else if (ret == 0) {
			if (lifetime == 0) {
				DEBUG(3, ("Ticket in credentials cache for %s expired, will refresh\n",
					  cli_credentials_get_principal(cred, cred)));
				expired = true;
			} else if (lifetime < 300) {
				DEBUG(3, ("Ticket in credentials cache for %s will shortly expire (%u secs), will refresh\n", 
					  cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
				expired = true;
			}
		} else {
			(*error_string) = talloc_asprintf(cred, "failed to get ccache lifetime: %s\n",
							  smb_get_krb5_error_message(cred->ccache->smb_krb5_context->krb5_context,
										     ret, cred));
			return ret;
		}

		DEBUG(5, ("Ticket in credentials cache for %s will expire in %u secs\n", 
			  cli_credentials_get_principal(cred, cred), (unsigned int)lifetime));
		
		if (!expired) {
			*ccc = cred->ccache;
			return 0;
		}
	}
	if (cli_credentials_is_anonymous(cred)) {
		(*error_string) = "Cannot get anonymous kerberos credentials";
		return EINVAL;
	}

	ret = cli_credentials_new_ccache(cred, lp_ctx, ccache_name, ccc, error_string);
	if (ret) {
		return ret;
	}

	ret = kinit_to_ccache(cred, cred, (*ccc)->smb_krb5_context, event_ctx, (*ccc)->ccache, &obtained, error_string);
	if (ret) {
		return ret;
	}

	ret = cli_credentials_set_from_ccache(cred, *ccc, 
					      obtained, error_string);
	
	cred->ccache = *ccc;
	cred->ccache_obtained = cred->principal_obtained;
	if (ret) {
		return ret;
	}
	cli_credentials_invalidate_client_gss_creds(cred, cred->ccache_obtained);
	return 0;
}
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;
}
Esempio n. 20
0
static krb5_error_code salt_principal_from_msg(TALLOC_CTX *parent_ctx, 
					       struct ldb_message *msg, 
					       struct smb_krb5_context *smb_krb5_context,
					       krb5_principal *salt_princ,
					       const char **error_string)
{
	const char *salt_principal = ldb_msg_find_attr_as_string(msg, "saltPrincipal", NULL);
	const char *samAccountName = ldb_msg_find_attr_as_string(msg, "samAccountName", NULL);
	const char *realm = ldb_msg_find_attr_as_string(msg, "realm", NULL);
	if (salt_principal) {
		return parse_principal(parent_ctx, salt_principal, smb_krb5_context, salt_princ, error_string);
	} else if (samAccountName) {
		krb5_error_code ret;
		char *machine_username;
		char *salt_body;
		char *lower_realm;
		char *upper_realm;

		TALLOC_CTX *tmp_ctx;
		struct principal_container *mem_ctx = talloc(parent_ctx, struct principal_container);
		if (!mem_ctx) {
			*error_string = "Cannot allocate mem_ctx";
			return ENOMEM;
		}

		tmp_ctx = talloc_new(mem_ctx);
		if (!tmp_ctx) {
			talloc_free(mem_ctx);
			*error_string = "Cannot allocate tmp_ctx";
			return ENOMEM;
		}

		if (!realm) {
			*error_string = "Cannot have a kerberos secret in secrets.ldb without a realm";
			return EINVAL;
		}
		
		machine_username = talloc_strdup(tmp_ctx, samAccountName);
		if (!machine_username) {
			talloc_free(mem_ctx);
			*error_string = "Cannot duplicate samAccountName";
			return ENOMEM;
		}
		
		if (machine_username[strlen(machine_username)-1] == '$') {
			machine_username[strlen(machine_username)-1] = '\0';
		}

		lower_realm = strlower_talloc(tmp_ctx, realm);
		if (!lower_realm) {
			talloc_free(mem_ctx);
			*error_string = "Cannot allocate to lower case realm";
			return ENOMEM;
		}
		
		upper_realm = strupper_talloc(tmp_ctx, realm);
		if (!upper_realm) {
			talloc_free(mem_ctx);
			*error_string = "Cannot allocate to upper case realm";
			return ENOMEM;
		}
		
		salt_body = talloc_asprintf(tmp_ctx, "%s.%s", machine_username, 
					    lower_realm);
		talloc_free(lower_realm);
		talloc_free(machine_username);
		if (!salt_body) {
			talloc_free(mem_ctx);
			*error_string = "Cannot form salt principal body";
			return ENOMEM;
		}
		
		ret = krb5_make_principal(smb_krb5_context->krb5_context, salt_princ, 
					  upper_realm,
					  "host", salt_body, NULL);
		if (ret == 0) {
			/* This song-and-dance effectivly puts the principal
			 * into talloc, so we can't loose it. */
			mem_ctx->smb_krb5_context = talloc_reference(mem_ctx, smb_krb5_context);
			mem_ctx->principal = *salt_princ;
			talloc_set_destructor(mem_ctx, free_principal);
		} else {
			(*error_string) = smb_get_krb5_error_message(smb_krb5_context->krb5_context, ret, parent_ctx);
		}
		talloc_free(tmp_ctx);
		return ret;
	} else {
Esempio n. 21
0
static NTSTATUS gensec_krb5_update(struct gensec_security *gensec_security, 
				   TALLOC_CTX *out_mem_ctx, 
				   const DATA_BLOB in, DATA_BLOB *out) 
{
	struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data;
	krb5_error_code ret = 0;
	NTSTATUS nt_status;

	switch (gensec_krb5_state->state_position) {
	case GENSEC_KRB5_CLIENT_START:
	{
		DATA_BLOB unwrapped_out;
		
		if (gensec_krb5_state->gssapi) {
			unwrapped_out = data_blob_talloc(out_mem_ctx, gensec_krb5_state->enc_ticket.data, gensec_krb5_state->enc_ticket.length);
			
			/* wrap that up in a nice GSS-API wrapping */
			*out = gensec_gssapi_gen_krb5_wrap(out_mem_ctx, &unwrapped_out, TOK_ID_KRB_AP_REQ);
		} else {
			*out = data_blob_talloc(out_mem_ctx, gensec_krb5_state->enc_ticket.data, gensec_krb5_state->enc_ticket.length);
		}
		if (gensec_krb5_state->ap_req_options & AP_OPTS_MUTUAL_REQUIRED) {
			gensec_krb5_state->state_position = GENSEC_KRB5_CLIENT_MUTUAL_AUTH;
			nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED;
		} else {
			gensec_krb5_state->state_position = GENSEC_KRB5_DONE;
			nt_status = NT_STATUS_OK;
		}
		return nt_status;
	}
		
	case GENSEC_KRB5_CLIENT_MUTUAL_AUTH:
	{
		DATA_BLOB unwrapped_in;
		krb5_data inbuf;
		krb5_ap_rep_enc_part *repl = NULL;
		uint8_t tok_id[2];

		if (gensec_krb5_state->gssapi) {
			if (!gensec_gssapi_parse_krb5_wrap(out_mem_ctx, &in, &unwrapped_in, tok_id)) {
				DEBUG(1,("gensec_gssapi_parse_krb5_wrap(mutual authentication) failed to parse\n"));
				dump_data_pw("Mutual authentication message:\n", in.data, in.length);
				return NT_STATUS_INVALID_PARAMETER;
			}
		} else {
			unwrapped_in = in;
		}
		/* TODO: check the tok_id */

		inbuf.data = unwrapped_in.data;
		inbuf.length = unwrapped_in.length;
		ret = krb5_rd_rep(gensec_krb5_state->smb_krb5_context->krb5_context, 
				  gensec_krb5_state->auth_context,
				  &inbuf, &repl);
		if (ret) {
			DEBUG(1,("krb5_rd_rep (mutual authentication) failed (%s)\n",
				 smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, out_mem_ctx)));
			dump_data_pw("Mutual authentication message:\n", (uint8_t *)inbuf.data, inbuf.length);
			nt_status = NT_STATUS_ACCESS_DENIED;
		} else {
			*out = data_blob(NULL, 0);
			nt_status = NT_STATUS_OK;
			gensec_krb5_state->state_position = GENSEC_KRB5_DONE;
		}
		if (repl) {
			krb5_free_ap_rep_enc_part(gensec_krb5_state->smb_krb5_context->krb5_context, repl);
		}
		return nt_status;
	}

	case GENSEC_KRB5_SERVER_START:
	{
		DATA_BLOB unwrapped_in;
		DATA_BLOB unwrapped_out = data_blob(NULL, 0);
		krb5_data inbuf, outbuf;
		uint8_t tok_id[2];
		struct keytab_container *keytab;
		krb5_principal server_in_keytab;
		const char *error_string;
		enum credentials_obtained obtained;

		if (!in.data) {
			return NT_STATUS_INVALID_PARAMETER;
		}	

		/* Grab the keytab, however generated */
		ret = cli_credentials_get_keytab(gensec_get_credentials(gensec_security), 
						 gensec_security->settings->lp_ctx, &keytab);
		if (ret) {
			return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
		}
		
		/* This ensures we lookup the correct entry in that keytab */
		ret = principal_from_credentials(out_mem_ctx, gensec_get_credentials(gensec_security), 
						 gensec_krb5_state->smb_krb5_context, 
						 &server_in_keytab, &obtained, &error_string);

		if (ret) {
			DEBUG(2,("Failed to make credentials from principal: %s\n", error_string));
			return NT_STATUS_CANT_ACCESS_DOMAIN_INFO;
		}

		/* Parse the GSSAPI wrapping, if it's there... (win2k3 allows it to be omited) */
		if (gensec_krb5_state->gssapi
		    && gensec_gssapi_parse_krb5_wrap(out_mem_ctx, &in, &unwrapped_in, tok_id)) {
			inbuf.data = unwrapped_in.data;
			inbuf.length = unwrapped_in.length;
		} else {
			inbuf.data = in.data;
			inbuf.length = in.length;
		}

		ret = smb_rd_req_return_stuff(gensec_krb5_state->smb_krb5_context->krb5_context,
					      &gensec_krb5_state->auth_context, 
					      &inbuf, keytab->keytab, server_in_keytab,  
					      &outbuf, 
					      &gensec_krb5_state->ticket, 
					      &gensec_krb5_state->keyblock);

		if (ret) {
			return NT_STATUS_LOGON_FAILURE;
		}
		unwrapped_out.data = (uint8_t *)outbuf.data;
		unwrapped_out.length = outbuf.length;
		gensec_krb5_state->state_position = GENSEC_KRB5_DONE;
		/* wrap that up in a nice GSS-API wrapping */
		if (gensec_krb5_state->gssapi) {
			*out = gensec_gssapi_gen_krb5_wrap(out_mem_ctx, &unwrapped_out, TOK_ID_KRB_AP_REP);
		} else {
			*out = data_blob_talloc(out_mem_ctx, outbuf.data, outbuf.length);
		}
		krb5_data_free(&outbuf);
		return NT_STATUS_OK;
	}

	case GENSEC_KRB5_DONE:
	default:
		/* Asking too many times... */
		return NT_STATUS_INVALID_PARAMETER;
	}
}
Esempio n. 22
0
static krb5_error_code principals_from_msg(TALLOC_CTX *parent_ctx,
					   struct ldb_message *msg,
					   struct smb_krb5_context *smb_krb5_context,
					   struct principal_container ***principals_out,
					   const char **error_string)
{
	unsigned int i;
	krb5_error_code ret;
	char *upper_realm;
	const char *realm = ldb_msg_find_attr_as_string(msg, "realm", NULL);
	const char *samAccountName = ldb_msg_find_attr_as_string(msg, "samAccountName", NULL);
	struct ldb_message_element *spn_el = ldb_msg_find_element(msg, "servicePrincipalName");
	TALLOC_CTX *tmp_ctx;
	struct principal_container **principals;
	tmp_ctx = talloc_new(parent_ctx);
	if (!tmp_ctx) {
		*error_string = "Cannot allocate tmp_ctx";
		return ENOMEM;
	}

	if (!realm) {
		*error_string = "Cannot have a kerberos secret in secrets.ldb without a realm";
		return EINVAL;
	}

	upper_realm = strupper_talloc(tmp_ctx, realm);
	if (!upper_realm) {
		talloc_free(tmp_ctx);
		*error_string = "Cannot allocate full upper case realm";
		return ENOMEM;
	}

	principals = talloc_array(tmp_ctx, struct principal_container *, spn_el ? (spn_el->num_values + 2) : 2);

	spn_el = ldb_msg_find_element(msg, "servicePrincipalName");
	for (i=0; spn_el && i < spn_el->num_values; i++) {
		principals[i] = talloc(principals, struct principal_container);
		if (!principals[i]) {
			talloc_free(tmp_ctx);
			*error_string = "Cannot allocate mem_ctx";
			return ENOMEM;
		}

		principals[i]->smb_krb5_context = talloc_reference(principals[i], smb_krb5_context);
		principals[i]->string_form = talloc_asprintf(principals[i], "%*.*s@%s",
							     (int)spn_el->values[i].length,
							     (int)spn_el->values[i].length,
							     (const char *)spn_el->values[i].data, upper_realm);
		if (!principals[i]->string_form) {
			talloc_free(tmp_ctx);
			*error_string = "Cannot allocate full samAccountName";
			return ENOMEM;
		}

		ret = krb5_parse_name(smb_krb5_context->krb5_context,
				      principals[i]->string_form, &principals[i]->principal);
		
		if (ret) {
			talloc_free(tmp_ctx);
			(*error_string) = smb_get_krb5_error_message(smb_krb5_context->krb5_context, ret, parent_ctx);
			return ret;
		}

		/* This song-and-dance effectivly puts the principal
		 * into talloc, so we can't loose it. */
		talloc_set_destructor(principals[i], free_principal);
	}

	if (samAccountName) {
		principals[i] = talloc(principals, struct principal_container);
		if (!principals[i]) {
			talloc_free(tmp_ctx);
			*error_string = "Cannot allocate mem_ctx";
			return ENOMEM;
		}

		principals[i]->smb_krb5_context = talloc_reference(principals[i], smb_krb5_context);
		principals[i]->string_form = talloc_asprintf(parent_ctx, "%s@%s", samAccountName, upper_realm);
		if (!principals[i]->string_form) {
			talloc_free(tmp_ctx);
			*error_string = "Cannot allocate full samAccountName";
			return ENOMEM;
		}
		
		ret = krb5_make_principal(smb_krb5_context->krb5_context, &principals[i]->principal, upper_realm, samAccountName,
					  NULL);
		if (ret) {
			talloc_free(tmp_ctx);
			(*error_string) = smb_get_krb5_error_message(smb_krb5_context->krb5_context, ret, parent_ctx);
			return ret;
		}
		
		/* This song-and-dance effectivly puts the principal
		 * into talloc, so we can't loose it. */
		talloc_set_destructor(principals[i], free_principal);
		i++;
	}

	principals[i] = NULL;
	*principals_out = talloc_steal(parent_ctx, principals);

	talloc_free(tmp_ctx);
	return ret;
}
Esempio n. 23
0
static NTSTATUS gensec_krb5_start(struct gensec_security *gensec_security, bool gssapi)
{
	krb5_error_code ret;
	struct gensec_krb5_state *gensec_krb5_state;
	struct cli_credentials *creds;
	const struct tsocket_address *tlocal_addr, *tremote_addr;
	krb5_address my_krb5_addr, peer_krb5_addr;
	
	creds = gensec_get_credentials(gensec_security);
	if (!creds) {
		return NT_STATUS_INVALID_PARAMETER;
	}

	gensec_krb5_state = talloc(gensec_security, struct gensec_krb5_state);
	if (!gensec_krb5_state) {
		return NT_STATUS_NO_MEMORY;
	}

	gensec_security->private_data = gensec_krb5_state;
	gensec_krb5_state->smb_krb5_context = NULL;
	gensec_krb5_state->auth_context = NULL;
	gensec_krb5_state->ticket = NULL;
	ZERO_STRUCT(gensec_krb5_state->enc_ticket);
	gensec_krb5_state->keyblock = NULL;
	gensec_krb5_state->gssapi = gssapi;

	talloc_set_destructor(gensec_krb5_state, gensec_krb5_destroy); 

	if (cli_credentials_get_krb5_context(creds, 
					     gensec_security->settings->lp_ctx, &gensec_krb5_state->smb_krb5_context)) {
		talloc_free(gensec_krb5_state);
		return NT_STATUS_INTERNAL_ERROR;
	}

	ret = krb5_auth_con_init(gensec_krb5_state->smb_krb5_context->krb5_context, &gensec_krb5_state->auth_context);
	if (ret) {
		DEBUG(1,("gensec_krb5_start: krb5_auth_con_init failed (%s)\n", 
			 smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, 
						    ret, gensec_krb5_state)));
		talloc_free(gensec_krb5_state);
		return NT_STATUS_INTERNAL_ERROR;
	}

	ret = krb5_auth_con_setflags(gensec_krb5_state->smb_krb5_context->krb5_context, 
				     gensec_krb5_state->auth_context,
				     KRB5_AUTH_CONTEXT_DO_SEQUENCE);
	if (ret) {
		DEBUG(1,("gensec_krb5_start: krb5_auth_con_setflags failed (%s)\n", 
			 smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, 
						    ret, gensec_krb5_state)));
		talloc_free(gensec_krb5_state);
		return NT_STATUS_INTERNAL_ERROR;
	}

	tlocal_addr = gensec_get_local_address(gensec_security);
	if (tlocal_addr) {
		ssize_t socklen;
		struct sockaddr_storage ss;

		socklen = tsocket_address_bsd_sockaddr(tlocal_addr,
				(struct sockaddr *) &ss,
				sizeof(struct sockaddr_storage));
		if (socklen < 0) {
			talloc_free(gensec_krb5_state);
			return NT_STATUS_INTERNAL_ERROR;
		}
		ret = krb5_sockaddr2address(gensec_krb5_state->smb_krb5_context->krb5_context,
				(const struct sockaddr *) &ss, &my_krb5_addr);
		if (ret) {
			DEBUG(1,("gensec_krb5_start: krb5_sockaddr2address (local) failed (%s)\n", 
				 smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, 
							    ret, gensec_krb5_state)));
			talloc_free(gensec_krb5_state);
			return NT_STATUS_INTERNAL_ERROR;
		}
	}

	tremote_addr = gensec_get_remote_address(gensec_security);
	if (tremote_addr) {
		ssize_t socklen;
		struct sockaddr_storage ss;

		socklen = tsocket_address_bsd_sockaddr(tremote_addr,
				(struct sockaddr *) &ss,
				sizeof(struct sockaddr_storage));
		if (socklen < 0) {
			talloc_free(gensec_krb5_state);
			return NT_STATUS_INTERNAL_ERROR;
		}
		ret = krb5_sockaddr2address(gensec_krb5_state->smb_krb5_context->krb5_context,
				(const struct sockaddr *) &ss, &peer_krb5_addr);
		if (ret) {
			DEBUG(1,("gensec_krb5_start: krb5_sockaddr2address (local) failed (%s)\n", 
				 smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, 
							    ret, gensec_krb5_state)));
			talloc_free(gensec_krb5_state);
			return NT_STATUS_INTERNAL_ERROR;
		}
	}

	ret = krb5_auth_con_setaddrs(gensec_krb5_state->smb_krb5_context->krb5_context, 
				     gensec_krb5_state->auth_context,
				     tlocal_addr ? &my_krb5_addr : NULL,
				     tremote_addr ? &peer_krb5_addr : NULL);
	if (ret) {
		DEBUG(1,("gensec_krb5_start: krb5_auth_con_setaddrs failed (%s)\n", 
			 smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, 
						    ret, gensec_krb5_state)));
		talloc_free(gensec_krb5_state);
		return NT_STATUS_INTERNAL_ERROR;
	}

	return NT_STATUS_OK;
}
Esempio n. 24
0
static krb5_error_code salt_principal(TALLOC_CTX *parent_ctx,
                                      const char *samAccountName,
                                      const char *realm,
                                      const char *saltPrincipal,
                                      krb5_context context,
                                      krb5_principal *salt_princ,
                                      const char **error_string)
{

    krb5_error_code ret;
    char *machine_username;
    char *salt_body;
    char *lower_realm;
    char *upper_realm;

    TALLOC_CTX *tmp_ctx;

    if (saltPrincipal) {
        ret = krb5_parse_name(context, saltPrincipal, salt_princ);
        if (ret) {
            *error_string = smb_get_krb5_error_message(
                                context, ret, parent_ctx);
        }
        return ret;
    }

    if (!samAccountName) {
        (*error_string) = "Cannot determine salt principal, no "
                          "saltPrincipal or samAccountName specified";
        return EINVAL;
    }

    if (!realm) {
        *error_string = "Cannot make principal without a realm";
        return EINVAL;
    }

    tmp_ctx = talloc_new(parent_ctx);
    if (!tmp_ctx) {
        *error_string = "Cannot allocate tmp_ctx";
        return ENOMEM;
    }

    machine_username = talloc_strdup(tmp_ctx, samAccountName);
    if (!machine_username) {
        *error_string = "Cannot duplicate samAccountName";
        talloc_free(tmp_ctx);
        return ENOMEM;
    }

    if (machine_username[strlen(machine_username)-1] == '$') {
        machine_username[strlen(machine_username)-1] = '\0';
    }

    lower_realm = strlower_talloc(tmp_ctx, realm);
    if (!lower_realm) {
        *error_string = "Cannot allocate to lower case realm";
        talloc_free(tmp_ctx);
        return ENOMEM;
    }

    upper_realm = strupper_talloc(tmp_ctx, realm);
    if (!upper_realm) {
        *error_string = "Cannot allocate to upper case realm";
        talloc_free(tmp_ctx);
        return ENOMEM;
    }

    salt_body = talloc_asprintf(tmp_ctx, "%s.%s",
                                machine_username, lower_realm);
    if (!salt_body) {
        *error_string = "Cannot form salt principal body";
        talloc_free(tmp_ctx);
        return ENOMEM;
    }

    ret = smb_krb5_make_principal(context, salt_princ, upper_realm,
                                  "host", salt_body, NULL);
    if (ret) {
        *error_string = smb_get_krb5_error_message(context,
                        ret, parent_ctx);
    }

    talloc_free(tmp_ctx);
    return ret;
}
krb5_error_code smb_krb5_init_context(void *parent_ctx,
				      struct loadparm_context *lp_ctx,
				      struct smb_krb5_context **smb_krb5_context)
{
	krb5_error_code ret;
	TALLOC_CTX *tmp_ctx;
	krb5_context kctx;
#ifdef SAMBA4_USES_HEIMDAL
	krb5_log_facility *logf;
#endif

	initialize_krb5_error_table();

	tmp_ctx = talloc_new(parent_ctx);
	*smb_krb5_context = talloc_zero(tmp_ctx, struct smb_krb5_context);

	if (!*smb_krb5_context || !tmp_ctx) {
		talloc_free(tmp_ctx);
		return ENOMEM;
	}

	ret = smb_krb5_init_context_basic(tmp_ctx, lp_ctx, &kctx);
	if (ret) {
		DEBUG(1,("smb_krb5_context_init_basic failed (%s)\n",
			 error_message(ret)));
		talloc_free(tmp_ctx);
		return ret;
	}
	(*smb_krb5_context)->krb5_context = kctx;

	talloc_set_destructor(*smb_krb5_context, smb_krb5_context_destroy);

#ifdef SAMBA4_USES_HEIMDAL
	/* TODO: Should we have a different name here? */
	ret = krb5_initlog(kctx, "Samba", &logf);

	if (ret) {
		DEBUG(1,("krb5_initlog failed (%s)\n",
			 smb_get_krb5_error_message(kctx, ret, tmp_ctx)));
		talloc_free(tmp_ctx);
		return ret;
	}
	(*smb_krb5_context)->pvt_log_data = logf;

	ret = krb5_addlog_func(kctx, logf, 0 /* min */, -1 /* max */,
			       smb_krb5_debug_wrapper,
				smb_krb5_debug_close, NULL);
	if (ret) {
		DEBUG(1,("krb5_addlog_func failed (%s)\n",
			 smb_get_krb5_error_message(kctx, ret, tmp_ctx)));
		talloc_free(tmp_ctx);
		return ret;
	}
	krb5_set_warn_dest(kctx, logf);

	/* Set options in kerberos */

	krb5_set_dns_canonicalize_hostname(kctx,
			lpcfg_parm_bool(lp_ctx, NULL, "krb5",
					"set_dns_canonicalize", false));
#endif
	talloc_steal(parent_ctx, *smb_krb5_context);
	talloc_free(tmp_ctx);

	return 0;
}
Esempio n. 26
0
static krb5_error_code keytab_add_keys(TALLOC_CTX *parent_ctx,
                                       krb5_principal *principals,
                                       krb5_principal salt_princ,
                                       int kvno,
                                       const char *password_s,
                                       krb5_context context,
                                       krb5_enctype *enctypes,
                                       krb5_keytab keytab,
                                       const char **error_string)
{
    unsigned int i, p;
    krb5_error_code ret;
    krb5_data password;
    char *unparsed;

    password.data = discard_const_p(char, password_s);
    password.length = strlen(password_s);

    for (i = 0; enctypes[i]; i++) {
        krb5_keytab_entry entry;

        ZERO_STRUCT(entry);

        ret = smb_krb5_create_key_from_string(context,
                                              &salt_princ,
                                              NULL,
                                              &password,
                                              enctypes[i],
                                              KRB5_KT_KEY(&entry));
        if (ret != 0) {
            return ret;
        }

        entry.vno = kvno;

        for (p = 0; principals[p]; p++) {
            unparsed = NULL;
            entry.principal = principals[p];
            ret = krb5_kt_add_entry(context, keytab, &entry);
            if (ret != 0) {
                char *k5_error_string =
                    smb_get_krb5_error_message(context,
                                               ret, NULL);
                krb5_unparse_name(context,
                                  principals[p], &unparsed);
                *error_string = talloc_asprintf(parent_ctx,
                                                "Failed to add enctype %d entry for "
                                                "%s(kvno %d) to keytab: %s\n",
                                                (int)enctypes[i], unparsed,
                                                kvno, k5_error_string);

                free(unparsed);
                talloc_free(k5_error_string);
                krb5_free_keyblock_contents(context,
                                            KRB5_KT_KEY(&entry));
                return ret;
            }

            DEBUG(5, ("Added key (kvno %d) to keytab (enctype %d)\n",
                      kvno, (int)enctypes[i]));
        }
        krb5_free_keyblock_contents(context, KRB5_KT_KEY(&entry));
    }
    return 0;
}
Esempio n. 27
0
 krb5_error_code kerberos_encode_pac(TALLOC_CTX *mem_ctx,
				    struct PAC_DATA *pac_data,
				    krb5_context context,
				    const krb5_keyblock *krbtgt_keyblock,
				    const krb5_keyblock *service_keyblock,
				    DATA_BLOB *pac)
{
	NTSTATUS nt_status;
	krb5_error_code ret;
	enum ndr_err_code ndr_err;
	DATA_BLOB zero_blob = data_blob(NULL, 0);
	DATA_BLOB tmp_blob = data_blob(NULL, 0);
	struct PAC_SIGNATURE_DATA *kdc_checksum = NULL;
	struct PAC_SIGNATURE_DATA *srv_checksum = NULL;
	int i;

	/* First, just get the keytypes filled in (and lengths right, eventually) */
	for (i=0; i < pac_data->num_buffers; i++) {
		if (pac_data->buffers[i].type != PAC_TYPE_KDC_CHECKSUM) {
			continue;
		}
		kdc_checksum = &pac_data->buffers[i].info->kdc_cksum,
		ret = smb_krb5_make_pac_checksum(mem_ctx,
						 &zero_blob,
						 context,
						 krbtgt_keyblock,
						 &kdc_checksum->type,
						 &kdc_checksum->signature);
		if (ret) {
			DEBUG(2, ("making krbtgt PAC checksum failed: %s\n",
				  smb_get_krb5_error_message(context, ret, mem_ctx)));
			talloc_free(pac_data);
			return ret;
		}
	}

	for (i=0; i < pac_data->num_buffers; i++) {
		if (pac_data->buffers[i].type != PAC_TYPE_SRV_CHECKSUM) {
			continue;
		}
		srv_checksum = &pac_data->buffers[i].info->srv_cksum;
		ret = smb_krb5_make_pac_checksum(mem_ctx,
						 &zero_blob,
						 context,
						 service_keyblock,
						 &srv_checksum->type,
						 &srv_checksum->signature);
		if (ret) {
			DEBUG(2, ("making service PAC checksum failed: %s\n",
				  smb_get_krb5_error_message(context, ret, mem_ctx)));
			talloc_free(pac_data);
			return ret;
		}
	}

	if (!kdc_checksum) {
		DEBUG(2, ("Invalid PAC constructed for signing, no KDC checksum present!"));
		return EINVAL;
	}
	if (!srv_checksum) {
		DEBUG(2, ("Invalid PAC constructed for signing, no SRV checksum present!"));
		return EINVAL;
	}

	/* But wipe out the actual signatures */
	memset(kdc_checksum->signature.data, '\0', kdc_checksum->signature.length);
	memset(srv_checksum->signature.data, '\0', srv_checksum->signature.length);

	ndr_err = ndr_push_struct_blob(&tmp_blob, mem_ctx,
				       pac_data,
				       (ndr_push_flags_fn_t)ndr_push_PAC_DATA);
	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
		nt_status = ndr_map_error2ntstatus(ndr_err);
		DEBUG(1, ("PAC (presig) push failed: %s\n", nt_errstr(nt_status)));
		talloc_free(pac_data);
		return EINVAL;
	}

	/* Then sign the result of the previous push, where the sig was zero'ed out */
	ret = smb_krb5_make_pac_checksum(mem_ctx,
					 &tmp_blob,
					 context,
					 service_keyblock,
					 &srv_checksum->type,
					 &srv_checksum->signature);

	/* Then sign Server checksum */
	ret = smb_krb5_make_pac_checksum(mem_ctx,
					 &srv_checksum->signature,
					 context,
					 krbtgt_keyblock,
					 &kdc_checksum->type,
					 &kdc_checksum->signature);
	if (ret) {
		DEBUG(2, ("making krbtgt PAC checksum failed: %s\n",
			  smb_get_krb5_error_message(context, ret, mem_ctx)));
		talloc_free(pac_data);
		return ret;
	}

	/* And push it out again, this time to the world.  This relies on determanistic pointer values */
	ndr_err = ndr_push_struct_blob(&tmp_blob, mem_ctx,
				       pac_data,
				       (ndr_push_flags_fn_t)ndr_push_PAC_DATA);
	if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
		nt_status = ndr_map_error2ntstatus(ndr_err);
		DEBUG(1, ("PAC (final) push failed: %s\n", nt_errstr(nt_status)));
		talloc_free(pac_data);
		return EINVAL;
	}

	*pac = tmp_blob;

	return ret;
}
Esempio n. 28
0
static krb5_error_code create_keytab(TALLOC_CTX *parent_ctx,
                                     const char *samAccountName,
                                     const char *realm,
                                     const char *saltPrincipal,
                                     int kvno,
                                     const char *new_secret,
                                     const char *old_secret,
                                     uint32_t supp_enctypes,
                                     krb5_principal *principals,
                                     krb5_context context,
                                     krb5_keytab keytab,
                                     bool add_old,
                                     const char **error_string)
{
    krb5_error_code ret;
    krb5_principal salt_princ = NULL;
    krb5_enctype *enctypes;
    TALLOC_CTX *mem_ctx;

    if (!new_secret) {
        /* There is no password here, so nothing to do */
        return 0;
    }

    mem_ctx = talloc_new(parent_ctx);
    if (!mem_ctx) {
        *error_string = talloc_strdup(parent_ctx,
                                      "unable to allocate tmp_ctx for create_keytab");
        return ENOMEM;
    }

    /* The salt used to generate these entries may be different however,
     * fetch that */
    ret = salt_principal(mem_ctx, samAccountName, realm, saltPrincipal,
                         context, &salt_princ, error_string);
    if (ret) {
        talloc_free(mem_ctx);
        return ret;
    }

    ret = ms_suptypes_to_ietf_enctypes(mem_ctx, supp_enctypes, &enctypes);
    if (ret) {
        *error_string = talloc_asprintf(parent_ctx,
                                        "create_keytab: generating list of "
                                        "encryption types failed (%s)\n",
                                        smb_get_krb5_error_message(context,
                                                ret, mem_ctx));
        goto done;
    }

    ret = keytab_add_keys(mem_ctx, principals,
                          salt_princ, kvno, new_secret,
                          context, enctypes, keytab, error_string);
    if (ret) {
        talloc_steal(parent_ctx, *error_string);
        goto done;
    }

    if (old_secret && add_old && kvno != 0) {
        ret = keytab_add_keys(mem_ctx, principals,
                              salt_princ, kvno - 1, old_secret,
                              context, enctypes, keytab, error_string);
        if (ret) {
            talloc_steal(parent_ctx, *error_string);
        }
    }

done:
    krb5_free_principal(context, salt_princ);
    talloc_free(mem_ctx);
    return ret;
}
Esempio n. 29
0
/**
* @brief Decode a blob containing a NDR envoded PAC structure
*
* @param mem_ctx	  - The memory context
* @param pac_data_blob	  - The data blob containing the NDR encoded data
* @param context	  - The Kerberos Context
* @param service_keyblock - The Service Key used to verify the checksum
* @param client_principal - The client principal
* @param tgs_authtime     - The ticket timestamp
* @param pac_data_out	  - [out] The decoded PAC
*
* @return - A NTSTATUS error code
*/
NTSTATUS kerberos_decode_pac(TALLOC_CTX *mem_ctx,
			     DATA_BLOB pac_data_blob,
			     krb5_context context,
			     const krb5_keyblock *krbtgt_keyblock,
			     const krb5_keyblock *service_keyblock,
			     krb5_const_principal client_principal,
			     time_t tgs_authtime,
			     struct PAC_DATA **pac_data_out)
{
	NTSTATUS status;
	enum ndr_err_code ndr_err;
	krb5_error_code ret;
	DATA_BLOB modified_pac_blob;

	NTTIME tgs_authtime_nttime;
	krb5_principal client_principal_pac = NULL;
	int i;

	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_NAME *logon_name = NULL;
	struct PAC_LOGON_INFO *logon_info = NULL;
	struct PAC_DATA *pac_data = NULL;
	struct PAC_DATA_RAW *pac_data_raw = NULL;

	DATA_BLOB *srv_sig_blob = NULL;
	DATA_BLOB *kdc_sig_blob = NULL;

	bool bool_ret;

	*pac_data_out = NULL;

	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) {
		return NT_STATUS_NO_MEMORY;
	}

	ndr_err = ndr_pull_struct_blob(&pac_data_blob, pac_data, 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(
				&pac_data_blob, pac_data_raw, 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++) {
		struct PAC_BUFFER *data_buf = &pac_data->buffers[i];
		struct PAC_BUFFER_RAW *raw_buf = &pac_data_raw->buffers[i];

		if (data_buf->type != raw_buf->type) {
			DEBUG(0, ("misparse! PAC_DATA buffer %d has type "
				  "%d while PAC_DATA_RAW has %d\n", i,
				  data_buf->type, raw_buf->type));
			return NT_STATUS_INVALID_PARAMETER;
		}
		switch (data_buf->type) {
		case PAC_TYPE_LOGON_INFO:
			if (!data_buf->info) {
				break;
			}
			logon_info = data_buf->info->logon_info.info;
			break;
		case PAC_TYPE_SRV_CHECKSUM:
			if (!data_buf->info) {
				break;
			}
			srv_sig_ptr = &data_buf->info->srv_cksum;
			srv_sig_blob = &raw_buf->info->remaining;
			break;
		case PAC_TYPE_KDC_CHECKSUM:
			if (!data_buf->info) {
				break;
			}
			kdc_sig_ptr = &data_buf->info->kdc_cksum;
			kdc_sig_blob = &raw_buf->info->remaining;
			break;
		case PAC_TYPE_LOGON_NAME:
			logon_name = &data_buf->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, 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, 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, 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, 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, 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;
	}

	if (service_keyblock) {
		/* 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", error_message(ret)));
			return NT_STATUS_ACCESS_DENIED;
		}

		if (krbtgt_keyblock) {
			/* verify the service key checksum by krbtgt_key */
			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)));
				return NT_STATUS_ACCESS_DENIED;
			}
		}
	}

	if (tgs_authtime) {
		/* 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;
		}
	}

	if (client_principal) {
		ret = smb_krb5_parse_name_norealm(context,
						  logon_name->account_name,
						  &client_principal_pac);
		if (ret) {
			DEBUG(2, ("Could not parse name from PAC: [%s]:%s\n",
				  logon_name->account_name, error_message(ret)));
			return NT_STATUS_INVALID_PARAMETER;
		}

		bool_ret = smb_krb5_principal_compare_any_realm(context,
								client_principal,
								client_principal_pac);

		krb5_free_principal(context, client_principal_pac);

		if (!bool_ret) {
			DEBUG(2, ("Name in PAC [%s] does not match principal name "
				  "in ticket\n", logon_name->account_name));
			return NT_STATUS_ACCESS_DENIED;
		}
	}

	DEBUG(3,("Found account name from PAC: %s [%s]\n",
		 logon_info->info3.base.account_name.string,
		 logon_info->info3.base.full_name.string));

	DEBUG(10,("Successfully validated Kerberos PAC\n"));

	if (DEBUGLEVEL >= 10) {
		const char *s;
		s = NDR_PRINT_STRUCT_STRING(mem_ctx, PAC_DATA, pac_data);
		if (s) {
			DEBUGADD(10,("%s\n", s));
		}
	}

	*pac_data_out = pac_data;

	return NT_STATUS_OK;
}
Esempio n. 30
0
static krb5_error_code keytab_add_keys(TALLOC_CTX *parent_ctx,
				       const char *princ_string,
				       krb5_principal princ,
				       krb5_principal salt_princ,
				       int kvno,
				       const char *password_s,
				       struct smb_krb5_context *smb_krb5_context,
				       const char **enctype_strings,
				       krb5_keytab keytab)
{
	int i;
	krb5_error_code ret;
	krb5_data password;
	TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
	if (!mem_ctx) {
		return ENOMEM;
	}

	password.data = discard_const_p(char *, password_s);
	password.length = strlen(password_s);

	for (i=0; enctype_strings[i]; i++) {
		krb5_keytab_entry entry;
		krb5_enctype enctype;
		ret = krb5_string_to_enctype(smb_krb5_context->krb5_context, enctype_strings[i], &enctype);
		if (ret != 0) {
			DEBUG(1, ("Failed to interpret %s as a krb5 encryption type: %s\n",				  
				  enctype_strings[i],
				  smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
							     ret, mem_ctx)));
			talloc_free(mem_ctx);
			return ret;
		}
		ret = create_kerberos_key_from_string(smb_krb5_context->krb5_context, 
						      salt_princ, &password, &entry.keyblock, enctype);
		if (ret != 0) {
			talloc_free(mem_ctx);
			return ret;
		}

                entry.principal = princ;
                entry.vno       = kvno;
		ret = krb5_kt_add_entry(smb_krb5_context->krb5_context, keytab, &entry);
		if (ret != 0) {
			DEBUG(1, ("Failed to add %s entry for %s(kvno %d) to keytab: %s\n",
				  enctype_strings[i],
				  princ_string,
				  kvno,
				  smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
							     ret, mem_ctx)));
			talloc_free(mem_ctx);
			krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
			return ret;
		}

		DEBUG(5, ("Added %s(kvno %d) to keytab (%s)\n", 
			  princ_string, kvno,
			  enctype_strings[i]));
		
		krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &entry.keyblock);
	}
	talloc_free(mem_ctx);
	return 0;
}