Example #1
0
kadm5_ret_t
schpw_util_wrapper(void *server_handle, krb5_principal princ,
		   char *new_pw, char **ret_pw,
		   char *msg_ret, unsigned int msg_len)
{
    kadm5_ret_t ret;

    ret = check_min_life(server_handle, princ, msg_ret, msg_len);
    if (ret)
	return ret;

    return kadm5_chpass_principal_util(server_handle, princ,
				       new_pw, ret_pw,
				       msg_ret, msg_len);
}
Example #2
0
/*
 * Function: krb5_changepw
 *
 * Purpose: Initialize and call lower level routines to change a password
 *
 * Arguments:
 *
 *	princ_str	principal name to use, optional
 *	old_password 	old password
 *	new_password  	new password
 *
 * Returns:
 *                      exit status of PAM_SUCCESS for success
 *			1 principal unknown
 *			2 old password wrong
 *			3 cannot initialize admin server session
 *			4 new passwd mismatch or error trying to change pw
 *                      5 password not typed
 *                      6 misc error
 *                      7 incorrect usage
 *
 * Requires:
 *	Passwords cannot be more than 255 characters long.
 *
 * Modifies:
 *
 * Changes the principal's password.
 *
 */
static int
krb5_changepw(
	pam_handle_t *pamh,
	char *princ_str,
	char *old_password,
	char *new_password,
	int debug)
{
	kadm5_ret_t		code;
	krb5_principal 		princ = 0;
	char 			msg_ret[1024], admin_realm[1024];
	char			kprinc[2*MAXHOSTNAMELEN];
	char			*cpw_service;
	kadm5_principal_ent_rec principal_entry;
	kadm5_policy_ent_rec	policy_entry;
	void 			*server_handle;
	krb5_context		context;
	kadm5_config_params	params;

	(void) memset((char *)&params, 0, sizeof (params));
	(void) memset(&principal_entry, 0, sizeof (principal_entry));
	(void) memset(&policy_entry, 0, sizeof (policy_entry));

	if (code = krb5_init_context(&context)) {
		return (6);
	}

	if ((code = get_kmd_kuser(context, (const char *)princ_str, kprinc,
		2*MAXHOSTNAMELEN)) != 0) {
		return (code);
	}

	/* Need to get a krb5_principal struct */

	code = krb5_parse_name(context, kprinc, &princ);

	if (code != 0) {
		return (MISC_EXIT_STATUS);
	}

	if (strlen(old_password) == 0) {
		krb5_free_principal(context, princ);
		return (5);
	}

	(void) snprintf(admin_realm, sizeof (admin_realm), "%s",
		krb5_princ_realm(context, princ)->data);
	params.mask |= KADM5_CONFIG_REALM;
	params.realm = admin_realm;


	if (kadm5_get_cpw_host_srv_name(context, admin_realm, &cpw_service)) {
		syslog(LOG_ERR,
			dgettext(TEXT_DOMAIN,
				"PAM-KRB5 (password):unable to get host based "
				"service name for realm %s\n"),
			admin_realm);
		return (3);
	}

	code = kadm5_init_with_password(kprinc, old_password, cpw_service,
					&params, KADM5_STRUCT_VERSION,
					KADM5_API_VERSION_2, &server_handle);
	free(cpw_service);
	if (code != 0) {
		if (debug)
			syslog(LOG_DEBUG,
			    "PAM-KRB5 (password): changepw: "
			    "init_with_pw failed:  (%s)", error_message(code));
		krb5_free_principal(context, princ);
		return ((code == KADM5_BAD_PASSWORD) ? 2 : 3);
	}

	code = kadm5_chpass_principal_util(server_handle, princ,
					new_password,
					NULL /* don't need pw back */,
					msg_ret,
					sizeof (msg_ret));

	if (code) {
		char msgs[2][PAM_MAX_MSG_SIZE];

		(void) snprintf(msgs[0], PAM_MAX_MSG_SIZE, "%s",
			dgettext(TEXT_DOMAIN,
				"Kerberos password not changed: "));
		(void) snprintf(msgs[1], PAM_MAX_MSG_SIZE, "%s", msg_ret);

		display_msgs(pamh, PAM_ERROR_MSG, 2, msgs);
	}

	krb5_free_principal(context, princ);

	(void) kadm5_destroy(server_handle);

	if (debug)
		syslog(LOG_DEBUG,
		    "PAM-KRB5 (password): changepw: end %d", code);

	if (code == KRB5_LIBOS_CANTREADPWD)
		return (5);
	else if (code)
		return (4);
	else
		return (PAM_SUCCESS);
}
Example #3
0
static krb5_error_code
process_chpw_request(krb5_context context, void *server_handle,
			char *realm, int s, krb5_keytab keytab,
			struct sockaddr_in *sin, krb5_data *req,
			krb5_data *rep)
{
	krb5_error_code ret;
	char *ptr;
	int plen, vno;
	krb5_address local_kaddr, remote_kaddr;
	int allocated_mem = 0;
	krb5_data ap_req, ap_rep;
	krb5_auth_context auth_context;
	krb5_principal changepw;
	krb5_ticket *ticket;
	krb5_data cipher, clear;
	struct sockaddr local_addr, remote_addr;
	int addrlen;
	krb5_replay_data replay;
	krb5_error krberror;
	int numresult;
	char strresult[1024];

	ret = 0;
	rep->length = 0;

	auth_context = NULL;
	changepw = NULL;
	ap_rep.length = 0;
	ap_rep.data = NULL;
	ticket = NULL;
	clear.length = 0;
	clear.data = NULL;
	cipher.length = 0;
	cipher.data = NULL;

	if (req->length < 4) {
		/*
		 * either this, or the server is printing bad messages,
		 * or the caller passed in garbage
		 */
		ret = KRB5KRB_AP_ERR_MODIFIED;
		numresult = KRB5_KPASSWD_MALFORMED;
		(void) strlcpy(strresult, "Request was truncated",
				sizeof (strresult));
		goto chpwfail;
	}

	ptr = req->data;

	/*
	 * Verify length
	 */
	plen = (*ptr++ & 0xff);
	plen = (plen<<8) | (*ptr++ & 0xff);

	if (plen != req->length)
		return (KRB5KRB_AP_ERR_MODIFIED);

	/*
	 * Verify version number
	 */
	vno = (*ptr++ & 0xff);
	vno = (vno<<8) | (*ptr++ & 0xff);

	if (vno != 1) {
		ret = KRB5KDC_ERR_BAD_PVNO;
		numresult = KRB5_KPASSWD_MALFORMED;
		(void) snprintf(strresult, sizeof (strresult),
		    "Request contained unknown protocol version number %d",
		    vno);
		goto chpwfail;
	}

	/*
	 * Read, check ap-req length
	 */
	ap_req.length = (*ptr++ & 0xff);
	ap_req.length = (ap_req.length<<8) | (*ptr++ & 0xff);

	if (ptr + ap_req.length >= req->data + req->length) {
		ret = KRB5KRB_AP_ERR_MODIFIED;
		numresult = KRB5_KPASSWD_MALFORMED;
		(void) strlcpy(strresult, "Request was truncated in AP-REQ",
					sizeof (strresult));
		goto chpwfail;
	}

	/*
	 * Verify ap_req
	 */
	ap_req.data = ptr;
	ptr += ap_req.length;

	if (ret = krb5_auth_con_init(context, &auth_context)) {
		krb5_klog_syslog(LOG_ERR,
				gettext("Change password request failed. "
					"Failed initializing auth context: %s"),
				error_message(ret));
		numresult = KRB5_KPASSWD_HARDERROR;
		(void) strlcpy(strresult, "Failed initializing auth context",
					sizeof (strresult));
		goto chpwfail;
	}

	if (ret = krb5_auth_con_setflags(context, auth_context,
					KRB5_AUTH_CONTEXT_DO_SEQUENCE)) {
		krb5_klog_syslog(LOG_ERR,
				gettext("Change password request failed. "
						"Failed setting auth "
					    "context flags: %s"),
				error_message(ret));
		numresult = KRB5_KPASSWD_HARDERROR;
		(void) strlcpy(strresult, "Failed initializing auth context",
					sizeof (strresult));
		goto chpwfail;
	}

	if (ret = krb5_build_principal(context, &changepw, strlen(realm), realm,
				    "kadmin", "changepw", NULL)) {
		krb5_klog_syslog(LOG_ERR,
			gettext("Change password request failed "
					"Failed to build kadmin/changepw "
					"principal: %s"),
			error_message(ret));
		numresult = KRB5_KPASSWD_HARDERROR;
		(void) strlcpy(strresult,
				"Failed building kadmin/changepw principal",
				sizeof (strresult));
		goto chpwfail;
	}

	ret = krb5_rd_req(context, &auth_context, &ap_req, changepw, keytab,
			NULL, &ticket);

	if (ret) {
		char kt_name[MAX_KEYTAB_NAME_LEN];
		if (krb5_kt_get_name(context, keytab,
				kt_name, sizeof (kt_name)))
			strncpy(kt_name, "default keytab", sizeof (kt_name));

		switch (ret) {
		case KRB5_KT_NOTFOUND:
		krb5_klog_syslog(LOG_ERR,
			gettext("Change password request failed because "
					"keytab entry \"kadmin/changepw\" "
					"is missing from \"%s\""),
			kt_name);
		break;
		case ENOENT:
		krb5_klog_syslog(LOG_ERR,
			gettext("Change password request failed because "
					"keytab file \"%s\" does not exist"),
			kt_name);
		break;
		default:
		krb5_klog_syslog(LOG_ERR,
			gettext("Change password request failed. "
				"Failed to parse Kerberos AP_REQ message: %s"),
			error_message(ret));
		}

		numresult = KRB5_KPASSWD_AUTHERROR;
		(void) strlcpy(strresult, "Failed reading application request",
					sizeof (strresult));
		goto chpwfail;
	}

	/*
	 * Set up address info
	 */
	addrlen = sizeof (local_addr);

	if (getsockname(s, &local_addr, &addrlen) < 0) {
		ret = errno;
		numresult = KRB5_KPASSWD_HARDERROR;
		(void) strlcpy(strresult,
				"Failed getting server internet address",
				sizeof (strresult));
		goto chpwfail;
	}

	/*
	 * Some brain-dead OS's don't return useful information from
	 * the getsockname call.  Namely, windows and solaris.
	 */
	if (((struct sockaddr_in *)&local_addr)->sin_addr.s_addr != 0) {
		local_kaddr.addrtype = ADDRTYPE_INET;
		local_kaddr.length = sizeof (((struct sockaddr_in *)
						&local_addr)->sin_addr);
		/* CSTYLED */
		local_kaddr.contents = (krb5_octet *) &(((struct sockaddr_in *)&local_addr)->sin_addr);
	} else {
		krb5_address **addrs;

		krb5_os_localaddr(context, &addrs);

		local_kaddr.magic = addrs[0]->magic;
		local_kaddr.addrtype = addrs[0]->addrtype;
		local_kaddr.length = addrs[0]->length;
		if ((local_kaddr.contents = malloc(addrs[0]->length)) == 0) {
			ret = errno;
			numresult = KRB5_KPASSWD_HARDERROR;
			(void) strlcpy(strresult,
				"Malloc failed for local_kaddr",
				sizeof (strresult));
			goto chpwfail;
		}

		(void) memcpy(local_kaddr.contents, addrs[0]->contents,
				addrs[0]->length);
		allocated_mem++;

		krb5_free_addresses(context, addrs);
	}

	addrlen = sizeof (remote_addr);

	if (getpeername(s, &remote_addr, &addrlen) < 0) {
		ret = errno;
		numresult = KRB5_KPASSWD_HARDERROR;
		(void) strlcpy(strresult,
				"Failed getting client internet address",
				sizeof (strresult));
		goto chpwfail;
	}

	remote_kaddr.addrtype = ADDRTYPE_INET;
	remote_kaddr.length = sizeof (((struct sockaddr_in *)
					&remote_addr)->sin_addr);
	/* CSTYLED */
	remote_kaddr.contents = (krb5_octet *) &(((struct sockaddr_in *)&remote_addr)->sin_addr);
	remote_kaddr.addrtype = ADDRTYPE_INET;
	remote_kaddr.length = sizeof (sin->sin_addr);
	remote_kaddr.contents = (krb5_octet *) &sin->sin_addr;

	/*
	 * mk_priv requires that the local address be set.
	 * getsockname is used for this.  rd_priv requires that the
	 * remote address be set.  recvfrom is used for this.  If
	 * rd_priv is given a local address, and the message has the
	 * recipient addr in it, this will be checked.  However, there
	 * is simply no way to know ahead of time what address the
	 * message will be delivered *to*.  Therefore, it is important
	 * that either no recipient address is in the messages when
	 * mk_priv is called, or that no local address is passed to
	 * rd_priv.  Both is a better idea, and I have done that.  In
	 * summary, when mk_priv is called, *only* a local address is
	 * specified.  when rd_priv is called, *only* a remote address
	 * is specified.  Are we having fun yet?
	 */
	if (ret = krb5_auth_con_setaddrs(context, auth_context, NULL,
					&remote_kaddr)) {
		numresult = KRB5_KPASSWD_HARDERROR;
		(void) strlcpy(strresult,
				"Failed storing client internet address",
				sizeof (strresult));
		goto chpwfail;
	}

	/*
	 * Verify that this is an AS_REQ ticket
	 */
	if (!(ticket->enc_part2->flags & TKT_FLG_INITIAL)) {
		numresult = KRB5_KPASSWD_AUTHERROR;
		(void) strlcpy(strresult,
				"Ticket must be derived from a password",
				sizeof (strresult));
		goto chpwfail;
	}

	/*
	 * Construct the ap-rep
	 */
	if (ret = krb5_mk_rep(context, auth_context, &ap_rep)) {
		numresult = KRB5_KPASSWD_AUTHERROR;
		(void) strlcpy(strresult,
				"Failed replying to application request",
				sizeof (strresult));
		goto chpwfail;
	}

	/*
	 * Decrypt the new password
	 */
	cipher.length = (req->data + req->length) - ptr;
	cipher.data = ptr;

	if (ret = krb5_rd_priv(context, auth_context, &cipher,
				&clear, &replay)) {
		numresult = KRB5_KPASSWD_HARDERROR;
		(void) strlcpy(strresult, "Failed decrypting request",
					sizeof (strresult));
		goto chpwfail;
	}

	/*
	 * Change the password
	 */
	if ((ptr = (char *)malloc(clear.length + 1)) == NULL) {
		ret = errno;
		numresult = KRB5_KPASSWD_HARDERROR;
		(void) strlcpy(strresult, "Malloc failed for ptr",
			sizeof (strresult));
		goto chpwfail;
	}
	(void) memcpy(ptr, clear.data, clear.length);
	ptr[clear.length] = '\0';

	ret = (kadm5_ret_t)kadm5_chpass_principal_util(server_handle,
						ticket->enc_part2->client,
						ptr, NULL, strresult,
						sizeof (strresult));
	/*
	 * Zap the password
	 */
	(void) memset(clear.data, 0, clear.length);
	(void) memset(ptr, 0, clear.length);
	if (clear.data != NULL) {
		krb5_xfree(clear.data);
		clear.data = NULL;
	}
	free(ptr);
	clear.length = 0;

	if (ret) {
		if ((ret != KADM5_PASS_Q_TOOSHORT) &&
		    (ret != KADM5_PASS_REUSE) &&
		    (ret != KADM5_PASS_Q_CLASS) &&
		    (ret != KADM5_PASS_Q_DICT) &&
		    (ret != KADM5_PASS_TOOSOON))
			numresult = KRB5_KPASSWD_HARDERROR;
		else
			numresult = KRB5_KPASSWD_SOFTERROR;
		/*
		 * strresult set by kadb5_chpass_principal_util()
		 */
		goto chpwfail;
	}

	/*
	 * Success!
	 */
	numresult = KRB5_KPASSWD_SUCCESS;
	(void) strlcpy(strresult, "", sizeof (strresult));

chpwfail:

	clear.length = 2 + strlen(strresult);
	if (clear.data != NULL) {
		krb5_xfree(clear.data);
		clear.data = NULL;
	}
	if ((clear.data = (char *)malloc(clear.length)) == NULL) {
		ret = errno;
		numresult = KRB5_KPASSWD_HARDERROR;
		(void) strlcpy(strresult, "Malloc failed for clear.data",
			sizeof (strresult));
	}

	cipher.length = 0;

	if (ap_rep.length) {
		if (ret = krb5_auth_con_setaddrs(context, auth_context,
					&local_kaddr, NULL)) {
		    numresult = KRB5_KPASSWD_HARDERROR;
		    (void) strlcpy(strresult,
			"Failed storing client and server internet addresses",
			sizeof (strresult));
		} else {
			if (ret = krb5_mk_priv(context, auth_context, &clear,
						&cipher, &replay)) {
				numresult = KRB5_KPASSWD_HARDERROR;
				(void) strlcpy(strresult,
					"Failed encrypting reply",
					sizeof (strresult));
			}
		}
	}

	ptr = clear.data;
	*ptr++ = (numresult>>8) & 0xff;
	*ptr++ = numresult & 0xff;

	(void) memcpy(ptr, strresult, strlen(strresult));

	/*
	 * If no KRB-PRIV was constructed, then we need a KRB-ERROR.
	 * If this fails, just bail.  There's nothing else we can do.
	 */
	if (cipher.length == 0) {
		/*
		 * Clear out ap_rep now, so that it won't be inserted
		 * in the reply
		 */
		if (ap_rep.length) {
			if (ap_rep.data != NULL)
				krb5_xfree(ap_rep.data);
			ap_rep.data = NULL;
			ap_rep.length = 0;
		}

		krberror.ctime = 0;
		krberror.cusec = 0;
		krberror.susec = 0;
		if (ret = krb5_timeofday(context, &krberror.stime))
			goto bailout;

		/*
		 * This is really icky.  but it's what all the other callers
		 * to mk_error do.
		 */
		krberror.error = ret;
		krberror.error -= ERROR_TABLE_BASE_krb5;
		if (krberror.error < 0 || krberror.error > 128)
			krberror.error = KRB_ERR_GENERIC;

		krberror.client = NULL;
		if (ret = krb5_build_principal(context, &krberror.server,
					    strlen(realm), realm,
					    "kadmin", "changepw", NULL)) {
			goto bailout;
		}

		krberror.text.length = 0;
		krberror.e_data = clear;

		ret = krb5_mk_error(context, &krberror, &cipher);

		krb5_free_principal(context, krberror.server);

		if (ret)
			goto bailout;
	}

	/*
	 * Construct the reply
	 */
	rep->length = 6 + ap_rep.length + cipher.length;
	if ((rep->data = (char *)malloc(rep->length)) == NULL)  {
		ret = errno;
		goto bailout;
	}
	ptr = rep->data;

	/*
	 * Length
	 */
	*ptr++ = (rep->length>>8) & 0xff;
	*ptr++ = rep->length & 0xff;

	/*
	 * Version == 0x0001 big-endian
	 */
	*ptr++ = 0;
	*ptr++ = 1;

	/*
	 * ap_rep length, big-endian
	 */
	*ptr++ = (ap_rep.length>>8) & 0xff;
	*ptr++ = ap_rep.length & 0xff;

	/*
	 * ap-rep data
	 */
	if (ap_rep.length) {
		(void) memcpy(ptr, ap_rep.data, ap_rep.length);
		ptr += ap_rep.length;
	}

	/*
	 * krb-priv or krb-error
	 */
	(void) memcpy(ptr, cipher.data, cipher.length);

bailout:
	if (auth_context)
		krb5_auth_con_free(context, auth_context);
	if (changepw)
		krb5_free_principal(context, changepw);
	if (ap_rep.data != NULL)
		krb5_xfree(ap_rep.data);
	if (ticket)
		krb5_free_ticket(context, ticket);
	if (clear.data != NULL)
		krb5_xfree(clear.data);
	if (cipher.data != NULL)
		krb5_xfree(cipher.data);
	if (allocated_mem)
		krb5_xfree(local_kaddr.contents);

	return (ret);
}