Пример #1
0
int cli_credentials_get_ccache(struct cli_credentials *cred, 
			       struct ccache_container **ccc)
{
	krb5_error_code ret;
	
	if (cred->ccache_obtained >= (MAX(cred->principal_obtained, 
					  cred->username_obtained))) {
		*ccc = cred->ccache;
		return 0;
	}
	if (cli_credentials_is_anonymous(cred)) {
		return EINVAL;
	}

	ret = cli_credentials_new_ccache(cred, NULL);
	if (ret) {
		return ret;
	}
	ret = kinit_to_ccache(cred, cred, cred->ccache->smb_krb5_context, cred->ccache->ccache);
	if (ret) {
		return ret;
	}
	ret = cli_credentials_set_from_ccache(cred, cred->principal_obtained);

	if (ret) {
		return ret;
	}
	*ccc = cred->ccache;
	return ret;
}
Пример #2
0
 krb5_error_code kinit_to_ccache(TALLOC_CTX *parent_ctx,
				 struct cli_credentials *credentials,
				 struct smb_krb5_context *smb_krb5_context,
				 struct tevent_context *event_ctx,
				 krb5_ccache ccache,
				 enum credentials_obtained *obtained,
				 const char **error_string)
{
	krb5_error_code ret;
	const char *password;
#ifdef SAMBA4_USES_HEIMDAL
	const char *self_service;
#endif
	const char *target_service;
	time_t kdc_time = 0;
	krb5_principal princ;
	krb5_principal impersonate_principal;
	int tries;
	TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);
	krb5_get_init_creds_opt *krb_options;

	if (!mem_ctx) {
		(*error_string) = strerror(ENOMEM);
		return ENOMEM;
	}

	ret = principal_from_credentials(mem_ctx, credentials, smb_krb5_context, &princ, obtained, error_string);
	if (ret) {
		talloc_free(mem_ctx);
		return ret;
	}

	if (princ == NULL) {
		(*error_string) = talloc_asprintf(credentials, "principal, username or realm was not specified in the credentials");
		talloc_free(mem_ctx);
		return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
	}

	ret = impersonate_principal_from_credentials(mem_ctx, credentials, smb_krb5_context, &impersonate_principal, error_string);
	if (ret) {
		talloc_free(mem_ctx);
		return ret;
	}

#ifdef SAMBA4_USES_HEIMDAL
	self_service = cli_credentials_get_self_service(credentials);
#endif
	target_service = cli_credentials_get_target_service(credentials);

	password = cli_credentials_get_password(credentials);

	/* setup the krb5 options we want */
	if ((ret = krb5_get_init_creds_opt_alloc(smb_krb5_context->krb5_context, &krb_options))) {
		(*error_string) = talloc_asprintf(credentials, "krb5_get_init_creds_opt_alloc failed (%s)\n",
						  smb_get_krb5_error_message(smb_krb5_context->krb5_context,
									     ret, mem_ctx));
		talloc_free(mem_ctx);
		return ret;
	}

#ifdef SAMBA4_USES_HEIMDAL /* Disable for now MIT reads defaults when needed */
	/* get the defaults */
	krb5_get_init_creds_opt_set_default_flags(smb_krb5_context->krb5_context, NULL, NULL, krb_options);
#endif
	/* set if we want a forwardable ticket */
	switch (cli_credentials_get_krb_forwardable(credentials)) {
	case CRED_AUTO_KRB_FORWARDABLE:
		break;
	case CRED_NO_KRB_FORWARDABLE:
		krb5_get_init_creds_opt_set_forwardable(krb_options, FALSE);
		break;
	case CRED_FORCE_KRB_FORWARDABLE:
		krb5_get_init_creds_opt_set_forwardable(krb_options, TRUE);
		break;
	}

#ifdef SAMBA4_USES_HEIMDAL /* FIXME: MIT does not have this yet */
	/*
	 * In order to work against windows KDCs even if we use
	 * the netbios domain name as realm, we need to add the following
	 * flags:
	 * KRB5_INIT_CREDS_NO_C_CANON_CHECK;
	 * KRB5_INIT_CREDS_NO_C_NO_EKU_CHECK;
	 *
	 * On MIT: Set pkinit_eku_checking to none
	 */
	krb5_get_init_creds_opt_set_win2k(smb_krb5_context->krb5_context,
					  krb_options, true);
