Example #1
0
static void
change (krb5_auth_context auth_context,
        krb5_principal admin_principal,
        uint16_t version,
        int s,
        struct sockaddr *sa,
        int sa_size,
        krb5_data *in_data)
{
    krb5_error_code ret;
    char *client = NULL, *admin = NULL;
    const char *pwd_reason;
    kadm5_config_params conf;
    void *kadm5_handle = NULL;
    krb5_principal principal = NULL;
    krb5_data *pwd_data = NULL;
    char *tmp;
    ChangePasswdDataMS chpw;

    memset (&conf, 0, sizeof(conf));
    memset(&chpw, 0, sizeof(chpw));

    if (version == KRB5_KPASSWD_VERS_CHANGEPW) {
        ret = krb5_copy_data(context, in_data, &pwd_data);
        if (ret) {
            krb5_warn (context, ret, "krb5_copy_data");
            reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_MALFORMED,
                        "out out memory copying password");
            return;
        }
        principal = admin_principal;
    } else if (version == KRB5_KPASSWD_VERS_SETPW) {
        size_t len;

        ret = decode_ChangePasswdDataMS(in_data->data, in_data->length,
                                        &chpw, &len);
        if (ret) {
            krb5_warn (context, ret, "decode_ChangePasswdDataMS");
            reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_MALFORMED,
                        "malformed ChangePasswdData");
            return;
        }


        ret = krb5_copy_data(context, &chpw.newpasswd, &pwd_data);
        if (ret) {
            krb5_warn (context, ret, "krb5_copy_data");
            reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_MALFORMED,
                        "out out memory copying password");
            goto out;
        }

        if (chpw.targname == NULL && chpw.targrealm != NULL) {
            krb5_warn (context, ret, "kadm5_init_with_password_ctx");
            reply_priv (auth_context, s, sa, sa_size,
                        KRB5_KPASSWD_MALFORMED,
                        "targrealm but not targname");
            goto out;
        }

        if (chpw.targname) {
            krb5_principal_data princ;

            princ.name = *chpw.targname;
            princ.realm = *chpw.targrealm;
            if (princ.realm == NULL) {
                ret = krb5_get_default_realm(context, &princ.realm);

                if (ret) {
                    krb5_warnx (context,
                                "kadm5_init_with_password_ctx: "
                                "failed to allocate realm");
                    reply_priv (auth_context, s, sa, sa_size,
                                KRB5_KPASSWD_SOFTERROR,
                                "failed to allocate realm");
                    goto out;
                }
            }
            ret = krb5_copy_principal(context, &princ, &principal);
            if (*chpw.targrealm == NULL)
                free(princ.realm);
            if (ret) {
                krb5_warn(context, ret, "krb5_copy_principal");
                reply_priv(auth_context, s, sa, sa_size,
                           KRB5_KPASSWD_HARDERROR,
                           "failed to allocate principal");
                goto out;
            }
        } else
            principal = admin_principal;
    } else {
        krb5_warnx (context, "kadm5_init_with_password_ctx: unknown proto");
        reply_priv (auth_context, s, sa, sa_size,
                    KRB5_KPASSWD_HARDERROR,
                    "Unknown protocol used");
        return;
    }

    ret = krb5_unparse_name (context, admin_principal, &admin);
    if (ret) {
        krb5_warn (context, ret, "unparse_name failed");
        reply_priv (auth_context, s, sa, sa_size,
                    KRB5_KPASSWD_HARDERROR, "out of memory error");
        goto out;
    }

    conf.realm = principal->realm;
    conf.mask |= KADM5_CONFIG_REALM;

    ret = kadm5_init_with_password_ctx(context,
                                       admin,
                                       NULL,
                                       KADM5_ADMIN_SERVICE,
                                       &conf, 0, 0,
                                       &kadm5_handle);
    if (ret) {
        krb5_warn (context, ret, "kadm5_init_with_password_ctx");
        reply_priv (auth_context, s, sa, sa_size, 2,
                    "Internal error");
        goto out;
    }

    ret = krb5_unparse_name(context, principal, &client);
    if (ret) {
        krb5_warn (context, ret, "unparse_name failed");
        reply_priv (auth_context, s, sa, sa_size,
                    KRB5_KPASSWD_HARDERROR, "out of memory error");
        goto out;
    }

    /*
     * Check password quality if not changing as administrator
     */

    if (krb5_principal_compare(context, admin_principal, principal) == TRUE) {

        pwd_reason = kadm5_check_password_quality (context, principal,
                     pwd_data);
        if (pwd_reason != NULL ) {
            krb5_warnx (context,
                        "%s didn't pass password quality check with error: %s",
                        client, pwd_reason);
            reply_priv (auth_context, s, sa, sa_size,
                        KRB5_KPASSWD_SOFTERROR, pwd_reason);
            goto out;
        }
        krb5_warnx (context, "Changing password for %s", client);
    } else {
        ret = _kadm5_acl_check_permission(kadm5_handle, KADM5_PRIV_CPW,
                                          principal);
        if (ret) {
            krb5_warn (context, ret,
                       "Check ACL failed for %s for changing %s password",
                       admin, client);
            reply_priv (auth_context, s, sa, sa_size,
                        KRB5_KPASSWD_HARDERROR, "permission denied");
            goto out;
        }
        krb5_warnx (context, "%s is changing password for %s", admin, client);
    }

    ret = krb5_data_realloc(pwd_data, pwd_data->length + 1);
    if (ret) {
        krb5_warn (context, ret, "malloc: out of memory");
        reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_HARDERROR,
                    "Internal error");
        goto out;
    }
    tmp = pwd_data->data;
    tmp[pwd_data->length - 1] = '\0';

    ret = kadm5_s_chpass_principal_cond (kadm5_handle, principal, tmp);
    krb5_free_data (context, pwd_data);
    pwd_data = NULL;
    if (ret) {
        const char *str = krb5_get_error_message(context, ret);
        krb5_warnx(context, "kadm5_s_chpass_principal_cond: %s", str);
        reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_SOFTERROR,
                    str ? str : "Internal error");
        krb5_free_error_message(context, str);
        goto out;
    }
    reply_priv (auth_context, s, sa, sa_size, KRB5_KPASSWD_SUCCESS,
                "Password changed");