#else /* MIT */
	krb5_get_init_creds_opt_set_canonicalize(krb_options, true);
#endif

	tries = 2;
	while (tries--) {
#ifdef SAMBA4_USES_HEIMDAL
		struct tevent_context *previous_ev;
		/* Do this every time, in case we have weird recursive issues here */
		ret = smb_krb5_context_set_event_ctx(smb_krb5_context, event_ctx, &previous_ev);
		if (ret) {
			talloc_free(mem_ctx);
			return ret;
		}
#endif
		if (password) {
			if (impersonate_principal) {
#ifdef SAMBA4_USES_HEIMDAL
				ret = kerberos_kinit_s4u2_cc(
						smb_krb5_context->krb5_context,
						ccache, princ, password,
						impersonate_principal,
						self_service, target_service,
						krb_options, NULL, &kdc_time);
#else
				talloc_free(mem_ctx);
				(*error_string) = "INTERNAL error: s4u2 ops "
					"are not supported with MIT build yet";
				return EINVAL;
#endif
			} else {
				ret = kerberos_kinit_password_cc(
						smb_krb5_context->krb5_context,
						ccache, princ, password,
						target_service,
						krb_options, NULL, &kdc_time);
			}
		} else if (impersonate_principal) {
			talloc_free(mem_ctx);
			(*error_string) = "INTERNAL error: Cannot impersonate principal with just a keyblock.  A password must be specified in the credentials";
			return EINVAL;
		} else {
			/* No password available, try to use a keyblock instead */
			
			krb5_keyblock keyblock;
			const struct samr_Password *mach_pwd;
			mach_pwd = cli_credentials_get_nt_hash(credentials, mem_ctx);
			if (!mach_pwd) {
				talloc_free(mem_ctx);
				(*error_string) = "kinit_to_ccache: No password available for kinit\n";
				krb5_get_init_creds_opt_free(smb_krb5_context->krb5_context, krb_options);
#ifdef SAMBA4_USES_HEIMDAL
				smb_krb5_context_remove_event_ctx(smb_krb5_context, previous_ev, event_ctx);
#endif
				return EINVAL;
			}
			ret = smb_krb5_keyblock_init_contents(smb_krb5_context->krb5_context,
						 ENCTYPE_ARCFOUR_HMAC,
						 mach_pwd->hash, sizeof(mach_pwd->hash), 
						 &keyblock);
			
			if (ret == 0) {
				ret = kerberos_kinit_keyblock_cc(smb_krb5_context->krb5_context, ccache, 
								 princ, &keyblock,
								 target_service, krb_options,
								 NULL, &kdc_time);
				krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &keyblock);
			}
		}

#ifdef SAMBA4_USES_HEIMDAL
		smb_krb5_context_remove_event_ctx(smb_krb5_context, previous_ev, event_ctx);