out:
    free_ChangePasswdDataMS(&chpw);
    if (principal != admin_principal)
        krb5_free_principal(context, principal);
    if (admin)
        free(admin);
    if (client)
        free(client);
    if (pwd_data)
        krb5_free_data(context, pwd_data);
    if (kadm5_handle)
        kadm5_destroy (kadm5_handle);
}
Example #2
0
static bool kpasswd_process_request(struct kdc_server *kdc,
				    TALLOC_CTX *mem_ctx,
				    struct gensec_security *gensec_security,
				    uint16_t version,
				    DATA_BLOB *input,
				    DATA_BLOB *reply)
{
	struct auth_session_info *session_info;
	size_t pw_len;

	if (!NT_STATUS_IS_OK(gensec_session_info(gensec_security,
						 &session_info))) {
		return kpasswdd_make_error_reply(kdc, mem_ctx,
						KRB5_KPASSWD_HARDERROR,
						"gensec_session_info failed!",
						reply);
	}

	switch (version) {
	case KRB5_KPASSWD_VERS_CHANGEPW:
	{
		DATA_BLOB password;
		if (!convert_string_talloc_convenience(mem_ctx, lp_iconv_convenience(kdc->task->lp_ctx),
					       CH_UTF8, CH_UTF16,
					       (const char *)input->data,
					       input->length,
					       (void **)&password.data, &pw_len, false)) {
			return false;
		}
		password.length = pw_len;

		return kpasswdd_change_password(kdc, mem_ctx, session_info,
						&password, reply);
		break;
	}
	case KRB5_KPASSWD_VERS_SETPW:
	{
		NTSTATUS status;
		enum samPwdChangeReason reject_reason = SAM_PWD_CHANGE_NO_ERROR;
		struct samr_DomInfo1 *dominfo = NULL;
		struct ldb_context *samdb;
		struct ldb_message *msg;
		krb5_context context = kdc->smb_krb5_context->krb5_context;

		ChangePasswdDataMS chpw;
		DATA_BLOB password;

		krb5_principal principal;
		char *set_password_on_princ;
		struct ldb_dn *set_password_on_dn;

		size_t len;
		int ret;

		msg = ldb_msg_new(mem_ctx);
		if (!msg) {
			return false;
		}

		ret = decode_ChangePasswdDataMS(input->data, input->length,
						&chpw, &len);
		if (ret) {
			return kpasswdd_make_error_reply(kdc, mem_ctx,
							KRB5_KPASSWD_MALFORMED,
							"failed to decode password change structure",
							reply);
		}

		if (!convert_string_talloc_convenience(mem_ctx, lp_iconv_convenience(kdc->task->lp_ctx),
					       CH_UTF8, CH_UTF16,
					       (const char *)chpw.newpasswd.data,
					       chpw.newpasswd.length,
					       (void **)&password.data, &pw_len, false)) {
			free_ChangePasswdDataMS(&chpw);
			return false;
		}

		password.length = pw_len;

		if ((chpw.targname && !chpw.targrealm)
		    || (!chpw.targname && chpw.targrealm)) {
			return kpasswdd_make_error_reply(kdc, mem_ctx,
							KRB5_KPASSWD_MALFORMED,
							"Realm and principal must be both present, or neither present",
							reply);
		}
		if (chpw.targname && chpw.targrealm) {
#ifdef SAMBA4_INTERNAL_HEIMDAL
			if (_krb5_principalname2krb5_principal(kdc->smb_krb5_context->krb5_context,
							       &principal, *chpw.targname,
							       *chpw.targrealm) != 0) {
				free_ChangePasswdDataMS(&chpw);
				return kpasswdd_make_error_reply(kdc, mem_ctx,
								KRB5_KPASSWD_MALFORMED,
								"failed to extract principal to set",
								reply);

			}
#else /* SAMBA4_INTERNAL_HEIMDAL */
				return kpasswdd_make_error_reply(kdc, mem_ctx,
								KRB5_KPASSWD_BAD_VERSION,
								"Operation Not Implemented",
								reply);
#endif /* SAMBA4_INTERNAL_HEIMDAL */
		} else {
			free_ChangePasswdDataMS(&chpw);
			return kpasswdd_change_password(kdc, mem_ctx, session_info,
							&password, reply);
		}
		free_ChangePasswdDataMS(&chpw);

		if (krb5_unparse_name(context, principal, &set_password_on_princ) != 0) {
			krb5_free_principal(context, principal);
			return kpasswdd_make_error_reply(kdc, mem_ctx,
							KRB5_KPASSWD_MALFORMED,
							"krb5_unparse_name failed!",
							reply);
		}

		krb5_free_principal(context, principal);

		samdb = samdb_connect(mem_ctx, kdc->task->event_ctx, kdc->task->lp_ctx, session_info);
		if (!samdb) {
			return kpasswdd_make_error_reply(kdc, mem_ctx,
							 KRB5_KPASSWD_HARDERROR,
							 "Unable to open database!",
							 reply);
		}

		DEBUG(3, ("%s\\%s (%s) is changing password of %s\n",
			  session_info->server_info->domain_name,
			  session_info->server_info->account_name,
			  dom_sid_string(mem_ctx, session_info->security_token->user_sid),
			  set_password_on_princ));
		ret = ldb_transaction_start(samdb);
		if (ret) {
			status = NT_STATUS_TRANSACTION_ABORTED;
			return kpasswd_make_pwchange_reply(kdc, mem_ctx,
							   status,
							   SAM_PWD_CHANGE_NO_ERROR,
							   NULL,
							   reply);
		}

		status = crack_user_principal_name(samdb, mem_ctx,
						   set_password_on_princ,
						   &set_password_on_dn, NULL);
		free(set_password_on_princ);
		if (!NT_STATUS_IS_OK(status)) {
			ldb_transaction_cancel(samdb);
			return kpasswd_make_pwchange_reply(kdc, mem_ctx,
							   status,
							   SAM_PWD_CHANGE_NO_ERROR,
							   NULL,
							   reply);
		}

		msg = ldb_msg_new(mem_ctx);
		if (msg == NULL) {
			ldb_transaction_cancel(samdb);
			status = NT_STATUS_NO_MEMORY;
		} else {
			msg->dn = ldb_dn_copy(msg, set_password_on_dn);
			if (!msg->dn) {
				status = NT_STATUS_NO_MEMORY;
			}
		}

		if (NT_STATUS_IS_OK(status)) {
			/* Admin password set */
			status = samdb_set_password(samdb, mem_ctx,
						    set_password_on_dn, NULL,
						    msg, &password, NULL, NULL,
						    false, /* this is not a user password change */
						    &reject_reason, &dominfo);
		}

		if (NT_STATUS_IS_OK(status)) {
			/* modify the samdb record */
			ret = samdb_replace(samdb, mem_ctx, msg);
			if (ret != 0) {
				DEBUG(2,("Failed to modify record to set password on %s: %s\n",
					 ldb_dn_get_linearized(msg->dn),
					 ldb_errstring(samdb)));
				status = NT_STATUS_ACCESS_DENIED;
			}
		}
		if (NT_STATUS_IS_OK(status)) {
			ret = ldb_transaction_commit(samdb);
			if (ret != 0) {
				DEBUG(1,("Failed to commit transaction to set password on %s: %s\n",
					 ldb_dn_get_linearized(msg->dn),
					 ldb_errstring(samdb)));
				status = NT_STATUS_TRANSACTION_ABORTED;
			}
		} else {
			ldb_transaction_cancel(samdb);
		}
		return kpasswd_make_pwchange_reply(kdc, mem_ctx,
						   status,
						   reject_reason,
						   dominfo,
						   reply);
	}
	default:
		return kpasswdd_make_error_reply(kdc, mem_ctx,
						 KRB5_KPASSWD_BAD_VERSION,
						 talloc_asprintf(mem_ctx,
								 "Protocol version %u not supported",
								 version),
						 reply);
	}
	return true;
}