#endif

		if (ret == KRB5KRB_AP_ERR_SKEW || ret == KRB5_KDCREP_SKEW) {
			/* Perhaps we have been given an invalid skew, so try again without it */
			time_t t = time(NULL);
			krb5_set_real_time(smb_krb5_context->krb5_context, t, 0);
		} else {
			/* not a skew problem */
			break;
		}
	}

	krb5_get_init_creds_opt_free(smb_krb5_context->krb5_context, krb_options);

	if (ret == KRB5KRB_AP_ERR_SKEW || ret == KRB5_KDCREP_SKEW) {
		(*error_string) = talloc_asprintf(credentials, "kinit for %s failed (%s)\n",
						  cli_credentials_get_principal(credentials, mem_ctx),
						  smb_get_krb5_error_message(smb_krb5_context->krb5_context,
									     ret, mem_ctx));
		talloc_free(mem_ctx);
		return ret;
	}

	/* cope with ticket being in the future due to clock skew */
	if ((unsigned)kdc_time > time(NULL)) {
		time_t t = time(NULL);
		int time_offset =(unsigned)kdc_time-t;
		DEBUG(4,("Advancing clock by %d seconds to cope with clock skew\n", time_offset));
		krb5_set_real_time(smb_krb5_context->krb5_context, t + time_offset + 1, 0);
	}
	
	if (ret == KRB5KDC_ERR_PREAUTH_FAILED && cli_credentials_wrong_password(credentials)) {
		ret = kinit_to_ccache(parent_ctx,
				      credentials,
				      smb_krb5_context,
				      event_ctx,
				      ccache, obtained,
				      error_string);
	}

	if (ret) {
		(*error_string) = talloc_asprintf(credentials, "kinit for %s failed (%s)\n",
						  cli_credentials_get_principal(credentials, mem_ctx),
						  smb_get_krb5_error_message(smb_krb5_context->krb5_context,
									     ret, mem_ctx));
		talloc_free(mem_ctx);
		return ret;
	}

	DEBUG(10,("kinit for %s succeeded\n",
		cli_credentials_get_principal(credentials, mem_ctx)));


	talloc_free(mem_ctx);
	return 0;
}
Пример #3
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;
}
Пример #4
0
 krb5_error_code kinit_to_ccache(TALLOC_CTX *parent_ctx,
				 struct cli_credentials *credentials,
				 struct smb_krb5_context *smb_krb5_context,
				 krb5_ccache ccache) 
{
	krb5_error_code ret;
	const char *password;
	time_t kdc_time = 0;
	krb5_principal princ;
	int tries;
	TALLOC_CTX *mem_ctx = talloc_new(parent_ctx);

	if (!mem_ctx) {
		return ENOMEM;
	}

	ret = principal_from_credentials(mem_ctx, credentials, smb_krb5_context, &princ);
	if (ret) {
		talloc_free(mem_ctx);
		return ret;
	}

	password = cli_credentials_get_password(credentials);

	tries = 2;
	while (tries--) {
		if (password) {
			ret = kerberos_kinit_password_cc(smb_krb5_context->krb5_context, ccache, 
							 princ, 
							 password, NULL, &kdc_time);
		} else {
			/* No password available, try to use a keyblock instead */
			
			krb5_keyblock keyblock;
			const struct samr_Password *mach_pwd;
			mach_pwd = cli_credentials_get_nt_hash(credentials, mem_ctx);
			if (!mach_pwd) {
				talloc_free(mem_ctx);
				DEBUG(1, ("kinit_to_ccache: No password available for kinit\n"));
				return EINVAL;
			}
			ret = krb5_keyblock_init(smb_krb5_context->krb5_context,
						 ENCTYPE_ARCFOUR_HMAC,
						 mach_pwd->hash, sizeof(mach_pwd->hash), 
						 &keyblock);
			
			if (ret == 0) {
				ret = kerberos_kinit_keyblock_cc(smb_krb5_context->krb5_context, ccache, 
								 princ,
								 &keyblock, NULL, &kdc_time);
				krb5_free_keyblock_contents(smb_krb5_context->krb5_context, &keyblock);
			}
		}

		if (ret == KRB5KRB_AP_ERR_SKEW || ret == KRB5_KDCREP_SKEW) {
			/* Perhaps we have been given an invalid skew, so try again without it */
			time_t t = time(NULL);
			krb5_set_real_time(smb_krb5_context->krb5_context, t, 0);
		} else {
			/* not a skew problem */
			break;
		}
	}

	if (ret == KRB5KRB_AP_ERR_SKEW || ret == KRB5_KDCREP_SKEW) {
		DEBUG(1,("kinit for %s failed (%s)\n", 
			 cli_credentials_get_principal(credentials, mem_ctx), 
			 smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
						    ret, mem_ctx)));
		talloc_free(mem_ctx);
		return ret;
	}

	/* cope with ticket being in the future due to clock skew */
	if ((unsigned)kdc_time > time(NULL)) {
		time_t t = time(NULL);
		int time_offset =(unsigned)kdc_time-t;
		DEBUG(4,("Advancing clock by %d seconds to cope with clock skew\n", time_offset));
		krb5_set_real_time(smb_krb5_context->krb5_context, t + time_offset + 1, 0);
	}
	
	if (ret == KRB5KDC_ERR_PREAUTH_FAILED && cli_credentials_wrong_password(credentials)) {
		ret = kinit_to_ccache(parent_ctx,
				      credentials,
				      smb_krb5_context,
				      ccache); 
	}
	if (ret) {
		DEBUG(1,("kinit for %s failed (%s)\n", 
			 cli_credentials_get_principal(credentials, mem_ctx), 
			 smb_get_krb5_error_message(smb_krb5_context->krb5_context, 
						    ret, mem_ctx)));
		talloc_free(mem_ctx);
		return ret;
	} 
	talloc_free(mem_ctx);
	return 0;
}