Merge branch 'upstream'

This commit is contained in:
Timo Aaltonen committed 2021-11-26 09:43:46 +02:00
commit d19214ab8d
111 files changed
+18034 -15591

No files matched your search

+2 -14
View File
@@ -4,9 +4,8 @@ set -ex
FLAVOR="$1"
ENVPYTHON="$(realpath -s "$2")"
ENVSITEPACKAGESDIR="$(realpath -s "$3")"
ENVDIR="$4"
# 4...end are package requirements
shift 4
# 3...end are package requirements
shift 3
TOXINIDIR="$(cd "$(dirname "$0")" && pwd)"
@@ -26,21 +25,10 @@ if [ ! -f "${TOXINIDIR}/tox.ini" ]; then
exit 3
fi
if [ ! -d "${ENVDIR}" ]; then
echo "${ENVDIR}: no such directory"
exit 4
fi
# https://pip.pypa.io/en/stable/user_guide/#environment-variables
export PIP_CACHE_DIR="${TOXINIDIR}/.tox/cache"
mkdir -p "${PIP_CACHE_DIR}"
# /tmp could be mounted with noexec option.
# pip checks if path is executable and if not then doesn't set such
# permission bits
export PIP_BUILD="${ENVDIR}/pip_build"
rm -rf "${PIP_BUILD}"
DISTBUNDLE="${TOXINIDIR}/dist/bundle"
mkdir -p "${DISTBUNDLE}"
+4 -1
View File
@@ -1076,11 +1076,13 @@ args: 0,1,1
option: Str('version?')
output: Output('result')
command: config_mod/1
args: 0,29,3
args: 0,32,3
option: Flag('add_sids?', autofill=True, default=False)
option: Str('addattr*', cli_name='addattr')
option: Flag('all', autofill=True, cli_name='all', default=False)
option: Str('ca_renewal_master_server?', autofill=False)
option: Str('delattr*', cli_name='delattr')
option: Flag('enable_sid?', autofill=True, default=False)
option: StrEnum('ipaconfigstring*', autofill=False, cli_name='ipaconfigstring', values=[u'AllowNThash', u'KDC:Disable Last Success', u'KDC:Disable Lockout', u'KDC:Disable Default Preauth for SPNs'])
option: Str('ipadefaultemaildomain?', autofill=False, cli_name='emaildomain')
option: Str('ipadefaultloginshell?', autofill=False, cli_name='defaultshell')
@@ -1102,6 +1104,7 @@ option: StrEnum('ipauserauthtype*', autofill=False, cli_name='user_auth_type', v
option: Bool('ipauserdefaultsubordinateid?', autofill=False, cli_name='user_default_subid')
option: Str('ipauserobjectclasses*', autofill=False, cli_name='userobjectclasses')
option: IA5Str('ipausersearchfields?', autofill=False, cli_name='usersearch')
option: Str('netbios_name?', autofill=False)
option: Flag('raw', autofill=True, cli_name='raw', default=False)
option: Flag('rights', autofill=True, default=False)
option: Str('setattr*', cli_name='setattr')
+2
View File
@@ -81,6 +81,7 @@ Developers:
Jeremy Frasier
Jérôme Fenal
Jim Meyering
Jochen Kellner
JoeDrane
John Dennis
John L
@@ -134,6 +135,7 @@ Developers:
Ondřej Hamada
Orion Poplawski
Patrice Duc-Jacquet
Pavel Březina
Pavel Picka
Pavel Vomáčka
Pavel Zůna
+31 -6
View File
@@ -216,7 +216,7 @@ endif
$(MAKE) $(AM_MAKEFLAGS) acilint apilint polint pylint jslint $(RPMLINT_TARGET) yamllint check
@echo "All tests passed."
.PHONY: fastcheck fasttest fastlint
.PHONY: fastcheck fasttest fastlint fastcodestyle
fastcheck:
@$(MAKE) -j1 $(AM_MAKEFLAGS) fastlint $(RPMLINT_TARGET) yamllint fasttest apilint acilint
@@ -229,7 +229,36 @@ fasttest: $(GENERATED_PYTHON_FILES) ipasetup.py
--ignore $(abspath $(top_srcdir))/ipatests/test_integration \
--ignore $(abspath $(top_srcdir))/ipatests/test_xmlrpc
fastlint: $(GENERATED_PYTHON_FILES) ipasetup.py acilint apilint
fastcodestyle: $(GENERATED_PYTHON_FILES) ipasetup.py
@ # keep Python files in sync to pycodestyle configuration in
@ # tox.ini(filename=)
@echo "Fast code style checking with $(PYTHON) from branch '$(GIT_BRANCH)'"
@MERGEBASE=$$(git merge-base --fork-point $(GIT_BRANCH)); \
PYFILES=$$(git diff --name-only --diff-filter=d $${MERGEBASE} \
| grep -E '\.py$$' ); \
INFILES=$$(git diff --name-only --diff-filter=d $${MERGEBASE} \
| grep -E '\.in$$' \
| xargs -n1 file 2>/dev/null | grep Python \
| cut -d':' -f1; ); \
if [ -n "$${PYFILES}" ] && [ -n "$${INFILES}" ]; then \
FILES="$$( printf $${PYFILES}\\n$${INFILES} )" ; \
elif [ -n "$${PYFILES}" ]; then \
FILES="$${PYFILES}" ; \
else \
FILES="$${INFILES}" ; \
fi ; \
if [ -n "$${FILES}" ]; then \
echo -e "Fast code style checking for files:\n$${FILES}\n"; \
echo "pycodestyle"; \
echo "-----------"; \
git diff -U0 $${MERGEBASE} -- $${FILES} | \
$(PYTHON) -m pycodestyle -v --diff || exit $$?; \
else \
echo "No modified Python files found"; \
fi
fastlint: $(GENERATED_PYTHON_FILES) ipasetup.py fastcodestyle acilint apilint
if ! WITH_PYLINT
@echo "ERROR: pylint not available"; exit 1
endif
@@ -251,10 +280,6 @@ endif
fi ; \
if [ -n "$${FILES}" ]; then \
echo -e "Fast linting files:\n$${FILES}\n"; \
echo "pycodestyle"; \
echo "-----------"; \
git diff -U0 $${MERGEBASE} | \
$(PYTHON) -m pycodestyle --diff || exit $$?; \
echo -e "\npylint"; \
echo "------"; \
$(PYTHON) -m pylint --version; \
+3 -3
View File
@@ -21,7 +21,7 @@
########################################################
define(IPA_VERSION_MAJOR, 4)
define(IPA_VERSION_MINOR, 9)
define(IPA_VERSION_RELEASE, 7)
define(IPA_VERSION_RELEASE, 8)
########################################################
# For 'pre' releases the version will be #
@@ -86,8 +86,8 @@ define(IPA_DATA_VERSION, 20100614120000)
# #
########################################################
define(IPA_API_VERSION_MAJOR, 2)
# Last change: add subordinate id feature
define(IPA_API_VERSION_MINOR, 243)
# Last change: add enable_sid to config
define(IPA_API_VERSION_MINOR, 245)
########################################################
+3
View File
@@ -172,6 +172,9 @@ Specifies the Kerberos realm.
.B replication_wait_timeout <seconds>
The time to wait for a new entry to be replicated during replica installation. The default value is 300 seconds.
.TP
.B schema_ttl <seconds>
The number of seconds for the ipa tool to cache the IPA API and help schema. Reducing this value during development is helpful so that API changes are seen sooner in the tool. Setting this on a server will define the TTL for all client versions > 4.3.1. Client versions > 4.3.1 that connect to IPA servers older than 4.3.1 will use the client-side configuration value. The default is 3600 seconds. 0 disables the cache. A change in the ttl will not be immediately recognized by clients. They will use the new value once their current cache expires.
.TP
.B server <hostname>
Specifies the IPA Server hostname.
.TP
+2
View File
@@ -61,6 +61,7 @@ ipadb_la_LIBADD = \
$(UNISTRING_LIBS) \
$(SSSCERTMAP_LIBS) \
$(top_builddir)/util/libutil.la \
-lsamba-errors \
$(NULL)
if HAVE_CMOCKA
@@ -104,6 +105,7 @@ ipa_kdb_tests_LDADD = \
-lkdb5 \
-lsss_idmap \
-lsamba-security-samba4 \
-lsamba-errors \
$(NULL)
appdir = $(libexecdir)/ipa
+8
View File
@@ -79,6 +79,7 @@
#define IPA_USER_AUTH_TYPE "ipaUserAuthType"
struct ipadb_mspac;
struct dom_sid;
enum ipadb_user_auth {
IPADB_USER_AUTH_NONE = 0,
@@ -155,6 +156,8 @@ struct ipadb_e_data {
bool has_tktpolaux;
enum ipadb_user_auth user_auth;
struct ipadb_e_pol_limits pol_limits[IPADB_USER_AUTH_IDX_MAX];
bool has_sid;
struct dom_sid *sid;
};
struct ipadb_context *ipadb_get_context(krb5_context kcontext);
@@ -366,3 +369,8 @@ int ipadb_get_enc_salt_types(struct ipadb_context *ipactx, LDAPMessage *entry,
/* CERTAUTH PLUGIN */
void ipa_certauth_free_moddata(krb5_certauth_moddata *moddata);
#endif
int ipadb_string_to_sid(const char *str, struct dom_sid *sid);
void alloc_sid(struct dom_sid **sid);
void free_sid(struct dom_sid **sid);
bool dom_sid_check(const struct dom_sid *sid1, const struct dom_sid *sid2, bool exact_check);
+35 -1
View File
@@ -21,6 +21,8 @@
*/
#include "ipa_kdb.h"
#include <strings.h>
#include <unicase.h>
static char *acl_attrs[] = {
"objectClass",
@@ -188,10 +190,41 @@ krb5_error_code ipadb_check_allowed_to_delegate(krb5_context kcontext,
const krb5_db_entry *server,
krb5_const_principal proxy)
{
krb5_error_code kerr;
krb5_error_code kerr, result;
char *srv_principal = NULL;
krb5_db_entry *proxy_entry = NULL;
struct ipadb_e_data *ied_server, *ied_proxy;
LDAPMessage *res = NULL;
/* Handle the case where server == proxy, this is allowed in S4U*/
kerr = ipadb_get_principal(kcontext, proxy,
KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY,
&proxy_entry);
if (kerr) {
goto done;
}
ied_server = (struct ipadb_e_data *) server->e_data;
ied_proxy = (struct ipadb_e_data *) proxy_entry->e_data;
/* If we have SIDs for both entries, compare SIDs */
if ((ied_server->has_sid && ied_server->sid != NULL) &&
(ied_proxy->has_sid && ied_proxy->sid != NULL)) {
if (dom_sid_check(ied_server->sid, ied_proxy->sid, true)) {
kerr = 0;
goto done;
}
}
/* Otherwise, compare entry DNs */
kerr = ulc_casecmp(ied_server->entry_dn, strlen(ied_server->entry_dn),
ied_proxy->entry_dn, strlen(ied_proxy->entry_dn),
NULL, NULL, &result);
if (kerr == 0 && result == 0) {
goto done;
}
kerr = krb5_unparse_name(kcontext, server->princ, &srv_principal);
if (kerr) {
goto done;
@@ -208,6 +241,7 @@ krb5_error_code ipadb_check_allowed_to_delegate(krb5_context kcontext,
}
done:
ipadb_free_principal(kcontext, proxy_entry);
krb5_free_unparsed_name(kcontext, srv_principal);
ldap_msgfree(res);
return kerr;
+2 -2
View File
@@ -88,7 +88,7 @@ ipa_kdcpolicy_check_as(krb5_context context, krb5_kdcpolicy_moddata moddata,
}
ua = ied->user_auth;
/* If no mechanisms are set, allow every auth method */
if (ua == IPADB_USER_AUTH_NONE) {
jitter(ONE_DAY_SECONDS, lifetime_out);
@@ -128,7 +128,7 @@ ipa_kdcpolicy_check_as(krb5_context context, krb5_kdcpolicy_moddata moddata,
valid_auth_indicators++;
/* Allow hardened even if only password pre-auth is allowed */
if (!(ua & (IPADB_USER_AUTH_HARDENED | IPADB_USER_AUTH_PASSWORD))) {
*status = "Password pre-authentication not not allowed for this user.";
*status = "Password pre-authentication not allowed for this user.";
kerr = KRB5KDC_ERR_POLICY;
goto done;
}
+501 -45
View File
@@ -80,7 +80,20 @@ static char *memberof_pac_attrs[] = {
#define AUTHZ_DATA_TYPE_PAD "PAD"
#define AUTHZ_DATA_TYPE_NONE "NONE"
int string_to_sid(const char *str, struct dom_sid *sid)
void alloc_sid(struct dom_sid **sid)
{
*sid = malloc(sizeof(struct dom_sid));
}
void free_sid(struct dom_sid **sid)
{
if (sid != NULL && *sid != NULL) {
free(*sid);
*sid = NULL;
}
}
int ipadb_string_to_sid(const char *str, struct dom_sid *sid)
{
unsigned long val;
const char *s;
@@ -223,7 +236,7 @@ static struct dom_sid *dom_sid_dup(TALLOC_CTX *memctx,
* dom_sid_check() is supposed to be used with sid1 representing domain SID
* and sid2 being either domain or resource SID in the domain
*/
static bool dom_sid_check(const struct dom_sid *sid1, const struct dom_sid *sid2, bool exact_check)
bool dom_sid_check(const struct dom_sid *sid1, const struct dom_sid *sid2, bool exact_check)
{
int c, num;
@@ -372,7 +385,7 @@ static krb5_error_code ipadb_add_asserted_identity(struct ipadb_context *ipactx,
/* For S4U2Self, add Service Asserted Identity SID
* otherwise, add Authentication Authority Asserted Identity SID */
ret = string_to_sid((flags & KRB5_KDB_FLAG_PROTOCOL_TRANSITION) ?
ret = ipadb_string_to_sid((flags & KRB5_KDB_FLAG_PROTOCOL_TRANSITION) ?
"S-1-18-2" : "S-1-18-1",
arr[sidcount].sid);
if (ret) {
@@ -635,27 +648,21 @@ static krb5_error_code ipadb_fill_info3(struct ipadb_context *ipactx,
info3->base.logon_count = 0; /* we do not have this info yet */
info3->base.bad_password_count = 0; /* we do not have this info yet */
if ((is_host || is_service)) {
/* it is either host or service, so get the hostname first */
char *sep = strchr(info3->base.account_name.string, '/');
bool is_master = is_master_host(
ipactx,
sep ? sep + 1 : info3->base.account_name.string);
if (is_master) {
/* Well know RID of domain controllers group */
info3->base.rid = 516;
} else {
/* Well know RID of domain computers group */
info3->base.rid = 515;
}
} else {
ret = ipadb_ldap_attr_to_str(ipactx->lcontext, lentry,
"ipaNTSecurityIdentifier", &strres);
if (ret) {
/* SID is mandatory */
/* Use AES keys by default to detect changes.
* This bit is not used by Windows clients and servers so we can
* clear it after detecting the changes */
info3->base.acct_flags = ACB_USE_AES_KEYS;
ret = ipadb_ldap_attr_to_str(ipactx->lcontext, lentry,
"ipaNTSecurityIdentifier", &strres);
if (ret) {
/* SID is mandatory for all but host/services */
if (!(is_host || is_service)) {
return ret;
}
ret = string_to_sid(strres, &sid);
info3->base.rid = 0;
} else {
ret = ipadb_string_to_sid(strres, &sid);
free(strres);
if (ret) {
return ret;
@@ -666,6 +673,29 @@ static krb5_error_code ipadb_fill_info3(struct ipadb_context *ipactx,
}
}
/* If SID was present prefer using it even for hosts and services
* but we still need to set the account flags correctly */
if ((is_host || is_service)) {
/* it is either host or service, so get the hostname first */
char *sep = strchr(info3->base.account_name.string, '/');
bool is_master = is_master_host(
ipactx,
sep ? sep + 1 : info3->base.account_name.string);
if (is_master) {
/* Well known RID of domain controllers group */
if (info3->base.rid == 0) {
info3->base.rid = 516;
}
info3->base.acct_flags |= ACB_SVRTRUST;
} else {
/* Well known RID of domain computers group */
if (info3->base.rid == 0) {
info3->base.rid = 515;
}
info3->base.acct_flags |= ACB_WSTRUST;
}
}
ret = ipadb_ldap_deref_results(ipactx->lcontext, lentry, &deref_results);
switch (ret) {
LDAPDerefRes *dres;
@@ -700,7 +730,7 @@ static krb5_error_code ipadb_fill_info3(struct ipadb_context *ipactx,
}
}
if (strcasecmp(dval->type, "ipaNTSecurityIdentifier") == 0) {
ret = string_to_sid((char *)dval->vals[0].bv_val, &gsid);
ret = ipadb_string_to_sid((char *)dval->vals[0].bv_val, &gsid);
if (ret) {
continue;
}
@@ -786,9 +816,13 @@ static krb5_error_code ipadb_fill_info3(struct ipadb_context *ipactx,
/* always zero out, not used for Krb, only NTLM */
memset(&info3->base.LMSessKey, '\0', sizeof(info3->base.LMSessKey));
/* TODO: fill based on objectclass, user vs computer, etc... */
info3->base.acct_flags = ACB_NORMAL; /* samr_AcctFlags */
/* If account type was not set before, default to ACB_NORMAL */
if (!(info3->base.acct_flags & ~ACB_USE_AES_KEYS)) {
info3->base.acct_flags |= ACB_NORMAL; /* samr_AcctFlags */
}
/* Clear ACB_USE_AES_KEYS as it is not used by Windows */
info3->base.acct_flags &= ~ACB_USE_AES_KEYS;
info3->base.sub_auth_status = 0;
info3->base.last_successful_logon = 0;
info3->base.last_failed_logon = 0;
@@ -799,6 +833,155 @@ static krb5_error_code ipadb_fill_info3(struct ipadb_context *ipactx,
return ret;
}
#ifdef HAVE_PAC_REQUESTER_SID
static krb5_error_code ipadb_get_requester_sid(krb5_context context,
krb5_pac pac,
struct dom_sid *sid)
{
enum ndr_err_code ndr_err;
krb5_error_code ret;
DATA_BLOB pac_requester_sid_in;
krb5_data k5pac_requester_sid_in;
union PAC_INFO info;
TALLOC_CTX *tmp_ctx;
struct ipadb_context *ipactx;
ipactx = ipadb_get_context(context);
if (!ipactx) {
return KRB5_KDB_DBNOTINITED;
}
tmp_ctx = talloc_new(NULL);
if (tmp_ctx == NULL) {
return ENOMEM;
}
ret = krb5_pac_get_buffer(context, pac, PAC_TYPE_REQUESTER_SID,
&k5pac_requester_sid_in);
if (ret != 0) {
talloc_free(tmp_ctx);
return ret;
}
pac_requester_sid_in = data_blob_const(k5pac_requester_sid_in.data,
k5pac_requester_sid_in.length);
ndr_err = ndr_pull_union_blob(&pac_requester_sid_in, tmp_ctx, &info,
PAC_TYPE_REQUESTER_SID,
(ndr_pull_flags_fn_t)ndr_pull_PAC_INFO);
krb5_free_data_contents(context, &k5pac_requester_sid_in);
if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
talloc_free(tmp_ctx);
return EINVAL;
}
*sid = info.requester_sid.sid;
talloc_free(tmp_ctx);
return 0;
}
#endif
static krb5_error_code ipadb_get_sid_from_pac(TALLOC_CTX *ctx,
struct PAC_LOGON_INFO *info,
struct dom_sid *sid)
{
struct dom_sid *client_sid = NULL;
/* Construct SID from the PAC */
if (info->info3.base.rid == 0) {
client_sid = info->info3.sids[0].sid;
} else {
client_sid = dom_sid_dup(ctx, info->info3.base.domain_sid);
if (!client_sid) {
return ENOMEM;
}
sid_append_rid(client_sid, info->info3.base.rid);
}
*sid = *client_sid;
return 0;
}
#ifdef HAVE_PAC_ATTRIBUTES_INFO
static krb5_error_code ipadb_client_requested_pac(krb5_context context,
krb5_pac pac,
TALLOC_CTX *mem_ctx,
krb5_boolean *requested_pac)
{
enum ndr_err_code ndr_err;
krb5_data k5pac_attrs_in;
DATA_BLOB pac_attrs_in;
union PAC_INFO pac_attrs;
krb5_error_code ret;
*requested_pac = true;
ret = krb5_pac_get_buffer(context, pac, PAC_TYPE_ATTRIBUTES_INFO,
&k5pac_attrs_in);
if (ret != 0) {
return ret == ENOENT ? 0 : ret;
}
pac_attrs_in = data_blob_const(k5pac_attrs_in.data,
k5pac_attrs_in.length);
ndr_err = ndr_pull_union_blob(&pac_attrs_in, mem_ctx, &pac_attrs,
PAC_TYPE_ATTRIBUTES_INFO,
(ndr_pull_flags_fn_t)ndr_pull_PAC_INFO);
krb5_free_data_contents(context, &k5pac_attrs_in);
if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
krb5_klog_syslog(LOG_ERR, "can't parse the PAC ATTRIBUTES_INFO: %s\n",
nt_errstr(nt_status));
return KRB5_KDB_INTERNAL_ERROR;
}
if (pac_attrs.attributes_info.flags & (PAC_ATTRIBUTE_FLAG_PAC_WAS_GIVEN_IMPLICITLY
| PAC_ATTRIBUTE_FLAG_PAC_WAS_REQUESTED)) {
*requested_pac = true;
} else {
*requested_pac = false;
}
return 0;
}
static krb5_error_code ipadb_get_pac_attrs_blob(TALLOC_CTX *mem_ctx,
const krb5_boolean *pac_request,
DATA_BLOB *pac_attrs_data)
{
union PAC_INFO pac_attrs;
enum ndr_err_code ndr_err;
memset(&pac_attrs, 0, sizeof(pac_attrs));
*pac_attrs_data = data_blob_null;
/* Set the length of the flags in bits. */
pac_attrs.attributes_info.flags_length = 2;
if (pac_request == NULL) {
pac_attrs.attributes_info.flags
|= PAC_ATTRIBUTE_FLAG_PAC_WAS_GIVEN_IMPLICITLY;
} else if (*pac_request) {
pac_attrs.attributes_info.flags
|= PAC_ATTRIBUTE_FLAG_PAC_WAS_REQUESTED;
}
ndr_err = ndr_push_union_blob(pac_attrs_data, mem_ctx, &pac_attrs,
PAC_TYPE_ATTRIBUTES_INFO,
(ndr_push_flags_fn_t)ndr_push_PAC_INFO);
if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
NTSTATUS nt_status = ndr_map_error2ntstatus(ndr_err);
krb5_klog_syslog(LOG_ERR, "can't create PAC ATTRIBUTES_INFO: %s\n",
nt_errstr(nt_status));
return KRB5_KDB_INTERNAL_ERROR;
}
return 0;
}
#endif
static krb5_error_code ipadb_get_pac(krb5_context kcontext,
krb5_db_entry *client,
unsigned int flags,
@@ -817,6 +1000,7 @@ static krb5_error_code ipadb_get_pac(krb5_context kcontext,
enum ndr_err_code ndr_err;
union PAC_INFO pac_upn;
char *principal = NULL;
struct dom_sid client_sid;
/* When no client entry is there, we cannot generate MS-PAC */
if (!client) {
@@ -917,6 +1101,18 @@ static krb5_error_code ipadb_get_pac(krb5_context kcontext,
pac_upn.upn_dns_info.flags |= PAC_UPN_DNS_FLAG_CONSTRUCTED;
}
kerr = ipadb_get_sid_from_pac(tmpctx, pac_info.logon_info.info, &client_sid);
if (kerr) {
goto done;
}
#ifdef HAVE_PAC_UPN_DNS_INFO_EX
/* Add samAccountName and a SID */
pac_upn.upn_dns_info.flags |= PAC_UPN_DNS_FLAG_HAS_SAM_NAME_AND_SID;
pac_upn.upn_dns_info.ex.sam_name_and_sid.samaccountname = pac_info.logon_info.info->info3.base.account_name.string;
pac_upn.upn_dns_info.ex.sam_name_and_sid.objectsid = &client_sid;
#endif
ndr_err = ndr_push_union_blob(&pac_data, tmpctx, &pac_upn,
PAC_TYPE_UPN_DNS_INFO,
(ndr_push_flags_fn_t)ndr_push_PAC_INFO);
@@ -931,6 +1127,53 @@ static krb5_error_code ipadb_get_pac(krb5_context kcontext,
kerr = krb5_pac_add_buffer(kcontext, *pac, KRB5_PAC_UPN_DNS_INFO, &data);
#ifdef HAVE_PAC_ATTRIBUTES_INFO
/* == Add implicit PAC type attributes info as we always try to generate PAC == */
{
DATA_BLOB pac_attrs_data;
kerr = ipadb_get_pac_attrs_blob(tmpctx, NULL, &pac_attrs_data);
if (kerr) {
goto done;
}
data.magic = KV5M_DATA;
data.data = (char *)pac_attrs_data.data;
data.length = pac_attrs_data.length;
kerr = krb5_pac_add_buffer(kcontext, *pac, PAC_TYPE_ATTRIBUTES_INFO, &data);
if (kerr) {
goto done;
}
}
#endif
#ifdef HAVE_PAC_REQUESTER_SID
{
union PAC_INFO pac_requester_sid;
/* == Package PAC_REQUESTER_SID == */
memset(&pac_requester_sid, 0, sizeof(pac_requester_sid));
pac_requester_sid.requester_sid.sid = client_sid;
ndr_err = ndr_push_union_blob(&pac_data, tmpctx, &pac_requester_sid,
PAC_TYPE_REQUESTER_SID,
(ndr_push_flags_fn_t)ndr_push_PAC_INFO);
if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
kerr = KRB5_KDB_INTERNAL_ERROR;
goto done;
}
data.magic = KV5M_DATA;
data.data = (char *)pac_data.data;
data.length = pac_data.length;
kerr = krb5_pac_add_buffer(kcontext, *pac, PAC_TYPE_REQUESTER_SID, &data);
if (kerr) {
goto done;
}
}
#endif
done:
ldap_msgfree(results);
talloc_free(tmpctx);
@@ -1189,7 +1432,7 @@ static int map_groups(TALLOC_CTX *memctx, krb5_context kcontext,
}
if (strcasecmp(dval->type,
"ipaNTSecurityIdentifier") == 0) {
kerr = string_to_sid((char *)dval->vals[0].bv_val, &sid);
kerr = ipadb_string_to_sid((char *)dval->vals[0].bv_val, &sid);
if (kerr != 0) {
continue;
}
@@ -1391,6 +1634,124 @@ static void filter_logon_info_log_message_rid(struct dom_sid *sid, uint32_t rid)
}
}
static krb5_error_code check_logon_info_consistent(krb5_context context,
TALLOC_CTX *memctx,
krb5_const_principal client_princ,
krb5_boolean is_s4u,
struct PAC_LOGON_INFO_CTR *info)
{
krb5_error_code kerr = 0;
struct ipadb_context *ipactx;
bool result;
bool is_from_trusted_domain = false;
krb5_db_entry *client_actual = NULL;
struct ipadb_e_data *ied = NULL;
int flags = 0;
struct dom_sid client_sid;
#ifdef KRB5_KDB_FLAG_ALIAS_OK
flags = KRB5_KDB_FLAG_ALIAS_OK;
#endif
ipactx = ipadb_get_context(context);
if (!ipactx || !ipactx->mspac) {
return KRB5_KDB_DBNOTINITED;
}
/* We are asked to verify the PAC for our own principal,
* check that our own view on the PAC details is up to date */
if (ipactx->mspac->domsid.num_auths == 0) {
/* Force re-init of KDB's view on our domain */
kerr = ipadb_reinit_mspac(ipactx, true);
if (kerr != 0) {
krb5_klog_syslog(LOG_ERR,
"PAC issue: unable to update realm's view on PAC info");
return KRB5KDC_ERR_POLICY;
}
}
/* check exact domain SID */
result = dom_sid_check(&ipactx->mspac->domsid,
info->info->info3.base.domain_sid, true);
if (!result) {
/* In S4U case we might be dealing with the PAC issued by the trusted domain */
if (is_s4u && (ipactx->mspac->trusts != NULL)) {
/* Iterate through list of trusts and check if this SID belongs to
* one of the domains we trust */
for(int i = 0 ; i < ipactx->mspac->num_trusts ; i++) {
result = dom_sid_check(&ipactx->mspac->trusts[i].domsid,
info->info->info3.base.domain_sid, true);
if (result) {
is_from_trusted_domain = true;
break;
}
}
}
if (!result) {
/* memctx is freed by the caller */
char *sid = dom_sid_string(memctx, info->info->info3.base.domain_sid);
char *dom = dom_sid_string(memctx, &ipactx->mspac->domsid);
krb5_klog_syslog(LOG_ERR, "PAC issue: PAC record claims domain SID different "
"to local domain SID or any trusted domain SID: "
"local [%s], PAC [%s]",
dom ? dom : "<failed to display>",
sid ? sid : "<failed to display>");
return KRB5KDC_ERR_POLICY;
}
}
if (is_s4u && is_from_trusted_domain) {
/* If the PAC belongs to a user from the trusted domain, we cannot compare SIDs */
return 0;
}
kerr = ipadb_get_principal(context, client_princ, flags, &client_actual);
if (kerr != 0) {
krb5_klog_syslog(LOG_ERR, "PAC issue: ipadb_get_principal failed.");
return KRB5KDC_ERR_POLICY;
}
ied = (struct ipadb_e_data *)client_actual->e_data;
if (ied == NULL || ied->magic != IPA_E_DATA_MAGIC) {
krb5_klog_syslog(LOG_ERR, "PAC issue: client e_data fetching failed.");
kerr = EINVAL;
goto done;
}
if (!ied->has_sid || ied->sid == NULL) {
/* Kerberos principal might have no SID associated in the DB entry.
* If this is host or service, we'll associate RID -515 or -516 in PAC
* depending on whether this is a domain member or domain controller
* but since this is not recorded in the DB entry, we the check for
* SID is not needed */
goto done;
}
kerr = ipadb_get_sid_from_pac(memctx, info->info, &client_sid);
if (kerr) {
goto done;
}
result = dom_sid_check(ied->sid, &client_sid, true);
if (!result) {
/* memctx is freed by the caller */
char *local_sid = dom_sid_string(memctx, ied->sid);
char *pac_sid = dom_sid_string(memctx, &client_sid);
krb5_klog_syslog(LOG_ERR, "PAC issue: client principal has a SID "
"different from what PAC claims. "
"local [%s] vs PAC [%s]",
local_sid ? local_sid : "<failed to display>",
pac_sid ? pac_sid : "<failed to display>");
kerr = KRB5KDC_ERR_POLICY;
goto done;
}
done:
ipadb_free_principal(context, client_actual);
return kerr;
}
krb5_error_code filter_logon_info(krb5_context context,
TALLOC_CTX *memctx,
krb5_data realm,
@@ -1618,12 +1979,17 @@ krb5_error_code filter_logon_info(krb5_context context,
static krb5_error_code ipadb_check_logon_info(krb5_context context,
krb5_data origin_realm,
krb5_data *pac_blob)
krb5_const_principal client_princ,
krb5_boolean is_cross_realm,
krb5_boolean is_s4u,
krb5_data *pac_blob,
struct dom_sid *requester_sid)
{
struct PAC_LOGON_INFO_CTR info;
krb5_error_code kerr;
TALLOC_CTX *tmpctx;
krb5_data origin_realm = client_princ->realm;
bool result;
tmpctx = talloc_new(NULL);
if (!tmpctx) {
@@ -1635,6 +2001,38 @@ static krb5_error_code ipadb_check_logon_info(krb5_context context,
goto done;
}
/* Check that requester SID is the same as in the PAC entry */
if (requester_sid != NULL) {
struct dom_sid client_sid;
kerr = ipadb_get_sid_from_pac(tmpctx, info.info, &client_sid);
if (kerr) {
goto done;
}
result = dom_sid_check(&client_sid, requester_sid, true);
if (!result) {
/* memctx is freed by the caller */
char *pac_sid = dom_sid_string(tmpctx, &client_sid);
char *req_sid = dom_sid_string(tmpctx, requester_sid);
krb5_klog_syslog(LOG_ERR, "PAC issue: PAC has a SID "
"different from what PAC requester claims. "
"PAC [%s] vs PAC requester [%s]",
pac_sid ? pac_sid : "<failed to display>",
req_sid ? req_sid : "<failed to display>");
kerr = KRB5KDC_ERR_POLICY;
goto done;
}
}
if (!is_cross_realm) {
/* For local realm case we need to check whether the PAC is for our user
* but we don't need to process further. In S4U2Proxy case when the client
* is ours but operates on behalf of the cross-realm principal, we will
* search through the trusted domains but otherwise skip the exact SID check
* as we are not responsible for the principal from the trusted domain */
kerr = check_logon_info_consistent(context, tmpctx, client_princ, is_s4u, &info);
goto done;
}
kerr = filter_logon_info(context, tmpctx, origin_realm, &info);
if (kerr) {
goto done;
@@ -1749,7 +2147,10 @@ static krb5_error_code ipadb_add_transited_service(krb5_context context,
krb5_free_data_contents(context, &pac_blob);
memset(&pac_blob, 0, sizeof(krb5_data));
kerr = krb5_unparse_name(context, proxy->princ, &tmpstr);
kerr = krb5_unparse_name_flags(context, proxy->princ,
KRB5_PRINCIPAL_UNPARSE_NO_REALM |
KRB5_PRINCIPAL_UNPARSE_DISPLAY,
&tmpstr);
if (kerr != 0) {
goto done;
}
@@ -1824,6 +2225,8 @@ static krb5_error_code ipadb_verify_pac(krb5_context context,
krb5_data pac_blob = { 0 , 0, NULL};
bool is_cross_realm = false;
size_t i;
struct dom_sid *requester_sid = NULL;
struct dom_sid req_sid;
kerr = krb5_pac_parse(context,
authdata[0]->contents,
@@ -1860,19 +2263,30 @@ static krb5_error_code ipadb_verify_pac(krb5_context context,
goto done;
}
/* Now that the PAC is verified augment it with additional info if
* it is coming from a different realm */
if (is_cross_realm) {
kerr = krb5_pac_get_buffer(context, old_pac,
KRB5_PAC_LOGON_INFO, &pac_blob);
if (kerr != 0) {
goto done;
}
/* Now that the PAC is verified, do additional checks.
* Augment it with additional info if it is coming from a different realm */
kerr = krb5_pac_get_buffer(context, old_pac,
KRB5_PAC_LOGON_INFO, &pac_blob);
if (kerr != 0) {
goto done;
}
kerr = ipadb_check_logon_info(context, client_princ->realm, &pac_blob);
if (kerr != 0) {
goto done;
}
memset(&req_sid, '\0', sizeof(struct dom_sid));
#ifdef HAVE_PAC_REQUESTER_SID
kerr = ipadb_get_requester_sid(context, old_pac, &req_sid);
if (kerr == 0) {
requester_sid = &req_sid;
}
#endif
kerr = ipadb_check_logon_info(context,
client_princ,
is_cross_realm,
(flags & KRB5_KDB_FLAGS_S4U),
&pac_blob,
requester_sid);
if (kerr != 0) {
goto done;
}
/* extract buffers and rebuilt pac from scratch so that when re-signing
* with a different cksum type does not cause issues due to mismatching
@@ -1905,6 +2319,48 @@ static krb5_error_code ipadb_verify_pac(krb5_context context,
continue;
}
#ifdef HAVE_PAC_ATTRIBUTES_INFO
if (types[i] == PAC_TYPE_ATTRIBUTES_INFO &&
pac_blob.length != 0) {
/* == Check whether PAC was requested or given implicitly == */
DATA_BLOB pac_attrs_data;
krb5_boolean pac_requested;
TALLOC_CTX *tmpctx = talloc_new(NULL);
if (tmpctx == NULL) {
kerr = ENOMEM;
krb5_pac_free(context, new_pac);
goto done;
}
kerr = ipadb_client_requested_pac(context, old_pac, tmpctx, &pac_requested);
if (kerr != 0) {
talloc_free(tmpctx);
krb5_pac_free(context, new_pac);
goto done;
}
kerr = ipadb_get_pac_attrs_blob(tmpctx, &pac_requested, &pac_attrs_data);
if (kerr) {
talloc_free(tmpctx);
krb5_pac_free(context, new_pac);
goto done;
}
data.magic = KV5M_DATA;
data.data = (char *)pac_attrs_data.data;
data.length = pac_attrs_data.length;
kerr = krb5_pac_add_buffer(context, new_pac, PAC_TYPE_ATTRIBUTES_INFO, &data);
if (kerr) {
talloc_free(tmpctx);
krb5_pac_free(context, new_pac);
goto done;
}
continue;
}
#endif
if (types[i] == KRB5_PAC_DELEGATION_INFO &&
(flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION)) {
/* skip it here, we will add it explicitly later */
@@ -2434,7 +2890,7 @@ ipadb_adtrusts_fill_sid_blacklist(char **source_sid_blacklist,
}
for (i = 0; i < len; i++) {
(void) string_to_sid(source[i], &sid_blacklist[i]);
(void) ipadb_string_to_sid(source[i], &sid_blacklist[i]);
}
*result_sids = sid_blacklist;
@@ -2594,7 +3050,7 @@ ipadb_mspac_get_trusted_domains(struct ipadb_context *ipactx)
goto done;
}
ret = string_to_sid(t[n].domain_sid, &t[n].domsid);
ret = ipadb_string_to_sid(t[n].domain_sid, &t[n].domsid);
if (ret && t[n].domain_sid != NULL) {
ret = EINVAL;
goto done;
@@ -2812,7 +3268,7 @@ krb5_error_code ipadb_reinit_mspac(struct ipadb_context *ipactx, bool force_rein
goto done;
}
ret = string_to_sid(resstr, &ipactx->mspac->domsid);
ret = ipadb_string_to_sid(resstr, &ipactx->mspac->domsid);
if (ret) {
kerr = ret;
free(resstr);
@@ -2865,7 +3321,7 @@ krb5_error_code ipadb_reinit_mspac(struct ipadb_context *ipactx, bool force_rein
goto done;
}
if (ret == 0) {
ret = string_to_sid(resstr, &gsid);
ret = ipadb_string_to_sid(resstr, &gsid);
if (ret) {
free(resstr);
kerr = ret;
-1
View File
@@ -51,7 +51,6 @@ struct ipadb_adtrusts {
size_t *upn_suffixes_len;
};
int string_to_sid(const char *str, struct dom_sid *sid);
char *dom_sid_string(TALLOC_CTX *memctx, const struct dom_sid *dom_sid);
krb5_error_code filter_logon_info(krb5_context context, TALLOC_CTX *memctx,
krb5_data realm, struct PAC_LOGON_INFO_CTR *info);
+25
View File
@@ -79,6 +79,8 @@ static char *std_principal_attrs[] = {
"ipatokenRadiusConfigLink",
"krbAuthIndMaxTicketLife",
"krbAuthIndMaxRenewableAge",
"ipaNTSecurityIdentifier",
"ipaUniqueID",
"objectClass",
NULL
@@ -594,6 +596,7 @@ static krb5_error_code ipadb_parse_ldap_entry(krb5_context kcontext,
char *restring;
char *uidstring;
char **authz_data_list;
char *princ_sid;
krb5_timestamp restime;
bool resbool;
int result;
@@ -963,6 +966,27 @@ static krb5_error_code ipadb_parse_ldap_entry(krb5_context kcontext,
ipadb_parse_authind_policies(kcontext, lcontext, lentry, entry, ua);
}
/* Add SID if it is associated with the principal account */
ied->has_sid = false;
ied->sid = NULL;
ret = ipadb_ldap_attr_to_str(ipactx->lcontext, lentry,
"ipaNTSecurityIdentifier", &princ_sid);
if (ret == 0 && princ_sid != NULL) {
alloc_sid(&ied->sid);
if (ied->sid == NULL) {
kerr = KRB5_KDB_INTERNAL_ERROR;
free(princ_sid);
goto done;
}
ret = ipadb_string_to_sid(princ_sid, ied->sid);
free(princ_sid);
if (ret != 0) {
kerr = ret;
goto done;
}
ied->has_sid = true;
}
kerr = 0;
done:
@@ -1568,6 +1592,7 @@ void ipadb_free_principal_e_data(krb5_context kcontext, krb5_octet *e_data)
}
free(ied->authz_data);
free(ied->pol);
free_sid(&ied->sid);
free(ied);
}
}
+15 -15
View File
@@ -105,7 +105,7 @@ static int setup(void **state)
/* make sure data is not read from LDAP */
ipa_ctx->mspac->last_update = time(NULL) - 1;
ret = string_to_sid(DOM_SID, &ipa_ctx->mspac->domsid);
ret = ipadb_string_to_sid(DOM_SID, &ipa_ctx->mspac->domsid);
assert_int_equal(ret, 0);
ipa_ctx->mspac->num_trusts = 1;
@@ -121,7 +121,7 @@ static int setup(void **state)
ipa_ctx->mspac->trusts[0].domain_sid = strdup(DOM_SID_TRUST);
assert_non_null(ipa_ctx->mspac->trusts[0].domain_sid);
ret = string_to_sid(DOM_SID_TRUST, &ipa_ctx->mspac->trusts[0].domsid);
ret = ipadb_string_to_sid(DOM_SID_TRUST, &ipa_ctx->mspac->trusts[0].domsid);
assert_int_equal(ret, 0);
ipa_ctx->mspac->trusts[0].len_sid_blocklist_incoming = 1;
@@ -129,7 +129,7 @@ static int setup(void **state)
ipa_ctx->mspac->trusts[0].len_sid_blocklist_incoming,
sizeof(struct dom_sid));
assert_non_null(ipa_ctx->mspac->trusts[0].sid_blocklist_incoming);
ret = string_to_sid(BLOCKLIST_SID,
ret = ipadb_string_to_sid(BLOCKLIST_SID,
&ipa_ctx->mspac->trusts[0].sid_blocklist_incoming[0]);
assert_int_equal(ret, 0);
@@ -216,7 +216,7 @@ static void test_filter_logon_info(void **state)
assert_int_equal(kerr, EINVAL);
/* wrong domain SID */
ret = string_to_sid("S-1-5-21-1-1-1", &dom_sid);
ret = ipadb_string_to_sid("S-1-5-21-1-1-1", &dom_sid);
assert_int_equal(ret, 0);
info->info->info3.base.domain_sid = &dom_sid;
@@ -224,7 +224,7 @@ static void test_filter_logon_info(void **state)
assert_int_equal(kerr, EINVAL);
/* matching domain SID */
ret = string_to_sid(DOM_SID_TRUST, &dom_sid);
ret = ipadb_string_to_sid(DOM_SID_TRUST, &dom_sid);
assert_int_equal(ret, 0);
info->info->info3.base.domain_sid = &dom_sid;
@@ -292,7 +292,7 @@ static void test_filter_logon_info(void **state)
}
for (d = 0; d < info->info->info3.sidcount; d++) {
ret = string_to_sid(test_data[c].sids[d],
ret = ipadb_string_to_sid(test_data[c].sids[d],
info->info->info3.sids[d].sid);
assert_int_equal(ret, 0);
}
@@ -434,7 +434,7 @@ static void test_get_authz_data_types(void **state)
krb5_free_principal(test_ctx->krb5_ctx, non_nfs_princ);
}
static void test_string_to_sid(void **state)
static void test_ipadb_string_to_sid(void **state)
{
int ret;
struct dom_sid sid;
@@ -442,25 +442,25 @@ static void test_string_to_sid(void **state)
{21, 2127521184, 1604012920, 1887927527, 72713,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
ret = string_to_sid(NULL, &sid);
ret = ipadb_string_to_sid(NULL, &sid);
assert_int_equal(ret, EINVAL);
ret = string_to_sid("abc", &sid);
ret = ipadb_string_to_sid("abc", &sid);
assert_int_equal(ret, EINVAL);
ret = string_to_sid("S-", &sid);
ret = ipadb_string_to_sid("S-", &sid);
assert_int_equal(ret, EINVAL);
ret = string_to_sid("S-ABC", &sid);
ret = ipadb_string_to_sid("S-ABC", &sid);
assert_int_equal(ret, EINVAL);
ret = string_to_sid("S-123", &sid);
ret = ipadb_string_to_sid("S-123", &sid);
assert_int_equal(ret, EINVAL);
ret = string_to_sid("S-1-123-1-2-3-4-5-6-7-8-9-0-1-2-3-4-5-6", &sid);
ret = ipadb_string_to_sid("S-1-123-1-2-3-4-5-6-7-8-9-0-1-2-3-4-5-6", &sid);
assert_int_equal(ret, EINVAL);
ret = string_to_sid("S-1-5-21-2127521184-1604012920-1887927527-72713",
ret = ipadb_string_to_sid("S-1-5-21-2127521184-1604012920-1887927527-72713",
&sid);
assert_int_equal(ret, 0);
assert_memory_equal(&exp_sid, &sid, sizeof(struct dom_sid));
@@ -531,7 +531,7 @@ int main(int argc, const char *argv[])
setup, teardown),
cmocka_unit_test_setup_teardown(test_filter_logon_info,
setup, teardown),
cmocka_unit_test(test_string_to_sid),
cmocka_unit_test(test_ipadb_string_to_sid),
cmocka_unit_test_setup_teardown(test_dom_sid_string,
setup, teardown),
cmocka_unit_test_setup_teardown(test_check_trusted_realms,
@@ -542,7 +542,9 @@ int pack_ber_user(struct ipa_extdom_ctx *ctx,
if (strcasecmp(locat+1, domain_name) == 0 ) {
locat[0] = '\0';
} else {
ret = LDAP_INVALID_SYNTAX;
/* The found object is from a different domain than requested,
* that means it does not exist in the requested domain */
ret = LDAP_NO_SUCH_OBJECT;
goto done;
}
}
@@ -655,7 +657,9 @@ int pack_ber_group(enum response_types response_type,
if (strcasecmp(locat+1, domain_name) == 0 ) {
locat[0] = '\0';
} else {
ret = LDAP_INVALID_SYNTAX;
/* The found object is from a different domain than requested,
* that means it does not exist in the requested domain */
ret = LDAP_NO_SUCH_OBJECT;
goto done;
}
}
+12 -12
View File
@@ -136,7 +136,7 @@ All count and number attributes are single-value ``INTEGER`` type with
standard integer matching rules. OIDs ``2.16.840.1.113730.3.8.23.8`` and
``2.16.840.1.113730.3.8.23.11`` are reserved for future use.
```raw
```text
attributeTypes: (
2.16.840.1.113730.3.8.23.7
NAME 'ipaSubUidNumber'
@@ -192,7 +192,7 @@ The ``ipaSubordinateId`` object class is an auxiliary subclass of
``top`` and requires all four subordinate id attributes as well as
``ipaOwner`` attribute``
```raw
```text
objectClasses: (
2.16.840.1.113730.3.8.24.4
NAME 'ipaSubordinateId'
@@ -207,7 +207,7 @@ objectClasses: (
The ``ipaSubordinateGid`` and ``ipaSubordinateUid`` are defined for
future use. IPA always assumes the presence of ``ipaSubordinateId``.
```raw
```text
objectClasses: (
2.16.840.1.113730.3.8.24.2
NAME 'ipaSubordinateUid'
@@ -233,7 +233,7 @@ Subordinate id entries have the structural object class
classes ``ipaSubordinateId``, ``ipaSubordinateGid``, or
``ipaSubordinateUid``. ``ipaUniqueId`` is used as a primary key (RDN).
```raw
```text
objectClasses: (
2.16.840.1.113730.3.8.24.5
NAME 'ipaSubordinateIdEntry'
@@ -278,7 +278,7 @@ to use the value of entry attributes as step size.
The config plugin has a new option to enable or disable generation of
subordinate id entries for new users:
```raw
```text
$ ipa config-mod --user-default-subid=true
```
@@ -286,7 +286,7 @@ Subordinate ids are managed by a new plugin class. The ``subid-add``
and ``subid-del`` commands are hidden from command line. New subordinate
ids are generated and auto-assigned with ``subid-generate``.
```raw
```text
$ ipa help subid
Topic commands:
subid-find Search for subordinate id.
@@ -297,7 +297,7 @@ Topic commands:
subid-stats Subordinate id statistics
```
```raw
```text
$ ipa subid-generate --owner testuser9
-----------------------------------------------------------
Added subordinate id "aa28f132-457c-488b-82e1-d123727e4f81"
@@ -312,7 +312,7 @@ Added subordinate id "aa28f132-457c-488b-82e1-d123727e4f81"
```
```raw
```text
$ ipa subid-find --owner testuser9
------------------------
1 subordinate id matched
@@ -328,7 +328,7 @@ Number of entries returned 1
----------------------------
```
```raw
```text
$ ipa -vv subid-stats
...
ipa: INFO: Response: {
@@ -432,7 +432,7 @@ The command uses automatic LDAPI EXTERNAL bind when it's executed as
root user. Other it requires valid Kerberos TGT of an admin or user
administrator.
```raw
```text
# /usr/libexec/ipa/ipa-subids --help
Usage: ipa-subids
@@ -479,7 +479,7 @@ The ``user-find`` command search by start value of subordinate uid and
gid range. The new command ``user-match-subid`` can be used to find a
user by any subordinate id in their range.
```raw
```text
$ ipa subid-match --subuid=2147549183
------------------------
1 subordinate id matched
@@ -528,7 +528,7 @@ attribute to fetch ``uidNumber`` from the user object.
### Deref control example
```raw
```text
$ ldapsearch -L -E '!deref=ipaOwner:uid,uidNumber' \
-b 'cn=subids,cn=accounts,dc=ipasubid,dc=test' \
'(ipaOwner=uid=testuser10,cn=users,cn=accounts,dc=ipasubid,dc=test)'
+9 -2
View File
@@ -1,3 +1,5 @@
%define ipa_requires_gt() %(LC_ALL="C" echo '%*' | xargs -r rpm -q --qf 'Requires: %%{name} >= %%{epoch}:%%{version}-%%{release}\\n' | sed -e 's/ (none):/ /' -e 's/ 0:/ /' | grep -v "is not")
# ipatests enabled by default, can be disabled with --without ipatests
%bcond_without ipatests
# default to not use XML-RPC in Rawhide, can be turned around with --with ipa_join_xml
@@ -110,7 +112,7 @@
%if 0%{?fedora} < 34
%global ds_version 1.4.4.16-1
%else
%global ds_version 2.0.5-1
%global ds_version 2.0.7-1
%endif
# Fix for TLS 1.3 PHA, RHBZ#1775146
@@ -473,6 +475,8 @@ Requires: gssproxy >= 0.7.0-2
Requires: sssd-dbus >= %{sssd_version}
Requires: libpwquality
Requires: cracklib-dicts
# NDR libraries are internal in Samba and change with version without changing SONAME
%ipa_requires_gt samba-client-libs
Provides: %{alt_name}-server = %{version}
Conflicts: %{alt_name}-server
@@ -572,9 +576,11 @@ Requires: %{name}-server = %{version}-%{release}
Requires: bind-dyndb-ldap >= 11.2-2
Requires: bind >= %{bind_version}
Requires: bind-utils >= %{bind_version}
# bind-dnssec-utils is required by the OpenDNSSec integration
# https://pagure.io/freeipa/issue/9026
Requires: bind-dnssec-utils >= %{bind_version}
%if %{with bind_pkcs11}
Requires: bind-pkcs11 >= %{bind_version}
Requires: bind-pkcs11-utils >= %{bind_version}
%else
Requires: softhsm >= %{softhsm_version}
Requires: openssl-pkcs11 >= %{openssl_pkcs11_version}
@@ -1378,6 +1384,7 @@ fi
%dir %{_libexecdir}/ipa/oddjob
%attr(0755,root,root) %{_libexecdir}/ipa/oddjob/org.freeipa.server.conncheck
%attr(0755,root,root) %{_libexecdir}/ipa/oddjob/org.freeipa.server.trust-enable-agent
%attr(0755,root,root) %{_libexecdir}/ipa/oddjob/org.freeipa.server.config-enable-sid
%config(noreplace) %{_sysconfdir}/dbus-1/system.d/org.freeipa.server.conf
%config(noreplace) %{_sysconfdir}/oddjobd.conf.d/ipa-server.conf
%dir %{_libexecdir}/ipa/certmonger
+2
View File
@@ -7,6 +7,7 @@ dbusconfdir = $(sysconfdir)/dbus-1/system.d
dist_noinst_DATA = \
com.redhat.idm.trust-fetch-domains.in \
org.freeipa.server.trust-enable-agent.in \
org.freeipa.server.config-enable-sid.in \
etc/oddjobd.conf.d/oddjobd-ipa-trust.conf.in \
etc/oddjobd.conf.d/ipa-server.conf.in \
$(NULL)
@@ -18,6 +19,7 @@ dist_oddjob_SCRIPTS = \
nodist_oddjob_SCRIPTS = \
com.redhat.idm.trust-fetch-domains \
org.freeipa.server.trust-enable-agent \
org.freeipa.server.config-enable-sid \
$(NULL)
@@ -17,6 +17,12 @@
prepend_user_name="no"
argument_passing_method="cmdline"/>
</method>
<method name="config_enable_sid">
<helper exec="@ODDJOBDIR@/org.freeipa.server.config-enable-sid"
arguments="10"
prepend_user_name="no"
argument_passing_method="cmdline"/>
</method>
</interface>
<interface name="org.freedesktop.DBus.Introspectable">
<allow min_uid="0" max_uid="0"/>
@@ -0,0 +1,76 @@
#!/usr/bin/python3
#
# Copyright (C) 2021 FreeIPA Contributors see COPYING for license
#
import logging
from ipalib import api
from ipalib.install import sysrestore
from ipaplatform.paths import paths
from ipapython import ipaldap
from ipapython.admintool import AdminTool
from ipaserver.install import adtrust, adtrustinstance
logger = logging.getLogger(__name__)
class IPAConfigEnableSid(AdminTool):
command_name = "ipa-enable-sid"
log_file_name = paths.IPASERVER_ENABLESID_LOG
usage = "%prog"
description = "Enable SID generation"
@classmethod
def add_options(cls, parser):
super(IPAConfigEnableSid, cls).add_options(parser)
parser.add_option(
"--add-sids",
dest="add_sids", default=False, action="store_true",
help="Add SIDs for existing users and groups as the final step"
)
parser.add_option(
"--netbios-name",
dest="netbios_name", default=None,
help="NetBIOS name of the IPA domain"
)
parser.add_option(
"--reset-netbios-name",
dest="reset_netbios_name", default=False, action="store_true",
help="Force reset of the existing NetBIOS name"
)
def validate_options(self):
super(IPAConfigEnableSid, self).validate_options(needs_root=True)
def run(self):
api.bootstrap(in_server=True, confdir=paths.ETC_IPA)
api.finalize()
try:
api.Backend.ldap2.connect()
fstore = sysrestore.FileStore(paths.SYSRESTORE)
smb = adtrustinstance.ADTRUSTInstance(fstore, False)
smb.realm = api.env.realm
smb.autobind = ipaldap.AUTOBIND_ENABLED
smb.setup(api.env.host, api.env.realm,
self.options.netbios_name,
self.options.reset_netbios_name,
adtrust.DEFAULT_PRIMARY_RID_BASE,
adtrust.DEFAULT_SECONDARY_RID_BASE,
self.options.add_sids,
enable_compat=False)
smb.find_local_id_range()
smb.create_instance()
finally:
if api.Backend.ldap2.isconnected():
api.Backend.ldap2.disconnect()
return 0
IPAConfigEnableSid.run_cli()
+1
View File
@@ -19,6 +19,7 @@ $OTHER_LIBDEFAULTS
$REALM = {
kdc = $FQDN:88
master_kdc = $FQDN:88
kpasswd_server = $FQDN:464
admin_server = $FQDN:749
default_domain = $DOMAIN
pkinit_anchors = FILE:$KDC_CA_BUNDLE_PEM
+1 -1
View File
@@ -5,7 +5,7 @@ realm = $REALM
kerberos method = dedicated keytab
dedicated keytab file = /etc/samba/samba.keytab
create krb5 conf = no
server role = CLASSIC PRIMARY DOMAIN CONTROLLER
server role = $SERVER_ROLE
security = user
domain master = yes
domain logons = yes
+5 -2
View File
@@ -64,10 +64,11 @@ def parse_options():
parser.add_option("--no-msdcs", dest="no_msdcs", action="store_true",
default=False, help=SUPPRESS_HELP)
parser.add_option("--rid-base", dest="rid_base", type=int, default=1000,
parser.add_option("--rid-base", dest="rid_base", type=int,
default=adtrust.DEFAULT_PRIMARY_RID_BASE,
help="Start value for mapping UIDs and GIDs to RIDs")
parser.add_option("--secondary-rid-base", dest="secondary_rid_base",
type=int, default=100000000,
type=int, default=adtrust.DEFAULT_SECONDARY_RID_BASE,
help="Start value of the secondary range for mapping "
"UIDs and GIDs to RIDs")
parser.add_option("-U", "--unattended", dest="unattended",
@@ -209,6 +210,8 @@ def main():
raise ScriptError(
"Unrecognized error during check of admin rights: %s" % e)
# Force options.setup_adtrust
options.setup_adtrust = True
adtrust.install_check(True, options, api)
adtrust.install(True, options, fstore, api)
+16 -14
View File
@@ -205,10 +205,7 @@ Do not automatically create DNS SSHFP records.
\fB\-\-no\-dnssec\-validation\fR
Disable DNSSEC validation on this server.
.SS "AD TRUST OPTIONS"
.TP
\fB\-\-setup\-adtrust\fR
Configure AD Trust capability on a replica.
.SS "SID GENERATION OPTIONS"
.TP
\fB\-\-netbios\-name\fR=\fINETBIOS_NAME\fR
The NetBIOS name for the IPA domain. If not provided then this is determined
@@ -227,6 +224,21 @@ ipa\-adtrust\-install is run and scheduled independently. To start this task
you have to load an edited version of ipa-sidgen-task-run.ldif with the
ldapmodify command info the directory server.
.TP
\fB\-\-rid-base\fR=\fIRID_BASE\fR
First RID value of the local domain. The first Posix ID of the local domain will
be assigned to this RID, the second to RID+1 etc. See the online help of the
idrange CLI for details.
.TP
\fB\-\-secondary-rid-base\fR=\fISECONDARY_RID_BASE\fR
Start value of the secondary RID range, which is only used in the case a user
and a group share numerically the same Posix ID. See the online help of the
idrange CLI for details.
.SS "AD TRUST OPTIONS"
.TP
\fB\-\-setup\-adtrust\fR
Configure AD Trust capability on a replica.
.TP
\fB\-\-add\-agents\fR
Add IPA masters to the list that allows to serve information about
users from trusted forests. Starting with IPA 4.2, a regular IPA master
@@ -240,16 +252,6 @@ information about users from trusted forests only if they are enabled
via \ipa-adtrust\-install run on any other IPA master. At least SSSD
version 1.13 on IPA master is required to be able to perform as a trust agent.
.TP
\fB\-\-rid-base\fR=\fIRID_BASE\fR
First RID value of the local domain. The first Posix ID of the local domain will
be assigned to this RID, the second to RID+1 etc. See the online help of the
idrange CLI for details.
.TP
\fB\-\-secondary-rid-base\fR=\fISECONDARY_RID_BASE\fR
Start value of the secondary RID range, which is only used in the case a user
and a group share numerically the same Posix ID. See the online help of the
idrange CLI for details.
.TP
\fB\-\-enable\-compat\fR
Enables support for trusted domains users for old clients through Schema Compatibility plugin.
SSSD supports trusted domains natively starting with version 1.9. For platforms that
+6 -5
View File
@@ -230,11 +230,7 @@ Disable DNSSEC validation on this server.
\fB\-\-allow\-zone\-overlap\fR
Allow creation of (reverse) zone even if the zone is already resolvable. Using this option is discouraged as it result in later problems with domain name resolution.
.SS "AD TRUST OPTIONS"
.TP
\fB\-\-setup\-adtrust\fR
Configure AD Trust capability.
.SS "SID GENERATION OPTIONS"
.TP
\fB\-\-netbios\-name\fR=\fINETBIOS_NAME\fR
The NetBIOS name for the IPA domain. If not provided, this is determined
@@ -252,6 +248,11 @@ idrange CLI for details.
Start value of the secondary RID range, which is only used in the case a user
and a group share numerically the same POSIX ID. See the online help of the
idrange CLI for details.
.SS "AD TRUST OPTIONS"
.TP
\fB\-\-setup\-adtrust\fR
Configure AD Trust capability.
.TP
\fB\-\-enable\-compat\fR
Enables support for trusted domains users for old clients through Schema Compatibility plugin.
+16 -1
View File
@@ -840,7 +840,22 @@ IPA.host_dnsrecord_entity_link_widget = function(spec) {
var first_dot = pkey.search(/\./);
var pkeys = [];
pkeys[1] = pkey.substring(0,first_dot);
pkeys[0] = pkey.substring(first_dot+1);
var dnszone = pkey.substring(first_dot+1);
pkeys[0] = dnszone;
// Check whether DNS record associated with the host belongs to a
// fully qualified DNS zone (has trailing '.'). If so, modify the
// pkey to be correct in the link.
if (that.check_data && dnszone[dnszone.length-1] !== '.') {
var avas = that.check_data.dn.split(',');
for (var i=0, j=avas.length; i<j; i++) {
var ava = avas[i];
if (ava.indexOf('idnsname') === 0 && ava.indexOf(dnszone + '.') > 0) {
pkeys[0] = dnszone + '.';
}
}
}
return pkeys;
};
+3
View File
@@ -5223,6 +5223,9 @@ IPA.link_widget = function(spec) {
retry: false,
on_success: function(data) {
that.is_link = data.result && data.result.result;
if (that.is_link) {
that.check_data = data.result.result;
}
that.update_link();
},
on_error: function() {
+1 -1
View File
@@ -16,7 +16,7 @@ only: nsslapd-pluginPrecedence: 60
# Set limits to suite better IPA deployment sizes, defaults are too
# conservative
dn: cn=config
default: nsslapd-sizelimit:100000
replace: nsslapd-sizelimit:2000::100000
dn: cn=config,cn=ldbm database,cn=plugins,cn=config
replace: nsslapd-lookthroughlimit:5000::100000
+11 -7
View File
@@ -446,13 +446,17 @@ def uninstall(fstore, statestore, options):
fstore.restore_file(paths.SMB_CONF)
# Remove samba's persistent and temporary tdb files
tdb_files = [
tdb_file
for tdb_file in os.listdir(paths.SAMBA_DIR)
if tdb_file.endswith(".tdb")
]
for tdb_file in tdb_files:
ipautil.remove_file(tdb_file)
# in /var/lib/samba and /var/lib/samba/private
for smbpath in (paths.SAMBA_DIR,
os.path.join(paths.SAMBA_DIR, "private"),
os.path.join(paths.SAMBA_DIR, "lock")):
tdb_files = [
os.path.join(smbpath, tdb_file)
for tdb_file in os.listdir(smbpath)
if tdb_file.endswith(".tdb")
]
for tdb_file in tdb_files:
ipautil.remove_file(tdb_file)
# Remove our keys from samba's keytab
if os.path.exists(paths.SAMBA_KEYTAB):
+7 -8
View File
@@ -34,6 +34,7 @@ class ServerInfo(MutableMapping):
hostname = DNSName(api.env.server).ToASCII()
self._path = os.path.join(self._DIR, hostname)
self._force_check = api.env.force_schema_check
self._now = time.time()
self._dict = {}
# copy-paste from ipalib/rpc.py
@@ -87,12 +88,11 @@ class ServerInfo(MutableMapping):
def __len__(self):
return len(self._dict)
def update_validity(self, ttl=None):
if ttl is None:
ttl = 3600
self['expiration'] = time.time() + ttl
self['language'] = self._language
self._write()
def update_validity(self, ttl):
if not self.is_valid():
self['expiration'] = self._now + ttl
self['language'] = self._language
self._write()
def is_valid(self):
if self._force_check:
@@ -105,8 +105,7 @@ class ServerInfo(MutableMapping):
# if any of these is missing consider the entry expired
return False
if expiration < time.time():
# validity passed
if expiration < self._now:
return False
if language != self._language:
+4 -1
View File
@@ -58,7 +58,10 @@ def get_package(server_info, client):
else:
server_version = '2.0'
server_info['version'] = server_version
server_info.update_validity()
# in compat mode we don't get the schema TTL from the server
# so use the client context value.
server_info.update_validity(client.api.env.schema_ttl)
server_version = APIVersion(server_version)
+3 -4
View File
@@ -375,7 +375,7 @@ class Schema:
namespaces = {'classes', 'commands', 'topics'}
_DIR = os.path.join(USER_CACHE_PATH, 'ipa', 'schema', FORMAT)
def __init__(self, client, fingerprint=None):
def __init__(self, client, fingerprint=None, ttl=0):
self._dict = {}
self._namespaces = {}
self._help = None
@@ -384,7 +384,6 @@ class Schema:
self._dict[ns] = {}
self._namespaces[ns] = _SchemaNameSpace(self, ns)
ttl = None
read_failed = False
if fingerprint is not None:
@@ -554,10 +553,10 @@ def get_package(server_info, client):
else:
schema = Schema(client, fingerprint)
except SchemaUpToDate as e:
schema = Schema(client, e.fingerprint)
schema = Schema(client, e.fingerprint, e.ttl)
except NotAvailable:
fingerprint = None
ttl = None
ttl = 3600 # set a ttl so we don't hammer the remote server
except SchemaUpToDate as e:
fingerprint = e.fingerprint
ttl = e.ttl
+6
View File
@@ -23,6 +23,7 @@ All constants centralised in one file.
"""
import os
import string
from ipaplatform.constants import constants as _constants
from ipapython.dn import DN
@@ -182,6 +183,9 @@ DEFAULT_CONFIG = (
# How long to wait for a certmonger request to finish
('certmonger_wait_timeout', 300),
# Number of seconds before client should check for schema update.
('schema_ttl', 3600),
# Web Application mount points
('mount_ipa', '/ipa/'),
@@ -368,3 +372,5 @@ KRA_TRACKING_REQS = {
'transportCert cert-pki-kra': 'caTransportCert',
'storageCert cert-pki-kra': 'caStorageCert',
}
ALLOWED_NETBIOS_CHARS = string.ascii_uppercase + string.digits + '-'
+2 -1
View File
@@ -259,7 +259,7 @@ class BasePathNamespace:
IPA_PKI_RETRIEVE_KEY = "/usr/libexec/ipa/ipa-pki-retrieve-key"
IPA_HTTPD_PASSWD_READER = "/usr/libexec/ipa/ipa-httpd-pwdreader"
IPA_PKI_WAIT_RUNNING = "/usr/libexec/ipa/ipa-pki-wait-running"
DNSSEC_KEYFROMLABEL = "/usr/sbin/dnssec-keyfromlabel-pkcs11"
DNSSEC_KEYFROMLABEL = "/usr/sbin/dnssec-keyfromlabel"
GETSEBOOL = "/usr/sbin/getsebool"
GROUPADD = "/usr/sbin/groupadd"
USERMOD = "/usr/sbin/usermod"
@@ -364,6 +364,7 @@ class BasePathNamespace:
IPAREPLICA_CONNCHECK_LOG = "/var/log/ipareplica-conncheck.log"
IPAREPLICA_INSTALL_LOG = "/var/log/ipareplica-install.log"
IPARESTORE_LOG = "/var/log/iparestore.log"
IPASERVER_ENABLESID_LOG = "/var/log/ipaserver-enable-sid.log"
IPASERVER_INSTALL_LOG = "/var/log/ipaserver-install.log"
IPASERVER_ADTRUST_INSTALL_LOG = "/var/log/ipaserver-adtrust-install.log"
IPASERVER_DNS_INSTALL_LOG = "/var/log/ipaserver-dns-install.log"
-1
View File
@@ -36,7 +36,6 @@ class FedoraPathNamespace(RedHatPathNamespace):
NAMED_CRYPTO_POLICY_FILE = "/etc/crypto-policies/back-ends/bind.config"
if HAS_NFS_CONF:
SYSCONFIG_NFS = '/etc/nfs.conf'
DNSSEC_KEYFROMLABEL = "/usr/sbin/dnssec-keyfromlabel"
paths = FedoraPathNamespace()
+3
View File
@@ -756,6 +756,9 @@ class RedHatTaskNamespace(BaseTaskNamespace):
"{}.module".format(name))
for name, _module, _disabled in PKCS11_MODULES)
def enable_sssd_sudo(self, _fstore):
"""sudo enablement is handled by authselect"""
def enable_ldap_automount(self, statestore):
"""
Point automount to ldap in nsswitch.conf.
+12 -3
View File
@@ -763,6 +763,7 @@ class LDAPClient:
'nsslapd-enable-upgrade-hash': True,
'nsslapd-db-locks': True,
'nsslapd-logging-hr-timestamps-enabled': True,
'nsslapd-sizelimit': True,
})
time_limit = -1.0 # unlimited
@@ -1820,9 +1821,17 @@ class LDAPCache(LDAPClient):
entry=None, exception=None):
# idnsname - caching prevents delete when mod value to None
# cospriority - in a Class of Service object, uncacheable
# TODO - usercertificate was banned at one point and I don't remember
# why...
BANNED_ATTRS = {'idnsname', 'cospriority'}
# usercertificate* - caching subtypes is tricky, trade less
# complexity for performance
#
# TODO: teach the cache about subtypes
BANNED_ATTRS = {
'idnsname',
'cospriority',
'usercertificate',
'usercertificate;binary'
}
if not self._enable_cache:
return
+62 -3
View File
@@ -32,6 +32,7 @@ logger = logging.getLogger(__name__)
IPA_DEFAULT_MASTER_SRV_REC = (
# srv record name, port
(DNSName('_ldap._tcp'), 389),
# Kerberos records are provided for MIT KRB5 < 1.15 and AD
(DNSName('_kerberos._tcp'), 88),
(DNSName('_kerberos._udp'), 88),
(DNSName('_kerberos-master._tcp'), 88),
@@ -40,6 +41,20 @@ IPA_DEFAULT_MASTER_SRV_REC = (
(DNSName('_kpasswd._udp'), 464),
)
IPA_DEFAULT_MASTER_URI_REC = (
# URI record name, URI template
# MIT KRB5 1.15+ prefers URI records for service discovery
# scheme: always krb5srv
# flags: empty or 'm' for primary server
# transport: 'tcp', 'udp', or 'kkdcp')
# residual: 'hostname', 'hostname:port', or 'https://' URL
(DNSName('_kerberos'), "krb5srv:m:tcp:{hostname}"),
(DNSName('_kerberos'), "krb5srv:m:udp:{hostname}"),
(DNSName('_kpasswd'), "krb5srv:m:tcp:{hostname}"),
(DNSName('_kpasswd'), "krb5srv:m:udp:{hostname}"),
)
IPA_DEFAULT_ADTRUST_SRV_REC = (
# srv record name, port
(DNSName('_ldap._tcp.Default-First-Site-Name._sites.dc._msdcs'), 389),
@@ -67,6 +82,8 @@ class IPASystemRecords:
# fixme do it configurable
PRIORITY_HIGH = 0
PRIORITY_LOW = 50
# FIXME: use TTL from config
TTL = 3600
def __init__(self, api_instance, all_servers=False):
self.api_instance = api_instance
@@ -134,7 +151,35 @@ class IPASystemRecords:
rdataset = zone_obj.get_rdataset(
r_name, rdatatype.SRV, create=True)
rdataset.add(rd, ttl=86400) # FIXME: use TTL from config
rdataset.add(rd, ttl=self.TTL)
def __add_uri_records(
self, zone_obj, hostname, rname_uri_map,
weight=100, priority=0, location=None
):
assert isinstance(hostname, DNSName)
assert isinstance(priority, int)
assert isinstance(weight, int)
if location:
suffix = self.__get_location_suffix(location)
else:
suffix = self.domain_abs
for name, uri_template in rname_uri_map:
uri = uri_template.format(hostname=hostname.make_absolute())
rd = rdata.from_text(
rdataclass.IN, rdatatype.URI,
'{0} {1} {2}'.format(
priority, weight, uri
)
)
r_name = name.derelativize(suffix)
rdataset = zone_obj.get_rdataset(
r_name, rdatatype.URI, create=True)
rdataset.add(rd, ttl=self.TTL)
def __add_ca_records_from_hostname(self, zone_obj, hostname):
assert isinstance(hostname, DNSName) and hostname.is_absolute()
@@ -163,7 +208,7 @@ class IPASystemRecords:
for rd in rrset:
rdataset = zone_obj.get_rdataset(
r_name, rd.rdtype, create=True)
rdataset.add(rd, ttl=86400) # FIXME: use TTL from config
rdataset.add(rd, ttl=self.TTL)
def __add_kerberos_txt_rec(self, zone_obj):
# FIXME: with external DNS, this should generate records for all
@@ -174,7 +219,7 @@ class IPASystemRecords:
rdataset = zone_obj.get_rdataset(
r_name, rdatatype.TXT, create=True
)
rdataset.add(rd, ttl=86400) # FIXME: use TTL from config
rdataset.add(rd, ttl=self.TTL)
def _add_base_dns_records_for_server(
self, zone_obj, hostname, roles=None, include_master_role=True,
@@ -198,6 +243,12 @@ class IPASystemRecords:
IPA_DEFAULT_MASTER_SRV_REC,
weight=server['weight']
)
self.__add_uri_records(
zone_obj,
hostname_abs,
IPA_DEFAULT_MASTER_URI_REC,
weight=server['weight']
)
if 'CA server' in eff_roles:
self.__add_ca_records_from_hostname(zone_obj, hostname_abs)
@@ -244,6 +295,14 @@ class IPASystemRecords:
priority=priority,
location=location
)
self.__add_uri_records(
zone_obj,
hostname_abs,
IPA_DEFAULT_MASTER_URI_REC,
weight=server['weight'],
priority=priority,
location=location
)
if 'AD trust controller' in eff_roles:
self.__add_srv_records(
+1
View File
@@ -127,6 +127,7 @@ class BINDMgr:
)
cmd = [
paths.DNSSEC_KEYFROMLABEL,
'-E', 'pkcs11',
'-K', workdir,
'-a', attrs['idnsSecAlgorithm'][0],
'-l', uri
+59 -41
View File
@@ -39,6 +39,9 @@ logger = logging.getLogger(__name__)
netbios_name = None
reset_netbios_name = False
DEFAULT_PRIMARY_RID_BASE = 1000
DEFAULT_SECONDARY_RID_BASE = 100000000
def netbios_name_error(name):
logger.error("\nIllegal NetBIOS name [%s].\n", name)
@@ -413,7 +416,7 @@ def install_check(standalone, options, api):
global netbios_name
global reset_netbios_name
if not standalone:
if options.setup_adtrust and not standalone:
check_for_installed_deps()
realm_not_matching_domain = (api.env.domain.upper() != api.env.realm)
@@ -432,26 +435,27 @@ def install_check(standalone, options, api):
# Check if /etc/samba/smb.conf already exists. In case it was not generated
# by IPA, print a warning that we will break existing configuration.
if adtrustinstance.ipa_smb_conf_exists():
if not options.unattended:
print("IPA generated smb.conf detected.")
if not ipautil.user_input("Overwrite smb.conf?",
default=False,
allow_empty=False):
raise ScriptError("Aborting installation.")
if options.setup_adtrust:
if adtrustinstance.ipa_smb_conf_exists():
if not options.unattended:
print("IPA generated smb.conf detected.")
if not ipautil.user_input("Overwrite smb.conf?",
default=False,
allow_empty=False):
raise ScriptError("Aborting installation.")
elif os.path.exists(paths.SMB_CONF):
print("WARNING: The smb.conf already exists. Running "
"ipa-adtrust-install will break your existing samba "
"configuration.\n\n")
if not options.unattended:
if not ipautil.user_input("Do you wish to continue?",
default=False,
allow_empty=False):
raise ScriptError("Aborting installation.")
elif os.path.exists(paths.SMB_CONF):
print("WARNING: The smb.conf already exists. Running "
"ipa-adtrust-install will break your existing samba "
"configuration.\n\n")
if not options.unattended:
if not ipautil.user_input("Do you wish to continue?",
default=False,
allow_empty=False):
raise ScriptError("Aborting installation.")
if not options.unattended and not options.enable_compat:
options.enable_compat = enable_compat_tree()
if not options.unattended and not options.enable_compat:
options.enable_compat = enable_compat_tree()
netbios_name, reset_netbios_name = set_and_check_netbios_name(
options.netbios_name, options.unattended, api)
@@ -467,7 +471,7 @@ def install(standalone, options, fstore, api):
print("Please wait until the prompt is returned.")
print("")
smb = adtrustinstance.ADTRUSTInstance(fstore)
smb = adtrustinstance.ADTRUSTInstance(fstore, options.setup_adtrust)
smb.realm = api.env.realm
smb.autobind = ipaldap.AUTOBIND_ENABLED
smb.setup(api.env.host, api.env.realm,
@@ -530,7 +534,41 @@ def generate_dns_service_records_help(api):
@group
class ADTrustInstallInterface(ServiceAdminInstallInterface):
class SIDInstallInterface(ServiceAdminInstallInterface):
"""
Interface for the SID generation Installer
Knobs defined here will be available in:
* ipa-server-install
* ipa-replica-install
* ipa-adtrust-install
"""
description = "SID generation"
add_sids = knob(
None,
description="Add SIDs for existing users and groups as the final step"
)
add_sids = replica_install_only(add_sids)
netbios_name = knob(
str,
None,
description="NetBIOS name of the IPA domain"
)
rid_base = knob(
int,
DEFAULT_PRIMARY_RID_BASE,
description="Start value for mapping UIDs and GIDs to RIDs"
)
secondary_rid_base = knob(
int,
DEFAULT_SECONDARY_RID_BASE,
description="Start value of the secondary range for mapping "
"UIDs and GIDs to RIDs"
)
@group
class ADTrustInstallInterface(SIDInstallInterface):
"""
Interface for the AD trust installer
@@ -543,10 +581,6 @@ class ADTrustInstallInterface(ServiceAdminInstallInterface):
# the following knobs are provided on top of those specified for
# admin credentials
add_sids = knob(
None,
description="Add SIDs for existing users and groups as the final step"
)
add_agents = knob(
None,
description="Add IPA masters to a list of hosts allowed to "
@@ -557,24 +591,8 @@ class ADTrustInstallInterface(ServiceAdminInstallInterface):
None,
description="Enable support for trusted domains for old clients"
)
netbios_name = knob(
str,
None,
description="NetBIOS name of the IPA domain"
)
no_msdcs = knob(
None,
description="Deprecated: has no effect",
deprecated=True
)
rid_base = knob(
int,
1000,
description="Start value for mapping UIDs and GIDs to RIDs"
)
secondary_rid_base = knob(
int,
100000000,
description="Start value of the secondary range for mapping "
"UIDs and GIDs to RIDs"
)
+82 -43
View File
@@ -25,7 +25,6 @@ import errno
import ldap
import tempfile
import uuid
import string
import struct
import re
import socket
@@ -36,7 +35,8 @@ from ipaserver.install import service
from ipaserver.install import installutils
from ipaserver.install.replication import wait_for_task
from ipalib import errors, api
from ipalib.constants import SUBID_RANGE_START
from ipalib.constants import SUBID_RANGE_START, ALLOWED_NETBIOS_CHARS
from ipalib.util import normalize_zone
from ipapython.dn import DN
from ipapython import ipachangeconf
@@ -54,8 +54,6 @@ if six.PY3:
logger = logging.getLogger(__name__)
ALLOWED_NETBIOS_CHARS = string.ascii_uppercase + string.digits + '-'
UPGRADE_ERROR = """
Entry %(dn)s does not exist.
This means upgrade from IPA 2.x to 3.x did not went well and required S4U2Proxy
@@ -148,8 +146,10 @@ class ADTRUSTInstance(service.Service):
OBJC_GROUP = "ipaNTGroupAttrs"
OBJC_DOMAIN = "ipaNTDomainAttrs"
FALLBACK_GROUP_NAME = u'Default SMB Group'
SERVER_ROLE_OLD = "CLASSIC PRIMARY DOMAIN CONTROLLER"
SERVER_ROLE_NEW = "IPA PRIMARY DOMAIN CONTROLLER"
def __init__(self, fstore=None):
def __init__(self, fstore=None, fulltrust=True):
self.netbios_name = None
self.reset_netbios_name = None
self.add_sids = None
@@ -163,10 +163,15 @@ class ADTRUSTInstance(service.Service):
self.fqdn = None
self.host_netbios_name = None
self.fulltrust = fulltrust
super(ADTRUSTInstance, self).__init__(
"smb", service_desc="CIFS", fstore=fstore, service_prefix=u'cifs',
keytab=paths.SAMBA_KEYTAB)
if self.fulltrust:
super(ADTRUSTInstance, self).__init__(
"smb", service_desc="CIFS", fstore=fstore,
service_prefix=u'cifs',
keytab=paths.SAMBA_KEYTAB)
else:
super(ADTRUSTInstance, self).__init__("SID generation")
self.__setup_default_attributes()
@@ -200,12 +205,13 @@ class ADTRUSTInstance(service.Service):
api.env.container_cifsdomains,
self.suffix)
self.cifs_agent = DN(('krbprincipalname', self.principal.lower()),
api.env.container_service,
self.suffix)
self.host_princ = DN(('fqdn', self.fqdn),
api.env.container_host,
self.suffix)
if self.fulltrust:
self.cifs_agent = DN(('krbprincipalname', self.principal.lower()),
api.env.container_service,
self.suffix)
self.host_princ = DN(('fqdn', self.fqdn),
api.env.container_host,
self.suffix)
def __gen_sid_string(self):
@@ -329,6 +335,8 @@ class ADTRUSTInstance(service.Service):
# _ldap_mod does not return useful error codes, so we must check again
# if the fallback group was created properly.
try:
# Remove entry from cache otherwise get_entry won't find it
api.Backend.ldap2.remove_cache_entry(fb_group_dn)
api.Backend.ldap2.get_entry(fb_group_dn)
except errors.NotFound:
self.print_msg("Failed to add fallback group.")
@@ -546,7 +554,7 @@ class ADTRUSTInstance(service.Service):
try:
current = api.Backend.ldap2.get_entry(targets_dn)
members = current.get('memberPrincipal', [])
if not(self.principal in members):
if self.principal not in members:
current["memberPrincipal"] = members + [self.principal]
api.Backend.ldap2.update_entry(current)
else:
@@ -567,7 +575,16 @@ class ADTRUSTInstance(service.Service):
with tempfile.NamedTemporaryFile(mode='w') as tmp_conf:
tmp_conf.write(conf)
tmp_conf.flush()
ipautil.run([paths.NET, "conf", "import", tmp_conf.name])
try:
ipautil.run([paths.NET, "conf", "import", tmp_conf.name])
except ipautil.CalledProcessError as e:
if e.returncode == 255:
# We have old Samba that doesn't support IPA DC server role
# re-try again with the older variant, upgrade code will
# take care to change the role later when Samba is upgraded
# as well.
self.sub_dict['SERVER_ROLE'] = self.SERVER_ROLE_OLD
self.__write_smb_registry()
def __map_Guests_to_nobody(self):
map_Guests_to_nobody()
@@ -769,6 +786,7 @@ class ADTRUSTInstance(service.Service):
LDAPI_SOCKET=self.ldapi_socket,
FQDN=self.fqdn,
SAMBA_DIR=paths.SAMBA_DIR,
SERVER_ROLE=self.SERVER_ROLE_NEW,
)
def setup(self, fqdn, realm_name, netbios_name,
@@ -838,45 +856,59 @@ class ADTRUSTInstance(service.Service):
self.sub_dict['IPA_LOCAL_RANGE'] = get_idmap_range(self.realm)
def create_instance(self):
self.step("validate server hostname",
self.__validate_server_hostname)
self.step("stopping smbd", self.__stop)
if self.fulltrust:
self.step("validate server hostname",
self.__validate_server_hostname)
self.step("stopping smbd", self.__stop)
self.step("creating samba domain object", \
self.__create_samba_domain_object)
self.step("retrieve local idmap range", self.__retrieve_local_range)
self.step("writing samba config file", self.__write_smb_conf)
self.step("creating samba config registry", self.__write_smb_registry)
self.step("adding cifs Kerberos principal",
self.request_service_keytab)
self.step("adding cifs and host Kerberos principals to the adtrust agents group", \
if self.fulltrust:
self.step("retrieve local idmap range",
self.__retrieve_local_range)
self.step("writing samba config file", self.__write_smb_conf)
self.step("creating samba config registry",
self.__write_smb_registry)
self.step("adding cifs Kerberos principal",
self.request_service_keytab)
self.step("adding cifs and host Kerberos principals to the "
"adtrust agents group",
self.__setup_group_membership)
self.step("check for cifs services defined on other replicas", self.__check_replica)
self.step("adding cifs principal to S4U2Proxy targets", self.__add_s4u2proxy_target)
self.step("check for cifs services defined on other replicas",
self.__check_replica)
self.step("adding cifs principal to S4U2Proxy targets",
self.__add_s4u2proxy_target)
self.step("adding admin(group) SIDs", self.__add_admin_sids)
self.step("adding RID bases", self.__add_rid_bases)
self.step("updating Kerberos config", self.__update_krb5_conf)
self.step("activating CLDAP plugin", self.__add_cldap_module)
if self.fulltrust:
self.step("activating CLDAP plugin", self.__add_cldap_module)
self.step("activating sidgen task", self.__add_sidgen_task)
self.step("map BUILTIN\\Guests to nobody group",
self.__map_Guests_to_nobody)
self.step("configuring smbd to start on boot", self.__enable)
if self.fulltrust:
self.step("map BUILTIN\\Guests to nobody group",
self.__map_Guests_to_nobody)
self.step("configuring smbd to start on boot", self.__enable)
if self.enable_compat:
self.step("enabling trusted domains support for older clients via Schema Compatibility plugin",
self.step("enabling trusted domains support for older clients via "
"Schema Compatibility plugin",
self.__enable_compat_tree)
self.step("restarting Directory Server to take MS PAC and LDAP plugins changes into account", \
self.step("restarting Directory Server to take MS PAC and LDAP "
"plugins changes into account",
self.__restart_dirsrv)
self.step("adding fallback group", self.__add_fallback_group)
self.step("adding Default Trust View", self.__add_default_trust_view)
self.step("setting SELinux booleans", \
self.__configure_selinux_for_smbd)
self.step("starting CIFS services", self.__start)
if self.fulltrust:
self.step("adding Default Trust View",
self.__add_default_trust_view)
self.step("setting SELinux booleans",
self.__configure_selinux_for_smbd)
self.step("starting CIFS services", self.__start)
if self.add_sids:
self.step("adding SIDs to existing users and groups",
self.__add_sids)
self.step("restarting smbd", self.__restart_smb)
if self.fulltrust:
self.step("restarting smbd", self.__restart_smb)
self.start_creation(show_service_name=False)
@@ -918,11 +950,18 @@ class ADTRUSTInstance(service.Service):
ipautil.remove_file(self.smb_conf)
# Remove samba's persistent and temporary tdb files
if os.path.isdir(paths.SAMBA_DIR):
tdb_files = [tdb_file for tdb_file in os.listdir(paths.SAMBA_DIR)
if tdb_file.endswith(".tdb")]
for tdb_file in tdb_files:
ipautil.remove_file(tdb_file)
# in /var/lib/samba and /var/lib/samba/private
for smbpath in (paths.SAMBA_DIR,
os.path.join(paths.SAMBA_DIR, "private"),
os.path.join(paths.SAMBA_DIR, "lock")):
if os.path.isdir(smbpath):
tdb_files = [
os.path.join(smbpath, tdb_file)
for tdb_file in os.listdir(smbpath)
if tdb_file.endswith(".tdb")
]
for tdb_file in tdb_files:
ipautil.remove_file(tdb_file)
# Remove our keys from samba's keytab
self.clean_samba_keytab()
+11 -2
View File
@@ -1973,8 +1973,17 @@ def import_included_profiles():
# Create the profile, replacing any existing profile of same name
profile_data = __get_profile_config(profile_id)
_create_dogtag_profile(profile_id, profile_data, overwrite=True)
logger.debug("Imported profile '%s'", profile_id)
try:
_create_dogtag_profile(profile_id, profile_data,
overwrite=True)
except errors.HTTPRequestError as e:
logger.warning("Failed to import profile '%s': %s. Running "
"ipa-server-upgrade when installation is "
"completed may resolve this issue.",
profile_id, e)
conn.delete_entry(entry)
else:
logger.debug("Imported profile '%s'", profile_id)
else:
logger.debug(
"Profile '%s' is already in LDAP; skipping", profile_id
+32 -3
View File
@@ -36,8 +36,10 @@ from configparser import DEFAULTSECT, ConfigParser, RawConfigParser
import six
import pki
from pki.client import PKIConnection
import pki.system
import pki.util
from ipalib import api, errors, x509
from ipalib.install import certmonger
@@ -202,6 +204,18 @@ class DogtagInstance(service.Service):
"-f", cfg_file,
"--debug"]
# specify --log-file <path> on PKI 11.0.0 or later
pki_version = pki.util.Version(pki.specification_version())
if pki_version >= pki.util.Version("11.0.0"):
timestamp = time.strftime(
"%Y%m%d%H%M%S",
time.localtime(time.time()))
log_file = os.path.join(
paths.VAR_LOG_PKI_DIR,
"pki-%s-spawn.%s.log" % (self.subsystem.lower(), timestamp))
args.extend(["--log-file", log_file])
with open(cfg_file) as f:
logger.debug(
'Contents of pkispawn configuration file (%s):\n%s',
@@ -290,10 +304,25 @@ class DogtagInstance(service.Service):
if self.is_installed():
self.print_msg("Unconfiguring %s" % self.subsystem)
args = [paths.PKIDESTROY,
"-i", "pki-tomcat",
"-s", self.subsystem]
# specify --log-file <path> on PKI 11.0.0 or later
pki_version = pki.util.Version(pki.specification_version())
if pki_version >= pki.util.Version("11.0.0"):
timestamp = time.strftime(
"%Y%m%d%H%M%S",
time.localtime(time.time()))
log_file = os.path.join(
paths.VAR_LOG_PKI_DIR,
"pki-%s-destroy.%s.log" % (self.subsystem.lower(), timestamp))
args.extend(["--log-file", log_file])
try:
ipautil.run([paths.PKIDESTROY,
"-i", 'pki-tomcat',
"-s", self.subsystem])
ipautil.run(args)
except ipautil.CalledProcessError as e:
logger.critical("failed to uninstall %s instance %s",
self.subsystem, e)
-5
View File
@@ -432,11 +432,6 @@ class ServerInstallInterface(ServerCertificateInstallInterface,
"You cannot specify an --enable-compat option without the "
"--setup-adtrust option")
if self.netbios_name:
raise RuntimeError(
"You cannot specify a --netbios-name option without the "
"--setup-adtrust option")
if self.no_msdcs:
raise RuntimeError(
"You cannot specify a --no-msdcs option without the "
+7 -4
View File
@@ -443,6 +443,7 @@ def install_check(installer):
print(" * Configure KRA (dogtag) for secret management")
if options.setup_dns:
print(" * Configure DNS (bind)")
print(" * Configure SID generation")
if options.setup_adtrust:
print(" * Configure Samba (smb) and winbind for managing AD trusts")
if not options.no_pkinit:
@@ -703,8 +704,9 @@ def install_check(installer):
logger.debug('Starting Directory Server')
services.knownservices.dirsrv.start(instance_name)
if options.setup_adtrust:
adtrust.install_check(False, options, api)
# Always call adtrust.install_check
# if --setup-adtrust is not specified, only the SID part is executed
adtrust.install_check(False, options, api)
# installer needs to update hosts file when DNS subsystem will be
# installed or custom addresses are used
@@ -966,8 +968,9 @@ def install(installer):
if options.setup_dns:
dns.install(False, False, options)
if options.setup_adtrust:
adtrust.install(False, options, fstore, api)
# Always call adtrust installer to configure SID generation
# if --setup-adtrust is not specified, only the SID part is executed
adtrust.install(False, options, fstore, api)
# Set the admin user kerberos password
ds.change_admin_password(admin_password)
+6 -4
View File
@@ -1158,8 +1158,9 @@ def promote_check(installer):
# check addresses here, dns module is doing own check
no_matching_interface_for_ip_address_warning(config.ips)
if options.setup_adtrust:
adtrust.install_check(False, options, remote_api)
# Always call adtrust.install_check
# if --setup-adtrust is not specified, only the SID part is executed
adtrust.install_check(False, options, remote_api)
except errors.ACIError:
logger.debug("%s", traceback.format_exc())
@@ -1365,8 +1366,9 @@ def install(installer):
if options.setup_dns:
dns.install(False, True, options, api)
if options.setup_adtrust:
adtrust.install(False, options, fstore, api)
# Always call adtrust.install
# if --setup-adtrust is not specified, only the SID part is executed
adtrust.install(False, options, fstore, api)
if options.hidden_replica:
# Set services to hidden
+39
View File
@@ -358,6 +358,21 @@ def upgrade_adtrust_config():
else:
logger.warning("Error updating Samba registry: %s", e)
logger.info("[Change 'server role' from "
"'CLASSIC PRIMARY DOMAIN CONTROLLER' "
"to 'IPA PRIMARY DOMAIN CONTROLLER' in Samba configuration]")
args = [paths.NET, "conf", "setparm", "global",
"server role", "IPA PRIMARY DOMAIN CONTROLLER"]
try:
ipautil.run(args)
except ipautil.CalledProcessError as e:
# Only report an error if return code is not 255
# which indicates that the new server role is not supported
# and we don't need to do anything
if e.returncode != 255:
logger.warning("Error updating Samba registry: %s", e)
def ca_configure_profiles_acl(ca):
logger.info('[Authorizing RA Agent to modify profiles]')
@@ -1262,6 +1277,29 @@ def enable_server_snippet():
tasks.restore_context(paths.KRB5_FREEIPA_SERVER)
def setup_kpasswd_server(krb):
logger.info("[Setup kpasswd_server]")
aug = Augeas(
flags=Augeas.NO_LOAD | Augeas.NO_MODL_AUTOLOAD,
loadpath=paths.USR_SHARE_IPA_DIR,
)
try:
aug.transform("IPAKrb5", paths.KRB5_CONF)
aug.load()
kpass_srv_path = "/files{}/realms/{}/kpasswd_server"
kpass_srv_path = kpass_srv_path.format(paths.KRB5_CONF, krb.realm)
if aug.match(kpass_srv_path):
return
aug.set(kpass_srv_path, f"{krb.fqdn}:464")
aug.save()
finally:
aug.close()
def ntpd_cleanup(fqdn, fstore):
sstore = sysrestore.StateFile(paths.SYSRESTORE)
timeconf.restore_forced_timeservices(sstore, 'ntpd')
@@ -1871,6 +1909,7 @@ def upgrade_configuration():
setup_spake(krb)
setup_pkinit(krb)
enable_server_snippet()
setup_kpasswd_server(krb)
# Must be executed after certificate_renewal_update
# (see function docstring for details)
+97 -2
View File
@@ -17,12 +17,16 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import dbus
import dbus.mainloop.glib
import logging
from ipalib import api
from ipalib import Bool, Int, Str, IA5Str, StrEnum, DNParam
from ipalib import Bool, Int, Str, IA5Str, StrEnum, DNParam, Flag
from ipalib import errors
from ipalib.constants import MAXHOSTNAMELEN
from ipalib.plugable import Registry
from ipalib.request import context
from ipalib.util import validate_domain_name
from .baseldap import (
LDAPObject,
@@ -30,7 +34,12 @@ from .baseldap import (
LDAPRetrieve)
from .selinuxusermap import validate_selinuxuser
from ipalib import _
from ipapython.admintool import ScriptError
from ipapython.dn import DN
from ipaserver.plugins.privilege import principal_has_privilege
from ipaserver.install.adtrust import set_and_check_netbios_name
logger = logging.getLogger(__name__)
# 389-ds attributes that should be skipped in attribute checks
OPERATIONAL_ATTRIBUTES = ('nsaccountlock', 'member', 'memberof',
@@ -340,6 +349,24 @@ class config(LDAPObject):
doc=_('DNSec key master'),
flags={'virtual_attribute', 'no_create', 'no_update'}
),
Flag(
'enable_sid?',
label=_('Setup SID configuration'),
doc=_('New users and groups automatically get a SID assigned'),
flags={'virtual_attribute', 'no_create'}
),
Flag(
'add_sids?',
label=_('Add SIDs'),
doc=_('Add SIDs for existing users and groups'),
flags={'virtual_attribute', 'no_create'}
),
Str(
'netbios_name?',
label=_('NetBIOS name of the IPA domain'),
doc=_('NetBIOS name of the IPA domain'),
flags={'virtual_attribute', 'no_create'}
),
)
def get_dn(self, *keys, **kwargs):
@@ -479,6 +506,60 @@ class config(LDAPObject):
class config_mod(LDAPUpdate):
__doc__ = _('Modify configuration options.')
def _enable_sid(self, ldap, options):
# the user must have the Replication Administrators privilege
privilege = 'Replication Administrators'
if not principal_has_privilege(self.api, context.principal, privilege):
raise errors.ACIError(
info=_("not allowed to enable SID generation"))
# NetBIOS name is either taken from options or generated
try:
netbios_name, reset_netbios_name = set_and_check_netbios_name(
options.get('netbios_name', None), True, self.api)
except ScriptError:
raise errors.ValidationError(name="NetBIOS name",
error=_('Up to 15 characters and only uppercase ASCII letters'
', digits and dashes are allowed. Empty string is '
'not allowed.'))
_ret = 0
_stdout = ''
_stderr = ''
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
method_options = []
if options.get('add_sids', False):
method_options.extend(["--add-sids"])
method_options.extend(["--netbios-name", netbios_name])
if reset_netbios_name:
method_options.append("--reset-netbios-name")
# Dbus definition expects up to 10 arguments
method_options.extend([''] * (10 - len(method_options)))
try:
bus = dbus.SystemBus()
obj = bus.get_object('org.freeipa.server', '/',
follow_name_owner_changes=True)
server = dbus.Interface(obj, 'org.freeipa.server')
_ret, _stdout, _stderr = server.config_enable_sid(*method_options)
except dbus.DBusException as e:
logger.error('Failed to call org.freeipa.server.config_enable_sid.'
'DBus exception is %s', str(e))
raise errors.ExecutionError(message=_('Failed to call DBus'))
# The oddjob restarts dirsrv, we need to re-establish the conn
if self.api.Backend.ldap2.isconnected():
self.api.Backend.ldap2.disconnect()
self.api.Backend.ldap2.connect(ccache=context.ccache_name)
if _ret != 0:
logger.error("Helper config_enable_sid return code is %d", _ret)
raise errors.ExecutionError(
message=_('Configuration of SID failed. '
'See details in the error log'))
def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
assert isinstance(dn, DN)
if 'ipadefaultprimarygroup' in entry_attrs:
@@ -606,13 +687,27 @@ class config_mod(LDAPUpdate):
self.obj.validate_domain_resolution_order(entry_attrs)
# The options add_sids or netbios_name need the enable_sid option
for sid_option in ['add_sids', 'netbios_name']:
if options.get(sid_option, None) and not options['enable_sid']:
opt = "--" + sid_option.replace("_", "-")
error_message = _("You cannot specify %s without "
"the --enable-sid option" % opt)
raise errors.ValidationError(
name=opt,
error=error_message)
if options['enable_sid']:
self._enable_sid(ldap, options)
return dn
def exc_callback(self, keys, options, exc, call_func,
*call_args, **call_kwargs):
if (isinstance(exc, errors.EmptyModlist) and
call_func.__name__ == 'update_entry' and
'ca_renewal_master_server' in options):
('ca_renewal_master_server' in options or
'enable_sid' in options)):
return
super(config_mod, self).exc_callback(
+2 -1
View File
@@ -1832,7 +1832,8 @@ class ra(rabase.rabase, RestClient):
method='POST',
headers={'Accept-Encoding': 'gzip, deflate',
'User-Agent': 'IPA',
'Content-Type': 'application/xml'},
'Content-Type': 'application/xml',
'Accept': 'application/xml'},
body=payload
)
+2 -1
View File
@@ -922,7 +922,8 @@ migration process might be incomplete\n''')
# check whether the compat plugin is enabled
if not options.get('compat'):
try:
ldap.get_entry(DN(('cn', 'compat'), (api.env.basedn)))
ldap.get_entry(DN(('cn', 'users'), ('cn', 'compat'),
(api.env.basedn)))
return dict(result={}, failed={}, enabled=True, compat=False)
except errors.NotFound:
pass
+4 -1
View File
@@ -491,7 +491,10 @@ class pwpolicy(LDAPObject):
if minlife > maxlife:
raise errors.ValidationError(
name='maxlife',
error=_('Maximum password life must be greater than minimum.'),
error=_(
"Maximum password life must be equal to "
"or greater than the minimum."
),
)
def add_cospriority(self, entry, pwpolicy_name, rights=True):
+15 -12
View File
@@ -20,12 +20,6 @@ from ipalib.request import context
from ipalib.text import _
from ipapython.version import API_VERSION
# Schema TTL sent to clients in response to schema call.
# Number of seconds before client should check for schema update.
# This should be long enough to not slow down regular work or skripts
# but also short enough to ensure schema will be retvieved soon after
# it was updated
SCHEMA_TTL = 3600 # default: 1 hour
__doc__ = _("""
API Schema
@@ -129,8 +123,8 @@ class BaseMetaSearch(Search):
"(\"%s\")") % 'name',
)
def execute(self, command, criteria=None, **options):
result = list(self.obj.search(command, criteria, **options))
def execute(self, criteria=None, **options):
result = list(self.obj.search(criteria, **options))
return dict(result=result, count=len(result), truncated=False)
@@ -292,6 +286,13 @@ class command_defaults(PKQuery):
)
def execute(self, name, **options):
if name not in self.api.Command:
raise errors.NotFound(
reason=_("{oname}: {command_name} not found").format(
oname=self.name, command_name=name
)
)
command = self.api.Command[name]
params = options.get('params') or []
@@ -494,7 +495,9 @@ class BaseParamRetrieve(BaseParamMethod, BaseMetaRetrieve):
class BaseParamSearch(BaseParamMethod, BaseMetaSearch):
pass
def execute(self, command, criteria=None, **options):
result = list(self.obj.search(command, criteria, **options))
return dict(result=result, count=len(result), truncated=False)
@register()
@@ -592,7 +595,7 @@ class param(BaseParam):
obj[key] = unicode(value)
elif key in ('exclude',
'include'):
obj[key] = list(unicode(v) for v in value)
obj[key] = sorted(list(unicode(v) for v in value))
if isinstance(metaobj, Command):
if key == 'alwaysask':
obj.setdefault(key, value)
@@ -839,14 +842,14 @@ class schema(Command):
langs = "".join(getattr(context, "languages", []))
if getattr(self.api, "_schema", None) is None:
setattr(self.api, "_schema", {})
object.__setattr__(self.api, "_schema", {})
schema = self.api._schema.get(langs)
if schema is None:
schema = self._generate_schema(**kwargs)
self.api._schema[langs] = schema
schema['ttl'] = SCHEMA_TTL
schema['ttl'] = self.api.env.schema_ttl
if schema['fingerprint'] in kwargs.get('known_fingerprints', []):
raise errors.SchemaUpToDate(
+1 -2
View File
@@ -479,7 +479,7 @@ class server_del(LDAPDelete):
)
)
else:
raise errors.ServerRemovalError(reason=_(msg))
raise errors.ServerRemovalError(reason=msg)
ipa_config = self.api.Command.config_show()['result']
@@ -509,7 +509,6 @@ class server_del(LDAPDelete):
if self.api.Command.ca_is_enabled()['result']:
try:
roles = self.api.Command.server_role_find(
server_server=hostname,
role_servrole='KRA server',
status='enabled',
include_master=True,
+1
View File
@@ -524,6 +524,7 @@ class subid_match(subid_find):
osubuid = options["ipasubuidnumber"]
new_entries = []
for entry in entries:
self.obj.convert_owner(entry, options)
esubuid = int(entry.single_value["ipasubuidnumber"])
esubcount = int(entry.single_value["ipasubuidcount"])
minsubuid = esubuid
+5
View File
@@ -659,6 +659,10 @@ class user_add(baseuser_add):
entry_attrs.update(newentry)
# delete ipantsecurityidentifier if present
if ('ipantsecurityidentifier' in entry_attrs):
del entry_attrs['ipantsecurityidentifier']
if options.get('random', False):
try:
entry_attrs['randompassword'] = unicode(getattr(context, 'randompassword'))
@@ -1002,6 +1006,7 @@ class user_stage(LDAPMultiQuery):
u'ipauniqueid', u'krbcanonicalname',
u'sshpubkeyfp', u'krbextradata',
u'ipacertmapdata',
'ipantsecurityidentifier',
u'nsaccountlock']
def execute(self, *keys, **options):
+2
View File
@@ -383,6 +383,8 @@ class WSGIExecutioner(Executioner):
if self.api.env.debug:
time_start = time.perf_counter_ns()
try:
if 'KRB5CCNAME' in environ:
setattr(context, "ccache_name", environ['KRB5CCNAME'])
if ('HTTP_ACCEPT_LANGUAGE' in environ):
lang_reg_w_q = environ['HTTP_ACCEPT_LANGUAGE'].split(',')[0]
lang_reg = lang_reg_w_q.split(';')[0]
+338 -97
View File
@@ -1,6 +1,7 @@
{
"__defaultAction": "Change defaultAction to SCMP_ACT_LOG and then check Host's journal for SECCOMP",
"defaultAction": "SCMP_ACT_ERRNO",
"defaultAction": "SCMP_ACT_ERRNO",
"defaultErrnoRet": 38,
"archMap": [
{
"architecture": "SCMP_ARCH_X86_64",
@@ -53,6 +54,46 @@
"syscalls": [
{
"names": [
"bdflush",
"io_pgetevents",
"kexec_file_load",
"kexec_load",
"migrate_pages",
"move_pages",
"nfsservctl",
"nice",
"oldfstat",
"oldlstat",
"oldolduname",
"oldstat",
"olduname",
"pciconfig_iobase",
"pciconfig_read",
"pciconfig_write",
"sgetmask",
"ssetmask",
"swapcontext",
"swapoff",
"swapon",
"sysfs",
"uselib",
"userfaultfd",
"ustat",
"vm86",
"vm86old",
"vmsplice"
],
"action": "SCMP_ACT_ERRNO",
"args": [],
"comment": "",
"includes": {},
"excludes": {},
"errnoRet": 1
},
{
"names": [
"_llseek",
"_newselect",
"accept",
"accept4",
"access",
@@ -67,10 +108,17 @@
"chown",
"chown32",
"clock_adjtime",
"clock_adjtime64",
"clock_getres",
"clock_getres_time64",
"clock_gettime",
"clock_gettime64",
"clock_nanosleep",
"clock_nanosleep_time64",
"clone",
"clone3",
"close",
"close_range",
"connect",
"copy_file_range",
"creat",
@@ -82,6 +130,7 @@
"epoll_ctl",
"epoll_ctl_old",
"epoll_pwait",
"epoll_pwait2",
"epoll_wait",
"epoll_wait_old",
"eventfd",
@@ -110,7 +159,11 @@
"flock",
"fork",
"fremovexattr",
"fsconfig",
"fsetxattr",
"fsmount",
"fsopen",
"fspick",
"fstat",
"fstat64",
"fstatat64",
@@ -120,7 +173,10 @@
"ftruncate",
"ftruncate64",
"futex",
"futex_time64",
"futimesat",
"get_robust_list",
"get_thread_area",
"getcpu",
"getcwd",
"getdents",
@@ -134,6 +190,7 @@
"getgroups",
"getgroups32",
"getitimer",
"get_mempolicy",
"getpeername",
"getpgid",
"getpgrp",
@@ -146,12 +203,10 @@
"getresuid",
"getresuid32",
"getrlimit",
"get_robust_list",
"getrusage",
"getsid",
"getsockname",
"getsockopt",
"get_thread_area",
"gettid",
"gettimeofday",
"getuid",
@@ -162,14 +217,15 @@
"inotify_init1",
"inotify_rm_watch",
"io_cancel",
"ioctl",
"io_destroy",
"io_getevents",
"ioprio_get",
"ioprio_set",
"io_setup",
"io_submit",
"ioctl",
"ioprio_get",
"ioprio_set",
"ipc",
"keyctl",
"kill",
"lchown",
"lchown32",
@@ -179,14 +235,15 @@
"listen",
"listxattr",
"llistxattr",
"_llseek",
"lremovexattr",
"lseek",
"lsetxattr",
"lstat",
"lstat64",
"madvise",
"mbind",
"memfd_create",
"memfd_secret",
"mincore",
"mkdir",
"mkdirat",
@@ -197,12 +254,16 @@
"mlockall",
"mmap",
"mmap2",
"mount",
"move_mount",
"mprotect",
"mq_getsetattr",
"mq_notify",
"mq_open",
"mq_timedreceive",
"mq_timedreceive_time64",
"mq_timedsend",
"mq_timedsend_time64",
"mq_unlink",
"mremap",
"msgctl",
@@ -213,33 +274,47 @@
"munlock",
"munlockall",
"munmap",
"name_to_handle_at",
"nanosleep",
"newfstatat",
"_newselect",
"open",
"openat",
"openat2",
"open_tree",
"pause",
"pidfd_getfd",
"pidfd_open",
"pidfd_send_signal",
"pipe",
"pipe2",
"pivot_root",
"pkey_alloc",
"pkey_free",
"pkey_mprotect",
"poll",
"ppoll",
"ppoll_time64",
"prctl",
"pread64",
"preadv",
"preadv2",
"prlimit64",
"pselect6",
"pselect6_time64",
"pwrite64",
"pwritev",
"pwritev2",
"read",
"readahead",
"readdir",
"readlink",
"readlinkat",
"readv",
"reboot",
"recv",
"recvfrom",
"recvmmsg",
"recvmmsg_time64",
"recvmsg",
"remap_file_pages",
"removexattr",
@@ -248,6 +323,7 @@
"renameat2",
"restart_syscall",
"rmdir",
"rseq",
"rt_sigaction",
"rt_sigpending",
"rt_sigprocmask",
@@ -255,14 +331,16 @@
"rt_sigreturn",
"rt_sigsuspend",
"rt_sigtimedwait",
"rt_sigtimedwait_time64",
"rt_tgsigqueueinfo",
"sched_get_priority_max",
"sched_get_priority_min",
"sched_getaffinity",
"sched_getattr",
"sched_getparam",
"sched_get_priority_max",
"sched_get_priority_min",
"sched_getscheduler",
"sched_rr_get_interval",
"sched_rr_get_interval_time64",
"sched_setaffinity",
"sched_setattr",
"sched_setparam",
@@ -274,12 +352,18 @@
"semget",
"semop",
"semtimedop",
"semtimedop_time64",
"send",
"sendfile",
"sendfile64",
"sendmmsg",
"sendmsg",
"sendto",
"setns",
"set_mempolicy",
"set_robust_list",
"set_thread_area",
"set_tid_address",
"setfsgid",
"setfsgid32",
"setfsuid",
@@ -300,11 +384,8 @@
"setreuid",
"setreuid32",
"setrlimit",
"set_robust_list",
"setsid",
"setsockopt",
"set_thread_area",
"set_tid_address",
"setuid",
"setuid32",
"setxattr",
@@ -317,7 +398,6 @@
"signalfd",
"signalfd4",
"sigreturn",
"socket",
"socketcall",
"socketpair",
"splice",
@@ -332,41 +412,44 @@
"sync_file_range",
"syncfs",
"sysinfo",
"syslog",
"tee",
"tgkill",
"time",
"timer_create",
"timer_delete",
"timerfd_create",
"timerfd_gettime",
"timerfd_settime",
"timer_getoverrun",
"timer_gettime",
"timer_gettime64",
"timer_settime",
"timer_settime64",
"timerfd_create",
"timerfd_gettime",
"timerfd_gettime64",
"timerfd_settime",
"timerfd_settime64",
"times",
"tkill",
"truncate",
"truncate64",
"ugetrlimit",
"umask",
"umount",
"umount2",
"uname",
"unlink",
"unlinkat",
"unshare",
"utime",
"utimensat",
"utimensat_time64",
"utimes",
"vfork",
"vmsplice",
"wait4",
"waitid",
"waitpid",
"write",
"writev",
"mount",
"umount2",
"reboot",
"name_to_handle_at",
"unshare"
"writev"
],
"action": "SCMP_ACT_ALLOW",
"args": [],
@@ -555,23 +638,31 @@
},
"excludes": {}
},
{
"names": [
"open_by_handle_at"
],
"action": "SCMP_ACT_ERRNO",
"args": [],
"comment": "",
"includes": {},
"excludes": {
"caps": [
"CAP_DAC_READ_SEARCH"
]
},
"errnoRet": 1
},
{
"names": [
"bpf",
"clone",
"fanotify_init",
"lookup_dcookie",
"mount",
"name_to_handle_at",
"perf_event_open",
"quotactl",
"setdomainname",
"sethostname",
"setns",
"syslog",
"umount",
"umount2",
"unshare"
"setns"
],
"action": "SCMP_ACT_ALLOW",
"args": [],
@@ -585,68 +676,25 @@
},
{
"names": [
"clone"
],
"action": "SCMP_ACT_ALLOW",
"args": [
{
"index": 0,
"value": 2080505856,
"valueTwo": 0,
"op": "SCMP_CMP_MASKED_EQ"
}
"bpf",
"fanotify_init",
"lookup_dcookie",
"perf_event_open",
"quotactl",
"setdomainname",
"sethostname",
"setns"
],
"action": "SCMP_ACT_ERRNO",
"args": [],
"comment": "",
"includes": {},
"excludes": {
"caps": [
"CAP_SYS_ADMIN"
],
"arches": [
"s390",
"s390x"
]
}
},
{
"names": [
"clone"
],
"action": "SCMP_ACT_ALLOW",
"args": [
{
"index": 1,
"value": 2080505856,
"valueTwo": 0,
"op": "SCMP_CMP_MASKED_EQ"
}
],
"comment": "s390 parameter ordering for clone is different",
"includes": {
"arches": [
"s390",
"s390x"
]
},
"excludes": {
"caps": [
"CAP_SYS_ADMIN"
]
}
},
{
"names": [
"reboot"
],
"action": "SCMP_ACT_ALLOW",
"args": [],
"comment": "",
"includes": {
"caps": [
"CAP_SYS_BOOT"
]
},
"excludes": {}
"errnoRet": 1
},
{
"names": [
@@ -662,6 +710,21 @@
},
"excludes": {}
},
{
"names": [
"chroot"
],
"action": "SCMP_ACT_ERRNO",
"args": [],
"comment": "",
"includes": {},
"excludes": {
"caps": [
"CAP_SYS_CHROOT"
]
},
"errnoRet": 1
},
{
"names": [
"delete_module",
@@ -679,6 +742,24 @@
},
"excludes": {}
},
{
"names": [
"delete_module",
"init_module",
"finit_module",
"query_module"
],
"action": "SCMP_ACT_ERRNO",
"args": [],
"comment": "",
"includes": {},
"excludes": {
"caps": [
"CAP_SYS_MODULE"
]
},
"errnoRet": 1
},
{
"names": [
"acct"
@@ -693,9 +774,25 @@
},
"excludes": {}
},
{
"names": [
"acct"
],
"action": "SCMP_ACT_ERRNO",
"args": [],
"comment": "",
"includes": {},
"excludes": {
"caps": [
"CAP_SYS_PACCT"
]
},
"errnoRet": 1
},
{
"names": [
"kcmp",
"process_madvise",
"process_vm_readv",
"process_vm_writev",
"ptrace"
@@ -710,6 +807,25 @@
},
"excludes": {}
},
{
"names": [
"kcmp",
"process_madvise",
"process_vm_readv",
"process_vm_writev",
"ptrace"
],
"action": "SCMP_ACT_ERRNO",
"args": [],
"comment": "",
"includes": {},
"excludes": {
"caps": [
"CAP_SYS_PTRACE"
]
},
"errnoRet": 1
},
{
"names": [
"iopl",
@@ -725,11 +841,28 @@
},
"excludes": {}
},
{
"names": [
"iopl",
"ioperm"
],
"action": "SCMP_ACT_ERRNO",
"args": [],
"comment": "",
"includes": {},
"excludes": {
"caps": [
"CAP_SYS_RAWIO"
]
},
"errnoRet": 1
},
{
"names": [
"settimeofday",
"stime",
"clock_settime"
"clock_settime",
"clock_settime64"
],
"action": "SCMP_ACT_ALLOW",
"args": [],
@@ -741,6 +874,24 @@
},
"excludes": {}
},
{
"names": [
"settimeofday",
"stime",
"clock_settime",
"clock_settime64"
],
"action": "SCMP_ACT_ERRNO",
"args": [],
"comment": "",
"includes": {},
"excludes": {
"caps": [
"CAP_SYS_TIME"
]
},
"errnoRet": 1
},
{
"names": [
"vhangup"
@@ -757,30 +908,120 @@
},
{
"names": [
"get_mempolicy",
"mbind",
"set_mempolicy"
"vhangup"
],
"action": "SCMP_ACT_ALLOW",
"action": "SCMP_ACT_ERRNO",
"args": [],
"comment": "",
"includes": {
"includes": {},
"excludes": {
"caps": [
"CAP_SYS_NICE"
"CAP_SYS_TTY_CONFIG"
]
},
"excludes": {}
"errnoRet": 1
},
{
"names": [
"syslog"
"socket"
],
"action": "SCMP_ACT_ERRNO",
"args": [
{
"index": 0,
"value": 16,
"valueTwo": 0,
"op": "SCMP_CMP_EQ"
},
{
"index": 2,
"value": 9,
"valueTwo": 0,
"op": "SCMP_CMP_EQ"
}
],
"comment": "",
"includes": {},
"excludes": {
"caps": [
"CAP_AUDIT_WRITE"
]
},
"errnoRet": 22
},
{
"names": [
"socket"
],
"action": "SCMP_ACT_ALLOW",
"args": [],
"args": [
{
"index": 2,
"value": 9,
"valueTwo": 0,
"op": "SCMP_CMP_NE"
}
],
"comment": "",
"includes": {},
"excludes": {
"caps": [
"CAP_AUDIT_WRITE"
]
}
},
{
"names": [
"socket"
],
"action": "SCMP_ACT_ALLOW",
"args": [
{
"index": 0,
"value": 16,
"valueTwo": 0,
"op": "SCMP_CMP_NE"
}
],
"comment": "",
"includes": {},
"excludes": {
"caps": [
"CAP_AUDIT_WRITE"
]
}
},
{
"names": [
"socket"
],
"action": "SCMP_ACT_ALLOW",
"args": [
{
"index": 2,
"value": 9,
"valueTwo": 0,
"op": "SCMP_CMP_NE"
}
],
"comment": "",
"includes": {},
"excludes": {
"caps": [
"CAP_AUDIT_WRITE"
]
}
},
{
"names": [
"socket"
],
"action": "SCMP_ACT_ALLOW",
"args": null,
"comment": "",
"includes": {
"caps": [
"CAP_SYSLOG"
"CAP_AUDIT_WRITE"
]
},
"excludes": {}
+12 -8
View File
@@ -20,12 +20,6 @@ jobs:
steps:
- template: templates/${{ variables.PREPARE_BUILD_TEMPLATE }}
- template: templates/${{ variables.AUTOCONF_TEMPLATE }}
- script: |
set -e
git update-ref refs/heads/$(System.PullRequest.TargetBranch) origin/$(System.PullRequest.TargetBranch)
make V=0 "GIT_BRANCH=$(System.PullRequest.TargetBranch)" fastlint
displayName: Quick code style check
condition: eq(variables['Build.Reason'], 'PullRequest')
- template: templates/${{ variables.BUILD_TEMPLATE }}
- template: templates/publish-build.yml
parameters:
@@ -75,6 +69,12 @@ jobs:
echo "Running make target 'lint'"
make V=0 lint
displayName: Lint sources
- script: |
set -e
git update-ref refs/heads/$(System.PullRequest.TargetBranch) origin/$(System.PullRequest.TargetBranch)
make V=0 "GIT_BRANCH=$(System.PullRequest.TargetBranch)" fastcodestyle
displayName: Quick code style check
condition: eq(variables['Build.Reason'], 'PullRequest')
- job: Docs
pool:
@@ -144,7 +144,9 @@ jobs:
- job: BASE_XMLRPC
pool:
vmImage: $(VM_IMAGE)
dependsOn: Build
dependsOn:
- Build
- Lint
variables:
IPA_IMAGE_ARTIFACT: $[ dependencies.Build.outputs['artifacts_image.image'] ]
IPA_PACKAGES_ARTIFACT: $[ dependencies.Build.outputs['artifacts_packages.packages'] ]
@@ -158,7 +160,9 @@ jobs:
- job: GATING
pool:
vmImage: $(VM_IMAGE)
dependsOn: Build
dependsOn:
- Build
- Lint
variables:
IPA_IMAGE_ARTIFACT: $[ dependencies.Build.outputs['artifacts_image.image'] ]
IPA_PACKAGES_ARTIFACT: $[ dependencies.Build.outputs['artifacts_packages.packages'] ]
@@ -22,7 +22,8 @@ function collect_logs() {
/var/log/pki \
/var/log/samba \
"$BIND_DATADIR" \
systemd_journal.log
systemd_journal.log \
||:
}
server_password=Secret123
@@ -95,6 +96,8 @@ if [ "$install_result" -eq 0 ] ; then
else
echo "ipa-server-install failed with code ${install_result}, skip IPA tests"
fi
# let the services gracefully flush their logs
ipactl stop ||:
collect_logs ipaserver_install_logs.tar.gz
echo "Potential Python 3 incompatibilities in the IPA framework:"
+2 -2
View File
@@ -30,8 +30,8 @@ jobs:
git_repo: '{git_repo}'
git_refspec: '{git_refspec}'
template: &ci-ipa-4-9-latest
name: freeipa/ci-ipa-4-9-f34
version: 0.0.3
name: freeipa/ci-ipa-4-9-f35
version: 0.0.1
timeout: 1800
topology: *build
@@ -50,8 +50,8 @@ jobs:
git_repo: '{git_repo}'
git_refspec: '{git_refspec}'
template: &ci-ipa-4-9-latest
name: freeipa/ci-ipa-4-9-f34
version: 0.0.3
name: freeipa/ci-ipa-4-9-f35
version: 0.0.1
timeout: 1800
topology: *build
@@ -1699,7 +1699,7 @@ jobs:
build_url: '{fedora-latest-ipa-4-9/build_url}'
test_suite: test_integration/test_ipa_cert_fix.py
template: *ci-ipa-4-9-latest
timeout: 7200
timeout: 10800
topology: *master_1repl
fedora-latest-ipa-4-9/test_subids:
@@ -50,8 +50,8 @@ jobs:
git_repo: '{git_repo}'
git_refspec: '{git_refspec}'
template: &ci-ipa-4-9-latest
name: freeipa/ci-ipa-4-9-f34
version: 0.0.3
name: freeipa/ci-ipa-4-9-f35
version: 0.0.1
timeout: 1800
topology: *build
@@ -1821,7 +1821,7 @@ jobs:
selinux_enforcing: True
test_suite: test_integration/test_ipa_cert_fix.py
template: *ci-ipa-4-9-latest
timeout: 7200
timeout: 10800
topology: *master_1repl
fedora-latest-ipa-4-9/test_subids:
@@ -50,8 +50,8 @@ jobs:
git_repo: '{git_repo}'
git_refspec: '{git_refspec}'
template: &ci-ipa-4-9-previous
name: freeipa/ci-ipa-4-9-f33
version: 0.0.7
name: freeipa/ci-ipa-4-9-f34
version: 0.0.4
timeout: 1800
topology: *build
@@ -1699,7 +1699,7 @@ jobs:
build_url: '{fedora-previous-ipa-4-9/build_url}'
test_suite: test_integration/test_ipa_cert_fix.py
template: *ci-ipa-4-9-previous
timeout: 7200
timeout: 10800
topology: *master_1repl
fedora-previous-ipa-4-9/test_subids:
+2 -2
View File
@@ -56,8 +56,8 @@ jobs:
git_repo: '{git_repo}'
git_refspec: '{git_refspec}'
template: &ci-ipa-4-9-latest
name: freeipa/ci-ipa-4-9-f34
version: 0.0.3
name: freeipa/ci-ipa-4-9-f35
version: 0.0.1
timeout: 1800
topology: *build
+25 -10
View File
@@ -764,7 +764,13 @@ def remove_trust_info_from_ad(master, ad_domain, ad_hostname):
kinit_as_user(master,
'Administrator@{}'.format(ad_domain.upper()),
master.config.ad_admin_password)
master.run_command(['rpcclient', '-k', ad_hostname,
# Detect whether rpcclient supports -k or --use-kerberos option
res = master.run_command(['rpcclient', '-h'], raiseonerr=False)
if "--use-kerberos" in res.stderr_text:
rpcclient_krb5_knob = "--use-kerberos=desired"
else:
rpcclient_krb5_knob = "-k"
master.run_command(['rpcclient', rpcclient_krb5_knob, ad_hostname,
'-c', 'deletetrustdom {}'.format(master.domain.name)],
raiseonerr=False)
@@ -2093,13 +2099,15 @@ def ldapsearch_dm(host, base, ldap_args, scope='sub', **kwargs):
return host.run_command(args, **kwargs)
def create_temp_file(host, directory=None, create_file=True):
"""Creates temporary file using mktemp."""
def create_temp_file(host, directory=None, suffix=None, create_file=True):
"""Creates temporary file using mktemp. See `man 1 mktemp`."""
cmd = ['mktemp']
if create_file is False:
cmd += ['--dry-run']
if directory is not None:
cmd += ['-p', directory]
if suffix is not None:
cmd.extend(["--suffix", suffix])
return host.run_command(cmd).stdout_text.strip()
@@ -2121,7 +2129,7 @@ def create_active_user(host, login, password, first='test', last='user',
# Note raiseonerr=False:
# the assert is located after kdcinfo retrieval.
result = host.run_command(
"KRB5_TRACE=/dev/stdout kinit %s" % login,
f"KRB5_TRACE=/dev/stdout SSSD_KRB5_LOCATOR_DEBUG=1 kinit {login}",
stdin_text='{0}\n{1}\n{1}\n'.format(
temp_password, password
), raiseonerr=False
@@ -2158,8 +2166,8 @@ def run_command_as_user(host, user, command, *args, **kwargs):
def kinit_as_user(host, user, password, krb5_trace=False, raiseonerr=True):
"""Launch kinit as user on host.
If krb5_trace, then set KRB5_TRACE=/dev/stdout and collect
/var/lib/sss/pubconf/kdcinfo.$REALM
If krb5_trace, then set KRB5_TRACE=/dev/stdout, SSSD_KRB5_LOCATOR_DEBUG=1
and collect /var/lib/sss/pubconf/kdcinfo.$REALM
as this file contains the list of KRB5KDC IPs SSSD uses.
https://pagure.io/freeipa/issue/8510
"""
@@ -2175,7 +2183,7 @@ def kinit_as_user(host, user, password, krb5_trace=False, raiseonerr=True):
# Note raiseonerr=False:
# the assert is located after kdcinfo retrieval.
result = host.run_command(
"KRB5_TRACE=/dev/stdout kinit %s" % user,
f"KRB5_TRACE=/dev/stdout SSSD_KRB5_LOCATOR_DEBUG=1 kinit {user}",
stdin_text='{0}\n'.format(password),
raiseonerr=False
)
@@ -2761,8 +2769,11 @@ def run_ssh_cmd(
if auth_method == "password":
if expect_auth_success is True:
assert "Authentication succeeded (keyboard-interactive)" in \
stderr
patterns = [
r'Authenticated to .* using "keyboard-interactive"',
r'Authentication succeeded \(keyboard-interactive\)'
]
assert any(re.search(pattern, stderr) for pattern in patterns)
# do not assert the return code:
# it can be >0 if the command failed.
elif expect_auth_failure is True:
@@ -2771,7 +2782,11 @@ def run_ssh_cmd(
assert "Authentication succeeded" not in stderr
elif auth_method == "key":
if expect_auth_success is True:
assert "Authentication succeeded (publickey)" in stderr
patterns = [
r'Authenticated to .* using "publickey"',
r'Authentication succeeded \(publickey\)'
]
assert any(re.search(pattern, stderr) for pattern in patterns)
# do not assert the return code:
# it can be >0 if the command failed.
elif expect_auth_failure is True:
+80
View File
@@ -0,0 +1,80 @@
#
# Copyright (C) 2021 FreeIPA Contributors see COPYING for license
#
import pytest
import time
from ipaclient.remote_plugins import ServerInfo
class TestServerInfo(ServerInfo):
"""Simplified ServerInfo class with hardcoded values"""
def __init__(self, fingerprint='deadbeef', hostname='ipa.example.test',
force_check=False, language='en_US',
version='2.0', expiration=None):
self._force_check = force_check
self._language = language
self._now = time.time()
self._dict = {
'fingerprint': fingerprint,
'expiration': expiration or time.time() + 3600,
'language': language,
'version': version,
}
def _read(self):
"""Running on test controller, this is a no-op"""
def _write(self):
"""Running on test controller, this is a no-op"""
@pytest.mark.tier0
class TestIPAServerInfo:
"""Test that ServerInfo detects changes in remote configuration"""
def test_valid(self):
server_info = TestServerInfo()
assert server_info.is_valid() is True
def test_force_check(self):
server_info = TestServerInfo(force_check=True)
assert server_info.is_valid() is False
def test_language_change(self):
server_info = TestServerInfo()
assert server_info.is_valid() is True
server_info._language = 'fr_FR'
assert server_info.is_valid() is False
server_info._language = 'en_US'
def test_expired(self):
server_info = TestServerInfo(expiration=time.time() + 2)
assert server_info.is_valid() is True
# skip past the expiration time
server_info._now = time.time() + 5
assert server_info.is_valid() is False
# set a new expiration time in the future
server_info.update_validity(10)
assert server_info.is_valid() is True
# move to the future beyond expiration
server_info._now = time.time() + 15
assert server_info.is_valid() is False
def test_update_validity(self):
server_info = TestServerInfo(expiration=time.time() + 1)
# Expiration and time are one second off so the cache is ok
assert server_info.is_valid() is True
# Simulate time passing by
server_info._now = time.time() + 2
# the validity should be updated because it is now expired
server_info.update_validity(3600)
# the cache is now valid for another hour
assert server_info.is_valid() is True
+1 -3
View File
@@ -585,7 +585,7 @@ class TestACMERenew(IntegrationTest):
# Note raiseonerr=False:
# the assert is located after kdcinfo retrieval.
result = host.run_command(
"KRB5_TRACE=/dev/stdout kinit %s" % 'admin',
"KRB5_TRACE=/dev/stdout kinit admin",
stdin_text='{0}\n{0}\n{0}\n'.format(
self.clients[0].config.admin_password
),
@@ -619,8 +619,6 @@ class TestACMERenew(IntegrationTest):
cert = x509.load_pem_x509_certificate(data, backend=default_backend())
initial_expiry = cert.not_valid_after
tasks.kinit_admin(self.clients[0])
self.clients[0].run_command(['certbot', 'renew'])
data = self.clients[0].get_file_contents(
@@ -311,6 +311,11 @@ class BaseBackupAndRestoreWithDNS(IntegrationTest):
tasks.install_master(self.master, setup_dns=True)
self.master.run_command(['ipa-restore', backup_path],
stdin_text=dirman_password + '\nyes')
if reinstall:
# If the server was reinstalled, reinstall may have changed
# the uid and restore reverts to the original value.
# clear the cache to make sure we get up-to-date values
tasks.clear_sssd_cache(self.master)
tasks.resolve_record(self.master.ip, self.example_test_zone)
tasks.kinit_admin(self.master)
@@ -380,6 +385,12 @@ class BaseBackupAndRestoreWithDNSSEC(IntegrationTest):
self.master.run_command(['ipa-restore', backup_path],
stdin_text=dirman_password + '\nyes')
if reinstall:
# If the server was reinstalled, reinstall may have changed
# the uid and restore reverts to the original value.
# clear the cache to make sure we get up-to-date values
tasks.clear_sssd_cache(self.master)
assert (
wait_until_record_is_signed(
self.master.ip, self.example_test_zone)
@@ -464,6 +475,12 @@ class BaseBackupAndRestoreWithKRA(IntegrationTest):
self.master.run_command(['ipa-restore', backup_path],
stdin_text=dirman_password + '\nyes')
if reinstall:
# If the server was reinstalled, reinstall may have changed
# the uid and restore reverts to the original value.
# clear the cache to make sure we get up-to-date values
tasks.clear_sssd_cache(self.master)
tasks.kinit_admin(self.master)
# retrieve secret after restore
self.master.run_command([
+1
View File
@@ -60,6 +60,7 @@ def get_install_stdin(cert_passwords=()):
'', # Server host name (has default)
]
lines.extend(cert_passwords) # Enter foo.p12 unlock password
lines.extend('IPA') # NetBios name
lines += [
'no', # configure chrony with NTP server or pool address?
'yes', # Continue with these values?
+68 -1
View File
@@ -16,6 +16,7 @@ import string
import time
from ipaplatform.paths import paths
from ipapython.dn import DN
from cryptography import x509
from cryptography.x509.oid import ExtensionOID
from cryptography.hazmat.backends import default_backend
@@ -69,7 +70,7 @@ class TestInstallMasterClient(IntegrationTest):
# time to look into journal logs in
# test_certmonger_ipa_responder_jsonrpc
cls.since = time.strftime('%H:%M:%S')
cls.since = time.strftime('%Y-%m-%d %H:%M:%S')
def test_cacert_file_appear_with_option_F(self):
"""Test if getcert creates cacert file with -F option
@@ -183,6 +184,34 @@ class TestInstallMasterClient(IntegrationTest):
)
assert "profile: caServerCert" in result.stdout_text
def test_multiple_user_certificates(self):
"""Test that a user may be issued multiple certificates"""
ldap = self.master.ldap_connect()
user = 'user1'
tasks.kinit_admin(self.master)
tasks.user_add(self.master, user)
for id in (0,1):
csr_file = f'{id}.csr'
key_file = f'{id}.key'
cert_file = f'{id}.crt'
openssl_cmd = [
'openssl', 'req', '-newkey', 'rsa:2048', '-keyout', key_file,
'-nodes', '-out', csr_file, '-subj', '/CN=' + user]
self.master.run_command(openssl_cmd)
cmd_args = ['ipa', 'cert-request', '--principal', user,
'--certificate-out', cert_file, csr_file]
self.master.run_command(cmd_args)
# easier to count by pulling the LDAP entry
entry = ldap.get_entry(DN(('uid', user), ('cn', 'users'),
('cn', 'accounts'), self.master.domain.basedn))
assert len(entry.get('usercertificate')) == 2
@pytest.fixture
def test_subca_certs(self):
"""
@@ -517,3 +546,41 @@ class TestCertmongerInterruption(IntegrationTest):
assert ca_error is None
assert state == 'CA_WORKING'
class TestCAShowErrorHandling(IntegrationTest):
num_replicas = 1
@classmethod
def install(cls, mh):
tasks.install_master(cls.master)
tasks.install_replica(cls.master, cls.replicas[0])
def test_ca_show_error_handling(self):
"""
Test to verify if the case of a request
for /ca/rest/authority/{id}/cert (or .../chain)
where {id} is an unknown authority ID.
Test Steps:
1. Setup a freeipa server and a replica
2. Stop ipa-custodia service on replica
3. Create a LWCA on the replica
4. Verify LWCA is recognized on the server
5. Run `ipa ca-show <LWCA>`
PKI Github Link: https://github.com/dogtagpki/pki/pull/3605/
"""
self.replicas[0].run_command(['systemctl', 'stop', 'ipa-custodia'])
lwca = 'lwca1'
result = self.replicas[0].run_command([
'ipa', 'ca-add', lwca, '--subject', 'CN=LWCA 1'
])
assert 'Created CA "{}"'.format(lwca) in result.stdout_text
result = self.master.run_command(['ipa', 'ca-find'])
assert 'Name: {}'.format(lwca) in result.stdout_text
result = self.master.run_command(
['ipa', 'ca-show', lwca, ],
raiseonerr=False
)
error_msg = 'ipa: ERROR: The certificate for ' \
'{} is not available on this server.'.format(lwca)
assert error_msg in result.stderr_text
+78 -37
View File
@@ -84,43 +84,41 @@ isrgrootx1 = (
)
isrgrootx1_nick = 'CN=ISRG Root X1,O=Internet Security Research Group,C=US'
# This sub-CA expires on Oct 6, 2021 but it is functional for our
# purposes of testing, the date validity is not considered (yet).
letsencryptauthorityx3 = (
# This sub-CA expires on Sep 15, 2025 and will need to be replaced
# after this date. Otherwise TestIPACommand::test_cacert_manage fails.
letsencryptauthorityr3 = (
b'-----BEGIN CERTIFICATE-----\n'
b'MIIFjTCCA3WgAwIBAgIRANOxciY0IzLc9AUoUSrsnGowDQYJKoZIhvcNAQELBQAw\n'
b'MIIFFjCCAv6gAwIBAgIRAJErCErPDBinU/bWLiWnX1owDQYJKoZIhvcNAQELBQAw\n'
b'TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\n'
b'cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTYxMDA2MTU0MzU1\n'
b'WhcNMjExMDA2MTU0MzU1WjBKMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg\n'
b'RW5jcnlwdDEjMCEGA1UEAxMaTGV0J3MgRW5jcnlwdCBBdXRob3JpdHkgWDMwggEi\n'
b'MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCc0wzwWuUuR7dyXTeDs2hjMOrX\n'
b'NSYZJeG9vjXxcJIvt7hLQQWrqZ41CFjssSrEaIcLo+N15Obzp2JxunmBYB/XkZqf\n'
b'89B4Z3HIaQ6Vkc/+5pnpYDxIzH7KTXcSJJ1HG1rrueweNwAcnKx7pwXqzkrrvUHl\n'
b'Npi5y/1tPJZo3yMqQpAMhnRnyH+lmrhSYRQTP2XpgofL2/oOVvaGifOFP5eGr7Dc\n'
b'Gu9rDZUWfcQroGWymQQ2dYBrrErzG5BJeC+ilk8qICUpBMZ0wNAxzY8xOJUWuqgz\n'
b'uEPxsR/DMH+ieTETPS02+OP88jNquTkxxa/EjQ0dZBYzqvqEKbbUC8DYfcOTAgMB\n'
b'AAGjggFnMIIBYzAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADBU\n'
b'BgNVHSAETTBLMAgGBmeBDAECATA/BgsrBgEEAYLfEwEBATAwMC4GCCsGAQUFBwIB\n'
b'FiJodHRwOi8vY3BzLnJvb3QteDEubGV0c2VuY3J5cHQub3JnMB0GA1UdDgQWBBSo\n'
b'SmpjBH3duubRObemRWXv86jsoTAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8vY3Js\n'
b'LnJvb3QteDEubGV0c2VuY3J5cHQub3JnMHIGCCsGAQUFBwEBBGYwZDAwBggrBgEF\n'
b'BQcwAYYkaHR0cDovL29jc3Aucm9vdC14MS5sZXRzZW5jcnlwdC5vcmcvMDAGCCsG\n'
b'AQUFBzAChiRodHRwOi8vY2VydC5yb290LXgxLmxldHNlbmNyeXB0Lm9yZy8wHwYD\n'
b'VR0jBBgwFoAUebRZ5nu25eQBc4AIiMgaWPbpm24wDQYJKoZIhvcNAQELBQADggIB\n'
b'ABnPdSA0LTqmRf/Q1eaM2jLonG4bQdEnqOJQ8nCqxOeTRrToEKtwT++36gTSlBGx\n'
b'A/5dut82jJQ2jxN8RI8L9QFXrWi4xXnA2EqA10yjHiR6H9cj6MFiOnb5In1eWsRM\n'
b'UM2v3e9tNsCAgBukPHAg1lQh07rvFKm/Bz9BCjaxorALINUfZ9DD64j2igLIxle2\n'
b'DPxW8dI/F2loHMjXZjqG8RkqZUdoxtID5+90FgsGIfkMpqgRS05f4zPbCEHqCXl1\n'
b'eO5HyELTgcVlLXXQDgAWnRzut1hFJeczY1tjQQno6f6s+nMydLN26WuU4s3UYvOu\n'
b'OsUxRlJu7TSRHqDC3lSE5XggVkzdaPkuKGQbGpny+01/47hfXXNB7HntWNZ6N2Vw\n'
b'p7G6OfY+YQrZwIaQmhrIqJZuigsrbe3W+gdn5ykE9+Ky0VgVUsfxo52mwFYs1JKY\n'
b'2PGDuWx8M6DlS6qQkvHaRUo0FMd8TsSlbF0/v965qGFKhSDeQoMpYnwcmQilRh/0\n'
b'ayLThlHLN81gSkJjVrPI0Y8xCVPB4twb1PFUd2fPM3sA1tJ83sZ5v8vgFv2yofKR\n'
b'PB0t6JzUA81mSqM3kxl5e+IZwhYAyO0OTg3/fs8HqGTNKd9BqoUwSRBzp06JMg5b\n'
b'rUCGwbCUDI0mxadJ3Bz4WxR6fyNpBK2yAinWEsikxqEt\n'
b'cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMjAwOTA0MDAwMDAw\n'
b'WhcNMjUwOTE1MTYwMDAwWjAyMQswCQYDVQQGEwJVUzEWMBQGA1UEChMNTGV0J3Mg\n'
b'RW5jcnlwdDELMAkGA1UEAxMCUjMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK\n'
b'AoIBAQC7AhUozPaglNMPEuyNVZLD+ILxmaZ6QoinXSaqtSu5xUyxr45r+XXIo9cP\n'
b'R5QUVTVXjJ6oojkZ9YI8QqlObvU7wy7bjcCwXPNZOOftz2nwWgsbvsCUJCWH+jdx\n'
b'sxPnHKzhm+/b5DtFUkWWqcFTzjTIUu61ru2P3mBw4qVUq7ZtDpelQDRrK9O8Zutm\n'
b'NHz6a4uPVymZ+DAXXbpyb/uBxa3Shlg9F8fnCbvxK/eG3MHacV3URuPMrSXBiLxg\n'
b'Z3Vms/EY96Jc5lP/Ooi2R6X/ExjqmAl3P51T+c8B5fWmcBcUr2Ok/5mzk53cU6cG\n'
b'/kiFHaFpriV1uxPMUgP17VGhi9sVAgMBAAGjggEIMIIBBDAOBgNVHQ8BAf8EBAMC\n'
b'AYYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMBIGA1UdEwEB/wQIMAYB\n'
b'Af8CAQAwHQYDVR0OBBYEFBQusxe3WFbLrlAJQOYfr52LFMLGMB8GA1UdIwQYMBaA\n'
b'FHm0WeZ7tuXkAXOACIjIGlj26ZtuMDIGCCsGAQUFBwEBBCYwJDAiBggrBgEFBQcw\n'
b'AoYWaHR0cDovL3gxLmkubGVuY3Iub3JnLzAnBgNVHR8EIDAeMBygGqAYhhZodHRw\n'
b'Oi8veDEuYy5sZW5jci5vcmcvMCIGA1UdIAQbMBkwCAYGZ4EMAQIBMA0GCysGAQQB\n'
b'gt8TAQEBMA0GCSqGSIb3DQEBCwUAA4ICAQCFyk5HPqP3hUSFvNVneLKYY611TR6W\n'
b'PTNlclQtgaDqw+34IL9fzLdwALduO/ZelN7kIJ+m74uyA+eitRY8kc607TkC53wl\n'
b'ikfmZW4/RvTZ8M6UK+5UzhK8jCdLuMGYL6KvzXGRSgi3yLgjewQtCPkIVz6D2QQz\n'
b'CkcheAmCJ8MqyJu5zlzyZMjAvnnAT45tRAxekrsu94sQ4egdRCnbWSDtY7kh+BIm\n'
b'lJNXoB1lBMEKIq4QDUOXoRgffuDghje1WrG9ML+Hbisq/yFOGwXD9RiX8F6sw6W4\n'
b'avAuvDszue5L3sz85K+EC4Y/wFVDNvZo4TYXao6Z0f+lQKc0t8DQYzk1OXVu8rp2\n'
b'yJMC6alLbBfODALZvYH7n7do1AZls4I9d1P4jnkDrQoxB3UqQ9hVl3LEKQ73xF1O\n'
b'yK5GhDDX8oVfGKF5u+decIsH4YaTw7mP3GFxJSqv3+0lUFJoi5Lc5da149p90Ids\n'
b'hCExroL1+7mryIkXPeFM5TgO9r0rvZaBFOvV2z0gp35Z0+L4WPlbuEjN/lxPFin+\n'
b'HlUjr8gRsI3qfJOQFy/9rKIJR0Y/8Omwt/8oTWgy1mdeHmmjk7j1nYsvC9JSQ6Zv\n'
b'MldlTTKB3zhThV1+XWYp6rjd5JW1zbVWEkLNxE7GJThEUG3szgBVGP7pSWTUTsqX\n'
b'nLRbwHOoq7hHwg==\n'
b'-----END CERTIFICATE-----\n'
)
le_x3_nick = "CN=Let's Encrypt Authority X3,O=Let's Encrypt,C=US"
le_r3_nick = "CN=R3,O=Let's Encrypt,C=US"
class TestIPACommand(IntegrationTest):
@@ -1113,7 +1111,7 @@ class TestIPACommand(IntegrationTest):
result.stderr_text
# Install 3rd party CA's, Let's Encrypt in this case
for cert in (isrgrootx1, letsencryptauthorityx3):
for cert in (isrgrootx1, letsencryptauthorityr3):
certfile = os.path.join(self.master.config.test_dir, 'cert.pem')
self.master.put_file_contents(certfile, cert)
result = self.master.run_command(
@@ -1140,7 +1138,7 @@ class TestIPACommand(IntegrationTest):
# deletion of a subca
result = self.master.run_command(
['ipa-cacert-manage', 'delete', le_x3_nick],
['ipa-cacert-manage', 'delete', le_r3_nick],
raiseonerr=False
)
assert result.returncode == 0
@@ -1208,7 +1206,8 @@ class TestIPACommand(IntegrationTest):
# start to look at logs a bit before "now"
# https://pagure.io/freeipa/issue/8432
since = time.strftime(
'%H:%M:%S', (datetime.now() - timedelta(seconds=10)).timetuple()
'%Y-%m-%d %H:%M:%S',
(datetime.now() - timedelta(seconds=10)).timetuple()
)
password = 'WrongPassword'
@@ -1374,7 +1373,9 @@ class TestIPACommand(IntegrationTest):
stdin_text='{0}\n{0}\n'.format(original_passwd)
)
ldap = self.master.ldap_connect()
# Wait for the password update to be replicated from replicas[0]
# to other servers
ldap = self.replicas[0].ldap_connect()
tasks.wait_for_replication(ldap)
# The user can log in again
@@ -1578,6 +1579,46 @@ class TestIPACommandWithoutReplica(IntegrationTest):
tasks.ldapsearch_dm(self.master, base, ldap_args=[], scope='sub')
tasks.ldapsearch_dm(self.master, base, ldap_args=[], scope='base')
def test_sid_generation(self):
"""
Test SID generation
Check that new users are created with a SID and PAC data is
added in their Kerberos tickets.
"""
user = "pacuser"
passwd = "Secret123"
try:
# Create a nonadmin user
tasks.create_active_user(
self.master, user, passwd, first=user, last=user,
krb5_trace=True)
# Check SID is present in the new entry
base_dn = str(self.master.domain.basedn)
result = tasks.ldapsearch_dm(
self.master,
'uid={user},cn=users,cn=accounts,{base_dn}'.format(
user=user, base_dn=base_dn),
['ipantsecurityidentifier'],
scope='base'
)
assert 'ipantsecurityidentifier' in result.stdout_text
# Defaults: host/... principal for service
# keytab in /etc/krb5.keytab
self.master.run_command(["kinit", '-k'])
result = self.master.run_command(
[os.path.join(paths.LIBEXEC_IPA_DIR, "ipa-print-pac"),
"ticket", user],
stdin_text=(passwd + '\n')
)
assert "PAC_DATA" in result.stdout_text
finally:
tasks.kinit_admin(self.master)
self.master.run_command(['ipa', 'user-del', user])
class TestIPAautomount(IntegrationTest):
@classmethod
+90 -11
View File
@@ -28,6 +28,17 @@ IPA_DEFAULT_MASTER_SRV_REC = (
(DNSName(u'_kpasswd._udp'), 464),
)
IPA_DEFAULT_MASTER_URI_REC = (
(
DNSName('_kerberos'),
("krb5srv:m:tcp:{hostname}", "krb5srv:m:udp:{hostname}")
),
(
DNSName('_kpasswd'),
("krb5srv:m:tcp:{hostname}", "krb5srv:m:udp:{hostname}")
),
)
IPA_DEFAULT_ADTRUST_SRV_REC = (
# srv record name, port
(DNSName(u'_ldap._tcp.Default-First-Site-Name._sites.dc._msdcs'), 389),
@@ -79,6 +90,21 @@ def _gen_expected_srv_rrset(rname, port, servers, ttl=86400):
)
def _gen_expected_uri_rrset(rname, uri_templates, servers, ttl=86400):
rdata_list = [
"{prio} {weight} {uri}".format(
prio=prio,
weight=weight,
uri=uri_template.format(hostname=servername.make_absolute()),
)
for uri_template in uri_templates
for prio, weight, servername in servers
]
return dns.rrset.from_text_list(
rname, ttl, dns.rdataclass.IN, dns.rdatatype.URI, rdata_list
)
def _gen_expected_a_rrset(rname, servers, ttl=86400):
return dns.rrset.from_text_list(rname, ttl, dns.rdataclass.IN,
dns.rdatatype.A, servers)
@@ -174,6 +200,20 @@ class TestDNSLocations(IntegrationTest):
"with IP: '{}' for name '{}' (expected:\n{}\ngot:\n{})".
format(server_ip, name_abs, expected, query))
def _test_URI_rec_against_server(self, server_ip, domain, expected_servers,
rec_list=IPA_DEFAULT_MASTER_URI_REC):
for rname, uri_templates in rec_list:
name_abs = rname.derelativize(domain)
expected = _gen_expected_uri_rrset(
name_abs, uri_templates, expected_servers)
query = resolve_records_from_server(
name_abs, 'URI', server_ip)
assert expected == query, (
"Expected and received DNS data do not match on server "
"with IP: '{}' for name '{}' (expected:\n{}\ngot:\n{})".
format(server_ip, name_abs, expected, query))
def test_without_locations(self):
"""Servers are not in locations, this tests if basic system records
are generated properly"""
@@ -185,6 +225,9 @@ class TestDNSLocations(IntegrationTest):
for ip in (self.master.ip, self.replicas[0].ip, self.replicas[1].ip):
self._test_SRV_rec_against_server(ip, self.domain,
expected_servers)
self._test_URI_rec_against_server(
ip, self.domain, expected_servers
)
def test_nsupdate_without_locations(self):
"""Test nsupdate file generated by dns-update-system-records
@@ -227,11 +270,19 @@ class TestDNSLocations(IntegrationTest):
)
self._test_SRV_rec_against_server(
self.replicas[0].ip, domain_prague_loc, servers_prague_loc)
self.replicas[0].ip, domain_prague_loc, servers_prague_loc
)
self._test_URI_rec_against_server(
self.replicas[0].ip, domain_prague_loc, servers_prague_loc
)
for ip in (self.master.ip, self.replicas[1].ip):
self._test_SRV_rec_against_server(
ip, domain_without_loc, servers_without_loc)
ip, domain_without_loc, servers_without_loc
)
self._test_URI_rec_against_server(
ip, domain_without_loc, servers_without_loc
)
def test_two_replicas_in_location(self):
"""Put second replica to location and test if records changed properly
@@ -270,13 +321,25 @@ class TestDNSLocations(IntegrationTest):
self.master.domain.name).make_absolute())
self._test_SRV_rec_against_server(
self.replicas[0].ip, domain_prague_loc, servers_prague_loc)
self.replicas[0].ip, domain_prague_loc, servers_prague_loc
)
self._test_URI_rec_against_server(
self.replicas[0].ip, domain_prague_loc, servers_prague_loc
)
self._test_SRV_rec_against_server(
self.replicas[1].ip, domain_paris_loc, servers_paris_loc)
self.replicas[1].ip, domain_paris_loc, servers_paris_loc
)
self._test_URI_rec_against_server(
self.replicas[1].ip, domain_paris_loc, servers_paris_loc
)
self._test_SRV_rec_against_server(
self.master.ip, domain_without_loc, servers_without_loc)
self.master.ip, domain_without_loc, servers_without_loc
)
self._test_URI_rec_against_server(
self.master.ip, domain_without_loc, servers_without_loc
)
def test_all_servers_in_location(self):
"""Put master (as second server) to location and test if records
@@ -308,11 +371,19 @@ class TestDNSLocations(IntegrationTest):
self.master.domain.name).make_absolute())
self._test_SRV_rec_against_server(
self.replicas[0].ip, domain_prague_loc, servers_prague_loc)
self.replicas[0].ip, domain_prague_loc, servers_prague_loc
)
self._test_URI_rec_against_server(
self.replicas[0].ip, domain_prague_loc, servers_prague_loc
)
for ip in (self.replicas[1].ip, self.master.ip):
self._test_SRV_rec_against_server(ip, domain_paris_loc,
servers_paris_loc)
self._test_SRV_rec_against_server(
ip, domain_paris_loc, servers_paris_loc
)
self._test_URI_rec_against_server(
ip, domain_paris_loc, servers_paris_loc
)
def test_change_weight(self):
"""Change weight of master and test if records changed properly
@@ -347,11 +418,19 @@ class TestDNSLocations(IntegrationTest):
self.master.domain.name).make_absolute())
self._test_SRV_rec_against_server(
self.replicas[0].ip, domain_prague_loc, servers_prague_loc)
self.replicas[0].ip, domain_prague_loc, servers_prague_loc
)
self._test_URI_rec_against_server(
self.replicas[0].ip, domain_prague_loc, servers_prague_loc
)
for ip in (self.replicas[1].ip, self.master.ip):
self._test_SRV_rec_against_server(ip, domain_paris_loc,
servers_paris_loc)
self._test_SRV_rec_against_server(
ip, domain_paris_loc, servers_paris_loc
)
self._test_URI_rec_against_server(
ip, domain_paris_loc, servers_paris_loc
)
def test_change_weight_relative_zero_0(self):
"""Change weight of one master and check on relative weight %
+12 -5
View File
@@ -20,7 +20,6 @@ from __future__ import absolute_import
import os
import re
import time
import tempfile
from cryptography import x509
from cryptography.hazmat.backends import default_backend
@@ -464,8 +463,12 @@ class TestMultipleExternalCA(IntegrationTest):
def test_master_install_ca1(self):
install_server_external_ca_step1(self.master)
# Sign CA, transport it to the host and get ipa a root ca paths.
root_ca_fname1 = tempfile.mkdtemp(suffix='root_ca.crt', dir=paths.TMP)
ipa_ca_fname1 = tempfile.mkdtemp(suffix='ipa_ca.crt', dir=paths.TMP)
root_ca_fname1 = tasks.create_temp_file(
self.master, directory=paths.TMP, suffix="root_ca.crt"
)
ipa_ca_fname1 = tasks.create_temp_file(
self.master, directory=paths.TMP, suffix="ipa_ca.crt"
)
ipa_csr = self.master.get_file_contents(paths.ROOT_IPA_CSR)
@@ -485,8 +488,12 @@ class TestMultipleExternalCA(IntegrationTest):
assert "CN=RootCA1" in result.stdout_text
def test_master_install_ca2(self):
root_ca_fname2 = tempfile.mkdtemp(suffix='root_ca.crt', dir=paths.TMP)
ipa_ca_fname2 = tempfile.mkdtemp(suffix='ipa_ca.crt', dir=paths.TMP)
root_ca_fname2 = tasks.create_temp_file(
self.master, directory=paths.TMP, suffix="root_ca.crt"
)
ipa_ca_fname2 = tasks.create_temp_file(
self.master, directory=paths.TMP, suffix="ipa_ca.crt"
)
self.master.run_command([
paths.IPA_CACERT_MANAGE, 'renew', '--external-ca'])
@@ -22,6 +22,7 @@ from cryptography import x509 as crypto_x509
from ipalib import x509
from ipalib.constants import DOMAIN_LEVEL_0, KRA_TRACKING_REQS
from ipalib.constants import IPA_CA_RECORD
from ipalib.constants import ALLOWED_NETBIOS_CHARS
from ipalib.sysrestore import SYSRESTORE_STATEFILE, SYSRESTORE_INDEXFILE
from ipapython.dn import DN
from ipaplatform.constants import constants
@@ -69,6 +70,17 @@ def server_cleanup(request):
tasks.uninstall_master(host)
def create_netbios_name(host):
"""
Create a NetBIOS name based on the provided host
"""
netbios = ''.join(
c for c in host.domain.name.split('.')[0].upper() \
if c in ALLOWED_NETBIOS_CHARS
)[:15]
return netbios
class InstallTestBase1(IntegrationTest):
num_replicas = 3
@@ -570,6 +582,10 @@ class TestInstallWithCA_DNS3(CALessBase):
ticket 7239
"""
@pytest.mark.xfail(
osinfo.id == 'fedora' and osinfo.version_number >= (33,)
and osinfo.version_number < (35,),
reason='freeipa ticket 8700', strict=True)
@server_install_setup
def test_number_of_zones(self):
"""There should be two zones: one forward, one reverse"""
@@ -1171,6 +1187,8 @@ class TestInstallMaster(IntegrationTest):
hosts = self.master.get_file_contents(paths.HOSTS, encoding='utf-8')
new_hosts = hosts.replace(original_hostname, new_hostname)
self.master.put_file_contents(paths.HOSTS, new_hosts)
netbios = create_netbios_name(self.master)
try:
cmd = ['ipa-server-install', '--hostname', new_hostname]
with self.master.spawn_expect(cmd) as e:
@@ -1193,6 +1211,8 @@ class TestInstallMaster(IntegrationTest):
e.sendline(self.master.config.admin_password)
e.expect_exact('Password (confirm): ')
e.sendline(self.master.config.admin_password)
e.expect_exact('NetBIOS domain name [{}]: '.format(netbios))
e.sendline(netbios)
e.expect_exact('Do you want to configure chrony with '
'NTP server or pool address? [no]: ')
e.sendline('no')
@@ -1367,6 +1387,7 @@ class TestInstallMasterDNS(IntegrationTest):
https://pagure.io/freeipa/issue/2575
"""
cmd = ['ipa-server-install']
netbios = create_netbios_name(self.master)
with self.master.spawn_expect(cmd) as e:
e.expect_exact('Do you want to configure integrated '
'DNS (BIND)? [no]: ')
@@ -1398,6 +1419,8 @@ class TestInstallMasterDNS(IntegrationTest):
e.expect_exact('Do you want to search for missing reverse '
'zones? [yes]: ')
e.sendline('no') # irrelevant for this test
e.expect_exact('NetBIOS domain name [{}]: '.format(netbios))
e.sendline(netbios)
e.expect_exact('Do you want to configure chrony with NTP '
'server or pool address? [no]: ')
e.sendline('no') # irrelevant for this test
+186 -14
View File
@@ -5,15 +5,19 @@
"""
Module provides tests for ipa-cert-fix CLI.
"""
from cryptography.hazmat.backends import default_backend
from cryptography import x509
from datetime import datetime, date
import pytest
import time
import logging
from ipaplatform.paths import paths
from ipapython.ipaldap import realm_to_serverid
from ipatests.pytest_ipa.integration import tasks
from ipatests.test_integration.base import IntegrationTest
from ipatests.test_integration.test_caless import CALessBase, ipa_certs_cleanup
from ipatests.test_integration.test_cert import get_certmonger_fs_id
logger = logging.getLogger(__name__)
@@ -48,14 +52,47 @@ def check_status(host, cert_count, state, timeout=600):
return count
def move_date(host, chrony_state, date_str):
"""Helper method to move the date on given host
:param host: The host on which date is to be moved
:param chrony_state: State to which chrony service to be moved
:param date_str: date string to move the date i.e 2years1month1days
def needs_resubmit(host, req_id):
"""Helper method to identify if cert request needs to be resubmitted
:param host: the host
:param req_id: request id to perform operation for
Returns True if resubmit needed else False
"""
host.run_command(['systemctl', chrony_state, 'chronyd'])
host.run_command(['date', '-s', date_str])
# check if cert is in monitoring state
tasks.wait_for_certmonger_status(
host, ('MONITORING'), req_id, timeout=600
)
# check if cert is valid and not expired
cmd = host.run_command(
'getcert list -i {} | grep expires'.format(req_id)
)
cert_expiry = cmd.stdout_text.split(' ')
cert_expiry = datetime.strptime(cert_expiry[1], '%Y-%m-%d').date()
if cert_expiry > date.today():
return False
else:
return True
def get_cert_expiry(host, nssdb_path, cert_nick):
"""Method to get cert expiry date of given certificate
:param host: the host
:param nssdb_path: nssdb path of certificate
:param cert_nick: certificate nick name for extracting cert from nssdb
"""
# get initial expiry date to compare later with renewed cert
host.run_command([
'certutil', '-L', '-a',
'-d', nssdb_path,
'-n', cert_nick,
'-o', '/root/cert.pem'
])
data = host.get_file_contents('/root/cert.pem')
cert = x509.load_pem_x509_certificate(data, backend=default_backend())
return cert.not_valid_after
@pytest.fixture
@@ -74,15 +111,15 @@ def expire_cert_critical():
extra_args=['--no-ntp'])
if setup_kra:
tasks.install_kra(host)
host.run_command(['systemctl', 'stop', 'chronyd'])
host.run_command(['date', '-s', '+3Years+1day'])
# move date to expire certs
tasks.move_date(host, 'stop', '+3Years+1day')
yield _expire_cert_critical
host = hosts.pop('host')
tasks.uninstall_master(host)
host.run_command(['date', '-s', '-3Years-1day'])
host.run_command(['systemctl', 'start', 'chronyd'])
tasks.move_date(host, 'start', '-3Years-1day')
class TestIpaCertFix(IntegrationTest):
@@ -96,12 +133,12 @@ class TestIpaCertFix(IntegrationTest):
def expire_ca_cert(self):
tasks.install_master(self.master, setup_dns=False,
extra_args=['--no-ntp'])
move_date(self.master, 'stop', '+20Years+1day')
tasks.move_date(self.master, 'stop', '+20Years+1day')
yield
tasks.uninstall_master(self.master)
move_date(self.master, 'start', '-20Years-1day')
tasks.move_date(self.master, 'start', '-20Years-1day')
def test_missing_csr(self, expire_cert_critical):
"""
@@ -336,3 +373,138 @@ class TestCertFixKRA(IntegrationTest):
self.master.run_command(['ipa-cert-fix', '-v'], stdin_text='yes\n')
check_status(self.master, 12, "MONITORING")
class TestCertFixReplica(IntegrationTest):
num_replicas = 1
@classmethod
def install(cls, mh):
tasks.install_master(
mh.master, setup_dns=False, extra_args=['--no-ntp']
)
tasks.install_replica(
mh.master, mh.replicas[0],
setup_dns=False, extra_args=['--no-ntp']
)
@pytest.fixture
def expire_certs(self):
# move system date to expire certs
for host in self.master, self.replicas[0]:
tasks.move_date(host, 'stop', '+3years+1days')
yield
# move date back on replica and master
for host in self.master, self.replicas[0]:
tasks.move_date(host, 'start', '-3years-1days')
def test_renew_expired_cert_replica(self, expire_certs):
"""Test renewal of certificates on replica with ipa-cert-fix
This is to check that ipa-cert-fix renews the certificates
on replica
related: https://pagure.io/freeipa/issue/7885
"""
# wait for cert expiry
check_status(self.master, 8, "CA_UNREACHABLE")
self.master.run_command(['ipa-cert-fix', '-v'], stdin_text='yes\n')
check_status(self.master, 9, "MONITORING")
# replica operations
# 'Server-Cert cert-pki-ca' cert will be in CA_UNREACHABLE state
cmd = self.replicas[0].run_command(
['getcert', 'list',
'-d', paths.PKI_TOMCAT_ALIAS_DIR,
'-n', 'Server-Cert cert-pki-ca']
)
req_id = get_certmonger_fs_id(cmd.stdout_text)
tasks.wait_for_certmonger_status(
self.replicas[0], ('CA_UNREACHABLE'), req_id, timeout=600
)
# get initial expiry date to compare later with renewed cert
initial_expiry = get_cert_expiry(
self.replicas[0],
paths.PKI_TOMCAT_ALIAS_DIR,
'Server-Cert cert-pki-ca'
)
# check that HTTP,LDAP,PKINIT are renewed and in MONITORING state
instance = realm_to_serverid(self.master.domain.realm)
dirsrv_cert = paths.ETC_DIRSRV_SLAPD_INSTANCE_TEMPLATE % instance
for cert in (paths.KDC_CERT, paths.HTTPD_CERT_FILE):
cmd = self.replicas[0].run_command(
['getcert', 'list', '-f', cert]
)
req_id = get_certmonger_fs_id(cmd.stdout_text)
tasks.wait_for_certmonger_status(
self.replicas[0], ('MONITORING'), req_id, timeout=600
)
cmd = self.replicas[0].run_command(
['getcert', 'list', '-d', dirsrv_cert]
)
req_id = get_certmonger_fs_id(cmd.stdout_text)
tasks.wait_for_certmonger_status(
self.replicas[0], ('MONITORING'), req_id, timeout=600
)
# check if replication working fine
testuser = 'testuser1'
password = 'Secret@123'
stdin = (f"{self.master.config.admin_password}\n"
f"{self.master.config.admin_password}\n"
f"{self.master.config.admin_password}\n")
self.master.run_command(['kinit', 'admin'], stdin_text=stdin)
tasks.user_add(self.master, testuser, password=password)
self.replicas[0].run_command(['kinit', 'admin'], stdin_text=stdin)
self.replicas[0].run_command(['ipa', 'user-show', testuser])
# renew shared certificates by resubmitting to certmonger
cmd = self.replicas[0].run_command(
['getcert', 'list', '-f', paths.RA_AGENT_PEM]
)
req_id = get_certmonger_fs_id(cmd.stdout_text)
if needs_resubmit(self.replicas[0], req_id):
self.replicas[0].run_command(
['getcert', 'resubmit', '-i', req_id]
)
tasks.wait_for_certmonger_status(
self.replicas[0], ('MONITORING'), req_id, timeout=600
)
for cert_nick in ('auditSigningCert cert-pki-ca',
'ocspSigningCert cert-pki-ca',
'subsystemCert cert-pki-ca'):
cmd = self.replicas[0].run_command(
['getcert', 'list',
'-d', paths.PKI_TOMCAT_ALIAS_DIR,
'-n', cert_nick]
)
req_id = get_certmonger_fs_id(cmd.stdout_text)
if needs_resubmit(self.replicas[0], req_id):
self.replicas[0].run_command(
['getcert', 'resubmit', '-i', req_id]
)
tasks.wait_for_certmonger_status(
self.replicas[0], ('MONITORING'), req_id, timeout=600
)
self.replicas[0].run_command(
['ipa-cert-fix', '-v'], stdin_text='yes\n'
)
check_status(self.replicas[0], 9, "MONITORING")
# Sometimes certmonger takes time to update the cert status
# So check in nssdb instead of relying on getcert command
renewed_expiry = get_cert_expiry(
self.replicas[0],
paths.PKI_TOMCAT_ALIAS_DIR,
'Server-Cert cert-pki-ca'
)
assert renewed_expiry > initial_expiry
@@ -1221,7 +1221,7 @@ class TestIpaHealthCheck(IntegrationTest):
"--case-id",
caseid,
"--batch",
"-v",
"-vv",
"--build",
]
)
@@ -2415,7 +2415,10 @@ class TestIpaHealthCheckWithExternalCA(IntegrationTest):
"""
Test for IPAOpenSSLChainValidation when httpd cert is moved.
"""
error_msg = "Can't open {} for reading".format(paths.HTTPD_CERT_FILE)
error_msg1 = "Can't open {} for reading".format(paths.HTTPD_CERT_FILE)
# OpenSSL3 has a different error message
error_msg3 = "Could not open file or uri for loading certificate " \
"file from {}".format(paths.HTTPD_CERT_FILE)
returncode, data = run_healthcheck(
self.master,
"ipahealthcheck.ipa.certs",
@@ -2425,7 +2428,8 @@ class TestIpaHealthCheckWithExternalCA(IntegrationTest):
for check in data:
if check["kw"]["key"] == paths.HTTPD_CERT_FILE:
assert check["result"] == "ERROR"
assert error_msg in check["kw"]["reason"]
assert (error_msg1 in check["kw"]["reason"]
or error_msg3 in check["kw"]["reason"])
@pytest.fixture()
def replace_ipa_chain(self):
+1 -1
View File
@@ -130,7 +130,7 @@ class TestNFS(IntegrationTest):
nfsclt = self.clients[1]
# for journalctl --since
since = time.strftime('%H:%M:%S')
since = time.strftime('%Y-%m-%d %H:%M:%S')
nfsclt.run_command(["systemctl", "restart", "rpc-gssd"])
time.sleep(WAIT_AFTER_INSTALL)
mountpoints = ("/mnt/krb", "/mnt/std", "/home")
@@ -260,6 +260,8 @@ class TestNTPoptions(IntegrationTest):
"No\n"
# Server host name [hostname]:
"\n"
# Enter the NetBIOS name for the IPA domain
"IPA\n"
# Do you want to configure chrony with NTP server
# or pool address? [no]:
"Yes\n"
@@ -372,6 +374,7 @@ class TestNTPoptions(IntegrationTest):
server_input = (
"\n" +
"\n"
"IPA\n"
"No\n"
"Yes"
)
+8 -7
View File
@@ -571,9 +571,6 @@ class TestTrust(BaseTestTrust):
client = self.clients[0]
tasks.backup_file(self.master, paths.SSSD_CONF)
log_file = '{0}/sssd_{1}.log'.format(paths.VAR_LOG_SSSD_DIR,
client.domain.name)
logsize = len(client.get_file_contents(log_file))
res = self.master.run_command(['pidof', 'sssd_be'])
pid = res.stdout_text.strip()
test_id = 'id testuser@%s' % self.ad_domain
@@ -595,6 +592,10 @@ class TestTrust(BaseTestTrust):
self.master.run_command(remove_cache)
client.run_command(remove_cache)
log_file = '{0}/sssd_{1}.log'.format(paths.VAR_LOG_SSSD_DIR,
client.domain.name)
logsize = len(client.get_file_contents(log_file))
try:
# stop sssd_be, needed to simulate a timeout in the extdom plugin.
stop_sssdbe = self.master.run_command('kill -STOP %s' % pid)
@@ -943,12 +944,12 @@ class TestTrust(BaseTestTrust):
['chown', '--reference', paths.NAMED_CONF, ad_zone_file])
named_conf = self.master.get_file_contents(paths.NAMED_CONF,
encoding='utf-8')
named_conf += textwrap.dedent('''
zone "ad.test" {{
named_conf += textwrap.dedent(f'''
zone "{self.ad.domain.name}" {{
type master;
file "{}";
file "{ad_zone_file}";
}};
'''.format(ad_zone_file))
''')
self.master.put_file_contents(paths.NAMED_CONF, named_conf)
tasks.restart_named(self.master)
try:
+67 -1
View File
@@ -24,7 +24,7 @@ DNS tests
from ipatests.test_webui.ui_driver import UI_driver
from ipatests.test_webui.ui_driver import screenshot
from ipatests.test_webui.data_dns import (
ZONE_ENTITY, FORWARD_ZONE_ENTITY, CONFIG_ENTITY,
ZONE_ENTITY, FORWARD_ZONE_ENTITY, CONFIG_ENTITY, RECORD_ENTITY,
ZONE_DEFAULT_FACET, ZONE_PKEY, ZONE_DATA, FORWARD_ZONE_PKEY,
FORWARD_ZONE_DATA, RECORD_PKEY, A_IP, RECORD_ADD_DATA, RECORD_MOD_DATA,
CONFIG_MOD_DATA
@@ -113,3 +113,69 @@ class test_dns(UI_driver):
self.init_app()
self.navigate_by_menu('network_services/dns/dnsconfig')
self.mod_record(CONFIG_ENTITY, CONFIG_MOD_DATA)
def test_ptr_from_host_creation(self):
"""
Test scenario for RHBZ 2009114 - a PTR record is created correctly
with trailing dot when navigated to DNS Records page from host details
page.
"""
self.init_app()
zone = "ptrzone.test."
reverse_zone = "12.168.192.in-addr.arpa."
hostname = "server"
fqdn = "server.ptrzone.test"
dns_fqdn = fqdn + "."
ip = "192.168.12.20"
zone_add_data = {
"add": [
("textbox", "idnsname", zone),
],
}
reverse_zone_add_data = {
"add": [
("radio", "dnszone_name_type", "name_from_ip"),
("textbox", "name_from_ip", ip + "/24"),
],
}
record_add_data = {
"add": [
("textbox", "idnsname", "server"),
("textbox", "a_part_ip_address", ip),
]
}
host_add_data = {
"add": [
("textbox", "hostname", hostname),
("combobox", "dnszone", zone),
],
}
# Create needed DNS zones and record
self.add_record(ZONE_ENTITY, zone_add_data, navigate=True)
self.add_record(ZONE_ENTITY, reverse_zone_add_data, navigate=False)
self.navigate_to_record(zone)
self.add_record(ZONE_ENTITY, record_add_data,
facet=ZONE_DEFAULT_FACET)
# Create host record
self.navigate_by_menu("identity/host")
self.add_record("host", host_add_data, navigate=False)
self.navigate_to_record(fqdn)
self.click_on_link(fqdn)
self.wait_for_request(n=2, d=0.5)
self.assert_facet(RECORD_ENTITY, "details")
self.click_on_link(ip)
self.wait_for_request(n=2, d=0.5)
self.assert_dialog()
self.click_on_link("Create dns record")
self.wait_for_request(n=3, d=0.5)
self.assert_facet(RECORD_ENTITY, "details")
self.assert_record(dns_fqdn, table_name="ptrrecord")
# Cleanup
self.navigate_to_entity(ZONE_ENTITY)
self.delete_record([zone, reverse_zone])
self.navigate_to_entity("host")
self.delete_record([fqdn])
+7 -2
View File
@@ -297,8 +297,13 @@ class test_range(range_tasks):
# Without primary and secondary RID bases
data = self.get_data(pkey, base_rid='', secondary_base_rid='')
self.add_record(ENTITY, data, navigate=False)
self.delete_record(pkey)
self.add_record(ENTITY, data, navigate=False, negative=True)
try:
assert self.has_form_error('ipabaserid')
finally:
self.delete_record(pkey)
self.dialog_button_click('cancel')
@screenshot
def test_modify_range_with_invalid_or_missing_values(self):
@@ -356,6 +356,7 @@ class test_selinuxusermap(UI_driver):
self.fill_fields(selinuxmap.DATA['mod'], undo=True)
self.click_on_link('SELinux User Maps')
self.dialog_button_click('save')
self.close_notifications()
self.navigate_to_record(selinuxmap.PKEY)
self.verify_btn_action(mod_description, negative=True)
self.wait_for_request(n=2)
+2 -6
View File
@@ -192,7 +192,7 @@ class UI_driver:
if not NO_YAML and os.path.isfile(path):
try:
with open(path, 'r') as conf:
cls.config = yaml.load(conf)
cls.config = yaml.load(stream=conf, Loader=yaml.FullLoader)
except yaml.YAMLError as e:
pytest.skip("Invalid Web UI config.\n%s" % e)
except IOError as e:
@@ -1057,7 +1057,7 @@ class UI_driver:
# Chrome does not close search area on click
if list_cnt.is_displayed():
self.driver.switch_to_active_element().send_keys(Keys.RETURN)
self.driver.switch_to.active_element.send_keys(Keys.RETURN)
self.wait()
@@ -1441,10 +1441,6 @@ class UI_driver:
last_element = data[len(data) - 1]
pkeys = []
for record in data:
pkeys.append(record['pkey'])
if navigate:
self.navigate_to_entity(entity, facet)
-45
View File
@@ -2,55 +2,10 @@
#
# Copyright (C) 2016 FreeIPA Contributors see COPYING for license
#
from contextlib import contextmanager
import six
from ipalib import api
from ipatests.util import MockLDAP
trust_container_dn = "cn=ad,cn=trusts,{basedn}".format(
basedn=api.env.basedn)
trust_container_add = dict(
objectClass=[b"nsContainer", b"top"]
)
smb_cont_dn = "{cifsdomains},{basedn}".format(
cifsdomains=api.env.container_cifsdomains,
basedn=api.env.basedn)
smb_cont_add = dict(
objectClass=[b"nsContainer", b"top"]
)
def create_mock_trust_containers():
with MockLDAP() as ldap:
ldap.add_entry(trust_container_dn, trust_container_add)
ldap.add_entry(smb_cont_dn, smb_cont_add)
def remove_mock_trust_containers():
with MockLDAP() as ldap:
ldap.del_entry(trust_container_dn)
ldap.del_entry(smb_cont_dn)
@contextmanager
def mocked_trust_containers():
"""Mocked trust containers
Provides containers for the RPC tests:
cn=ad,cn=trusts,BASEDN
cn=ad,cn=etc,BASEDN
Upon exiting, it tries to remove the container entries.
If the user of the context manager failed to remove
all child entries, exiting the context manager will fail.
"""
create_mock_trust_containers()
try:
yield
finally:
remove_mock_trust_containers()
def get_range_dn(name):
format_str = "cn={name},cn=ranges,cn=etc,{basedn}"
+2 -2
View File
@@ -35,7 +35,7 @@ user_base = [
u'ipaSshGroupOfPubKeys',
]
user = user_base + [u'mepOriginEntry']
user = user_base + [u'mepOriginEntry', 'ipantuserattrs',]
group = [
u'top',
@@ -46,7 +46,7 @@ group = [
]
externalgroup = group + [u'ipaexternalgroup']
posixgroup = group + [u'posixgroup']
posixgroup = group + [u'posixgroup', 'ipantgroupattrs']
host = [
u'ipasshhost',
+5 -2
View File
@@ -25,6 +25,7 @@ from ipalib import api
from ipatests.test_xmlrpc import objectclasses
from ipatests.util import Fuzzy, assert_deepequal
from ipatests.test_xmlrpc.xmlrpc_test import (Declarative, fuzzy_digits,
fuzzy_set_optional_oc,
fuzzy_uuid)
from ipapython.dn import DN
import pytest
@@ -97,7 +98,8 @@ class test_batch(Declarative):
result=dict(
cn=[group1],
description=[u'Test desc 1'],
objectclass=objectclasses.group + [u'posixgroup'],
objectclass=fuzzy_set_optional_oc(
objectclasses.posixgroup, 'ipantgroupattrs'),
ipauniqueid=[fuzzy_uuid],
gidnumber=[fuzzy_digits],
dn=DN(('cn', 'testgroup1'),
@@ -168,7 +170,8 @@ class test_batch(Declarative):
result=dict(
cn=[group1],
description=[u'Test desc 1'],
objectclass=objectclasses.group + [u'posixgroup'],
objectclass=fuzzy_set_optional_oc(
objectclasses.posixgroup, 'ipantgroupattrs'),
ipauniqueid=[fuzzy_uuid],
gidnumber=[fuzzy_digits],
dn=DN(('cn', 'testgroup1'),
+15 -10
View File
@@ -27,7 +27,8 @@ import pytest
from ipalib import errors
from ipatests.test_xmlrpc import objectclasses
from ipatests.test_xmlrpc.xmlrpc_test import (
fuzzy_digits, fuzzy_uuid, fuzzy_set_ci, add_oc,
fuzzy_digits, fuzzy_uuid, fuzzy_set_ci,
fuzzy_user_or_group_sid,
XMLRPC_test, raises_exact
)
from ipatests.test_xmlrpc.tracker.group_plugin import GroupTracker
@@ -61,6 +62,8 @@ def managed_group(request, user):
tracker.exists = True
# Managed group gets created when user is created
tracker.track_create()
# Managed groups don't have a SID
del tracker.attrs['ipantsecurityidentifier']
return tracker
@@ -79,7 +82,7 @@ def user_npg2(request, group):
del tracker.attrs['mepmanagedentry']
tracker.attrs.update(
gidnumber=[u'1000'], description=[], memberof_group=[group.cn],
objectclass=add_oc(objectclasses.user_base, u'ipantuserattrs')
objectclass=objectclasses.user_base + ['ipantuserattrs']
)
return tracker.make_fixture(request)
@@ -314,9 +317,9 @@ class TestFindGroup(XMLRPC_test):
'gidnumber': [fuzzy_digits],
'cn': [u'admins'],
'description': [u'Account administrators group'],
'objectclass': fuzzy_set_ci(add_oc(
objectclasses.posixgroup, u'ipantgroupattrs')),
'objectclass': fuzzy_set_ci(objectclasses.posixgroup),
'ipauniqueid': [fuzzy_uuid],
'ipantsecurityidentifier': [fuzzy_user_or_group_sid],
},
{
'dn': get_group_dn('editors'),
@@ -324,27 +327,27 @@ class TestFindGroup(XMLRPC_test):
'cn': [u'editors'],
'description':
[u'Limited admins who can edit other users'],
'objectclass': fuzzy_set_ci(add_oc(
objectclasses.posixgroup, u'ipantgroupattrs')),
'objectclass': fuzzy_set_ci(objectclasses.posixgroup),
'ipauniqueid': [fuzzy_uuid],
'ipantsecurityidentifier': [fuzzy_user_or_group_sid],
},
{
'dn': get_group_dn(group.cn),
'cn': [group.cn],
'description': [u'Test desc1'],
'gidnumber': [fuzzy_digits],
'objectclass': fuzzy_set_ci(add_oc(
objectclasses.posixgroup, u'ipantgroupattrs')),
'objectclass': fuzzy_set_ci(objectclasses.posixgroup),
'ipauniqueid': [fuzzy_uuid],
'ipantsecurityidentifier': [fuzzy_user_or_group_sid],
},
{
'dn': get_group_dn(group2.cn),
'cn': [group2.cn],
'description': [u'Test desc2'],
'gidnumber': [fuzzy_digits],
'objectclass': fuzzy_set_ci(add_oc(
objectclasses.posixgroup, u'ipantgroupattrs')),
'objectclass': fuzzy_set_ci(objectclasses.posixgroup),
'ipauniqueid': [fuzzy_uuid],
'ipantsecurityidentifier': [fuzzy_user_or_group_sid],
},
]), result)
@@ -498,6 +501,8 @@ class TestExternalGroup(XMLRPC_test):
group.track_create()
del group.attrs['gidnumber']
group.attrs.update(objectclass=objectclasses.externalgroup)
# External group don't have a SID
del group.attrs['ipantsecurityidentifier']
command = group.make_create_command(**dict(external=True))
result = command()
group.check_create(result)
+10 -11
View File
@@ -27,7 +27,8 @@ import six
from ipalib import api, errors
from ipatests.test_xmlrpc import objectclasses
from ipatests.test_xmlrpc.xmlrpc_test import (Declarative, uuid_re, add_oc,
from ipatests.test_xmlrpc.xmlrpc_test import (Declarative, uuid_re,
fuzzy_set_optional_oc,
fuzzy_uuid, fuzzy_digits)
from ipatests.test_xmlrpc.test_user_plugin import get_user_result
from ipatests.test_xmlrpc.test_group_plugin import get_group_dn
@@ -216,10 +217,8 @@ class test_idviews(Declarative):
u'Test',
u'User1',
'add',
objectclass=add_oc(
objectclasses.user,
u'ipantuserattrs'
)
objectclass=fuzzy_set_optional_oc(
objectclasses.user, 'ipantuserattrs'),
),
),
),
@@ -237,7 +236,8 @@ class test_idviews(Declarative):
result=dict(
cn=[idoverridegroup1],
description=[u'Test desc 1'],
objectclass=objectclasses.posixgroup,
objectclass=fuzzy_set_optional_oc(
objectclasses.posixgroup, 'ipantgroupattrs'),
ipauniqueid=[fuzzy_uuid],
gidnumber=[fuzzy_digits],
dn=get_group_dn(idoverridegroup1),
@@ -1624,10 +1624,8 @@ class test_idviews(Declarative):
u'Removed',
u'User',
'add',
objectclass=add_oc(
objectclasses.user,
u'ipantuserattrs'
)
objectclass=fuzzy_set_optional_oc(
objectclasses.user, 'ipantuserattrs'),
),
),
),
@@ -1645,7 +1643,8 @@ class test_idviews(Declarative):
result=dict(
cn=[idoverridegroup_removed],
description=[u'Removed group'],
objectclass=objectclasses.posixgroup,
objectclass=fuzzy_set_optional_oc(
objectclasses.posixgroup, 'ipantgroupattrs'),
ipauniqueid=[fuzzy_uuid],
gidnumber=[fuzzy_digits],
dn=get_group_dn(idoverridegroup_removed),
@@ -19,7 +19,7 @@ from ipatests.test_xmlrpc.tracker.host_plugin import HostTracker
from ipatests.test_xmlrpc.tracker.service_plugin import ServiceTracker
from ipatests.test_xmlrpc.tracker.stageuser_plugin import StageUserTracker
from ipatests.test_xmlrpc.mock_trust import (
mocked_trust_containers, get_trust_dn, get_trusted_dom_dict,
get_trust_dn, get_trusted_dom_dict,
encode_mockldap_value)
from ipatests.util import unlock_principal_password, change_principal
@@ -62,7 +62,7 @@ def trusted_domain():
trusted_dom = TRUSTED_DOMAIN_MOCK
# Write the changes
with mocked_trust_containers(), MockLDAP() as ldap:
with MockLDAP() as ldap:
ldap.add_entry(trusted_dom['dn'], trusted_dom['ldif'])
yield trusted_dom
ldap.del_entry(trusted_dom['dn'])
@@ -83,7 +83,7 @@ def trusted_domain_with_suffix():
)
# Write the changes
with mocked_trust_containers(), MockLDAP() as ldap:
with MockLDAP() as ldap:
ldap.add_entry(trusted_dom['dn'], trusted_dom['ldif'])
yield trusted_dom
ldap.del_entry(trusted_dom['dn'])
+3 -1
View File
@@ -24,6 +24,7 @@ Test the `ipaserver/plugins/netgroup.py` module.
from ipalib import api
from ipalib import errors
from ipatests.test_xmlrpc.xmlrpc_test import (Declarative, fuzzy_digits,
fuzzy_set_optional_oc,
fuzzy_uuid, fuzzy_netgroupdn)
from ipatests.test_xmlrpc import objectclasses
from ipapython.dn import DN
@@ -300,7 +301,8 @@ class test_netgroup(Declarative):
cn=[group1],
description=[u'Test desc 1'],
gidnumber=[fuzzy_digits],
objectclass=objectclasses.group + [u'posixgroup'],
objectclass=fuzzy_set_optional_oc(
objectclasses.posixgroup, 'ipantgroupattrs'),
ipauniqueid=[fuzzy_uuid],
dn=DN(('cn',group1),('cn','groups'),('cn','accounts'),
api.env.basedn),
@@ -502,6 +502,7 @@ class test_range(Declarative):
user1, u'Test', u'User1', 'add',
uidnumber=[unicode(user1_uid)],
gidnumber=[unicode(user1_uid)],
objectclass=objectclasses.user_base + [u'mepOriginEntry'],
),
),
),
+361 -26
View File
@@ -8,22 +8,24 @@ Test the `ipaserver/plugins/schema.py` module.
import pytest
from ipalib import errors
from ipatests.test_xmlrpc.tracker.base import Tracker
from ipalib import api, errors
from ipapython.dnsutil import DNSName
from ipatests.test_xmlrpc.xmlrpc_test import XMLRPC_test
@pytest.mark.tier1
class TestParamFindAndShowCommand(XMLRPC_test):
"""Test functionality of the ipa param-{find,show} command"""
tracker = Tracker()
def run_command(self, command, *args, **options):
cmd = api.Command[command]
cmd_result = cmd(*args, **options)
return cmd_result
def test_param_find(self):
"""Test param-find command"""
cmd = "param_find"
# right command without criteria
result = self.tracker.run_command('param_find', u'user-add')
result = self.run_command(cmd, "user-add")
assert len(result['result']) != 0, result
assert result['result'][0]['name'] == 'uid', result
assert result['result'][0]['cli_name'] == 'login', result
@@ -31,26 +33,30 @@ class TestParamFindAndShowCommand(XMLRPC_test):
# right command, right criteria
criteria = u'postalcode'
result = self.tracker.run_command('param_find', u'user-add', criteria)
result = self.run_command(cmd, "user-add", criteria)
assert len(result['result']) != 0, result
for item in result['result']:
assert (criteria in item['name'].lower() or
criteria in item['doc'].lower()), item
# right command, wrong criteria
result = self.tracker.run_command('param_find', u'user-add', u'fake')
result = self.run_command(cmd, "user-add", "fake")
assert len(result['result']) == 0, result
# wrong command, wrong criteria
result = self.tracker.run_command('param_find', u'fake', u'fake')
result = self.run_command(cmd, "fake", "fake")
assert len(result['result']) == 0, result
# too many args
with pytest.raises(errors.MaxArgumentError):
self.run_command(cmd, "arg1", "arg2", "arg3")
def test_param_show(self):
"""Test param-show command"""
# right command, right criteria
criteria = u'uid'
result = self.tracker.run_command('param_show', u'user-add', criteria)
criteria = "uid"
cmd = "param_show"
result = self.run_command(cmd, "user-add", criteria)
assert result['result'] is not None, result
assert result['result']['name'] == 'uid', result
assert result['result']['cli_name'] == 'login', result
@@ -58,26 +64,33 @@ class TestParamFindAndShowCommand(XMLRPC_test):
# right command without criteria
with pytest.raises(errors.RequirementError):
self.tracker.run_command('param_show', u'user-add')
self.run_command(cmd, "user-add")
# right command, wrong criteria
with pytest.raises(errors.NotFound):
self.tracker.run_command('param_show', u'user-add', u'fake')
self.run_command(cmd, "user-add", "fake")
# wrong command, wrong criteria
with pytest.raises(errors.NotFound):
self.tracker.run_command('param_show', u'fake', u'fake')
self.run_command(cmd, "fake", "fake")
# too many args
with pytest.raises(errors.MaxArgumentError):
self.run_command(cmd, "arg1", "arg2", "arg3")
class TestOutputFindAndShowCommand(XMLRPC_test):
"""Test functionality of the ipa output-{find,show} command"""
tracker = Tracker()
def run_command(self, command, *args, **options):
cmd = api.Command[command]
cmd_result = cmd(*args, **options)
return cmd_result
def test_output_find(self):
"""Test output-find command"""
cmd = "output_find"
# right command without criteria
result = self.tracker.run_command('output_find', u'user-add')
result = self.run_command(cmd, "user-add")
assert len(result['result']) != 0, result
assert result['result'][0]['name'] == 'summary', result
assert result['result'][0]['doc'] == \
@@ -85,24 +98,29 @@ class TestOutputFindAndShowCommand(XMLRPC_test):
# right command, right criteria
criteria = u'result'
result = self.tracker.run_command('output_find', u'user-add', criteria)
result = self.run_command(cmd, "user-add", criteria)
assert len(result['result']) == 1, result
assert criteria in result['result'][0]['name'].lower(), result
# right command, wrong criteria
result = self.tracker.run_command('output_find', u'user-add', u'fake')
result = self.run_command(cmd, "user-add", "fake")
assert len(result['result']) == 0, result
# wrong command, wrong criteria
result = self.tracker.run_command('output_find', u'fake', u'fake')
result = self.run_command(cmd, "fake", "fake")
assert len(result['result']) == 0, result
# too many args
with pytest.raises(errors.MaxArgumentError):
self.run_command(cmd, "arg1", "arg2", "arg3")
def test_output_show(self):
"""Test output-show command"""
# right command, right criteria
criteria = u'value'
result = self.tracker.run_command('output_show', u'user-add', criteria)
criteria = "value"
cmd = "output_show"
result = self.run_command(cmd, "user-add", criteria)
assert len(result['result']) != 0, result
assert criteria in result['result']['name'].lower(), result
assert result['result']['doc'] == \
@@ -110,12 +128,329 @@ class TestOutputFindAndShowCommand(XMLRPC_test):
# right command without criteria
with pytest.raises(errors.RequirementError):
self.tracker.run_command('output_show', u'user-add')
self.run_command(cmd, "user-add")
# right command, wrong criteria
with pytest.raises(errors.NotFound):
self.tracker.run_command('output_show', u'user-add', u'fake')
self.run_command(cmd, "user-add", "fake")
# wrong command, wrong criteria
with pytest.raises(errors.NotFound):
self.tracker.run_command('output_show', u'fake', u'fake')
self.run_command(cmd, "fake", "fake")
# too many args
with pytest.raises(errors.MaxArgumentError):
self.run_command(cmd, "arg1", "arg2", "arg3")
class TestSchemaCommand(XMLRPC_test):
"""Test functionality of the ipa schema Command(no cli)"""
expected_keys = {
"classes", "commands", "fingerprint", "topics", "ttl", "version"
}
def run_command(self, command, *args, **options):
cmd = api.Command[command]
cmd_result = cmd(*args, **options)
return cmd_result
def test_schema_no_args(self):
"""Test schema command without any args"""
cmd_result = self.run_command("schema")
result = cmd_result["result"]
assert result.keys() == self.expected_keys
def test_schema_known_valid_fp(self):
"""Test schema command with valid fingerprint"""
# first, fetch current FP to reuse it
cmd_result = self.run_command("schema")
result = cmd_result["result"]
fp_valid = result["fingerprint"]
with pytest.raises(errors.SchemaUpToDate):
self.run_command("schema", known_fingerprints=(fp_valid,))
def test_schema_known_wrong_fp(self):
"""Test schema command with wrong fingerprint"""
fp_wrong = "wrong FP"
cmd_result = self.run_command("schema", known_fingerprints=(fp_wrong,))
result = cmd_result["result"]
assert result.keys() == self.expected_keys
def test_schema_too_many_args(self):
"""Test schema with too many args"""
with pytest.raises(errors.ZeroArgumentError):
self.run_command("schema", "arg1")
class TestCommandCommand(XMLRPC_test):
"""Test functionality of the ipa 'command' command"""
expected_keys = {
"doc",
"full_name",
"name",
"topic_topic",
"version",
}
def run_command(self, command, *args, **options):
cmd = api.Command[command]
cmd_result = cmd(*args, **options)
return cmd_result
def test_command_find_all(self):
"""Test command-find without any args"""
cmd_result = self.run_command("command_find")
result = cmd_result["result"]
assert len(result) != 0, result
for item in result:
assert self.expected_keys.issubset(item.keys()), item
def test_command_find_existent_command(self):
"""Test 'command' with existent command as arg"""
criteria = "user_add"
cmd_result = self.run_command("command_find", criteria)
result = cmd_result["result"]
assert len(result) != 0, result
for item in result:
assert self.expected_keys.issubset(item.keys()), item
name = item["name"].lower()
doc = item.get("doc", "").lower()
assert criteria in name or criteria in doc, item
def test_command_find_nonexistent_command(self):
"""Test command-find with nonexistent command as arg"""
cmd_result = self.run_command("command_find", "nonextentcommand")
result = cmd_result["result"]
assert len(result) == 0, result
def test_command_find_too_many_args(self):
"""Test command-find with too many args"""
with pytest.raises(errors.MaxArgumentError):
self.run_command("command_find", "arg1", "arg2")
def test_command_show_no_args(self):
"""Test command-show without args"""
with pytest.raises(errors.RequirementError):
self.run_command("command_show")
def test_command_show_existent_command(self):
"""Test command-show with existent command as arg"""
criteria = "user_add"
cmd_result = self.run_command("command_show", criteria)
result = cmd_result["result"]
assert self.expected_keys.issubset(result.keys()), result
assert result["name"] == criteria
def test_command_show_nonexistent_command(self):
"""Test command-show with nonexistent command as arg"""
with pytest.raises(errors.NotFound):
self.run_command("command_show", "nonextentcommand")
def test_command_show_too_many_args(self):
"""Test command-show with too many args"""
with pytest.raises(errors.MaxArgumentError):
self.run_command("command_show", "arg1", "arg2")
def test_command_defaults_no_args(self):
"""Test command_defaults without args"""
with pytest.raises(errors.RequirementError):
self.run_command("command_defaults")
def test_command_defaults_existent_command(self):
"""Test command_defaults with existent command as arg"""
criteria = "user_add"
cmd_result = self.run_command("command_defaults", criteria)
# 9c19dd350: do not validate unrequested params in command_defaults
result = cmd_result["result"]
assert len(result) == 0, result
def test_command_defaults_existent_command_params(self):
"""Test command_defaults with existent command as arg with params"""
criteria = "user_add"
params = ["all"]
cmd_result = self.run_command(
"command_defaults", criteria, params=params
)
result = cmd_result["result"]
assert len(result) == len(params), result
assert isinstance(result["all"], bool)
def test_command_defaults_existent_command_several_params(self):
"""
Test command_defaults with existent command as arg with several params
"""
criteria = "user_add"
params = ["all", "raw"]
cmd_result = self.run_command(
"command_defaults", criteria, params=params
)
result = cmd_result["result"]
assert len(result) == len(params), result
assert isinstance(result["all"], bool), result
assert isinstance(result["raw"], bool), result
def test_command_defaults_existent_command_kw(self):
"""Test command_defaults with existent command as arg with kw"""
criteria = "dnszone_add"
params = ["idnsname"]
kw = {"name_from_ip": "127.0.0.1"}
cmd_result = self.run_command(
"command_defaults", criteria, params=params
)
result = cmd_result["result"]
assert len(result) == 0, result
cmd_result = self.run_command(
"command_defaults", criteria, params=params, kw=kw
)
result = cmd_result["result"]
assert len(result) == len(params), result
assert isinstance(result["idnsname"], DNSName)
def test_command_defaults_nonexistent_command(self):
"""Test command-show with nonexistent command as arg"""
with pytest.raises(errors.NotFound):
self.run_command("command_defaults", "nonextentcommand")
def test_command_defaults_too_many_args(self):
"""Test command-show with too many args"""
with pytest.raises(errors.MaxArgumentError):
self.run_command("command_defaults", "arg1", "arg2")
class TestClassCommand(XMLRPC_test):
"""Test functionality of the ipa 'class' command"""
expected_keys = {
"name",
"full_name",
"version",
}
def run_command(self, command, *args, **options):
cmd = api.Command[command]
cmd_result = cmd(*args, **options)
return cmd_result
def test_class_find_all(self):
"""Test class-find without any args"""
cmd_result = self.run_command("class_find")
result = cmd_result["result"]
assert len(result) != 0, result
for item in result:
assert item.keys() == self.expected_keys, item
def test_class_find_existent_class(self):
"""Test class-find with existent class as arg"""
criteria = "user"
cmd_result = self.run_command("class_find", criteria)
result = cmd_result["result"]
assert len(result) != 0, result
for item in result:
assert item.keys() == self.expected_keys, item
name = item["name"].lower()
doc = item.get("doc", "").lower()
assert criteria in name or criteria in doc, item
def test_class_find_nonexistent_class(self):
"""Test class-find with nonexistent class as arg"""
cmd_result = self.run_command("class_find", "nonextentclass")
result = cmd_result["result"]
assert len(result) == 0, result
def test_class_find_too_many_args(self):
"""Test class-find with too many args"""
with pytest.raises(errors.MaxArgumentError):
self.run_command("class_find", "arg1", "arg2")
def test_class_show_no_args(self):
"""Test class-show without args"""
with pytest.raises(errors.RequirementError):
self.run_command("class_show")
def test_class_show_existent_class(self):
"""Test class-show with existent class as arg"""
criteria = "user"
cmd_result = self.run_command("class_show", criteria)
result = cmd_result["result"]
assert result.keys() == self.expected_keys, result
assert result["name"] == criteria
def test_class_show_nonexistent_class(self):
"""Test class-show with nonexistent class as arg"""
with pytest.raises(errors.NotFound):
self.run_command("class_show", "nonextentclass")
def test_class_show_too_many_args(self):
"""Test class-show with too many args"""
with pytest.raises(errors.MaxArgumentError):
self.run_command("class_show", "arg1", "arg2")
class TestTopicCommand(XMLRPC_test):
"""Test functionality of the ipa 'topic' command"""
expected_keys = {
"name",
"full_name",
"version",
"doc",
}
def run_command(self, command, *args, **options):
cmd = api.Command[command]
cmd_result = cmd(*args, **options)
return cmd_result
def test_topic_find_all(self):
"""Test topic-find without any args"""
cmd_result = self.run_command("topic_find")
result = cmd_result["result"]
assert len(result) != 0, result
for item in result:
assert self.expected_keys.issubset(item.keys()), item
def test_topic_find_existent_topic(self):
"""Test topic-find with existent topic as arg"""
criteria = "user"
cmd_result = self.run_command("topic_find", criteria)
result = cmd_result["result"]
assert len(result) != 0, result
for item in result:
assert self.expected_keys.issubset(item.keys()), item
name = item["name"].lower()
doc = item.get("doc", "").lower()
assert criteria in name or criteria in doc, item
def test_topic_find_nonexistent_topic(self):
"""Test topic-find with nonexistent topic as arg"""
cmd_result = self.run_command("topic_find", "nonextenttopic")
result = cmd_result["result"]
assert len(result) == 0, result
def test_topic_find_too_many_args(self):
"""Test topic-find with too many args"""
with pytest.raises(errors.MaxArgumentError):
self.run_command("topic_find", "arg1", "arg2")
def test_topic_show_no_args(self):
"""Test topic-show without args"""
with pytest.raises(errors.RequirementError):
self.run_command("topic_show")
def test_topic_show_existent_topic(self):
"""Test topic-show with existent topic as arg"""
criteria = "user"
cmd_result = self.run_command("topic_show", criteria)
result = cmd_result["result"]
assert self.expected_keys.issubset(result.keys()), result
assert result["name"] == criteria
def test_topic_show_nonexistent_topic(self):
"""Test topic-show with nonexistent topic as arg"""
with pytest.raises(errors.NotFound):
self.run_command("topic_show", "nonextenttopic")
def test_topic_show_too_many_args(self):
"""Test topic-show with too many args"""
with pytest.raises(errors.MaxArgumentError):
self.run_command("topic_show", "arg1", "arg2")
@@ -25,6 +25,7 @@ from ipaplatform.constants import constants as platformconstants
from ipatests.test_xmlrpc import objectclasses
from ipatests.test_xmlrpc.xmlrpc_test import (Declarative, fuzzy_digits,
fuzzy_set_optional_oc,
fuzzy_uuid)
from ipapython.dn import DN
from ipatests.util import Fuzzy
@@ -230,7 +231,8 @@ class test_selinuxusermap(Declarative):
cn=[group1],
description=[u'Test desc 1'],
gidnumber=[fuzzy_digits],
objectclass=objectclasses.group + [u'posixgroup'],
objectclass=fuzzy_set_optional_oc(
objectclasses.posixgroup, 'ipantgroupattrs'),
ipauniqueid=[fuzzy_uuid],
dn=DN(('cn', group1), ('cn', 'groups'), ('cn', 'accounts'),
api.env.basedn),
+5 -2
View File
@@ -25,6 +25,7 @@ from ipalib import api, errors
from ipatests.test_xmlrpc.xmlrpc_test import Declarative, fuzzy_uuid, fuzzy_hash
from ipatests.test_xmlrpc.xmlrpc_test import fuzzy_digits, fuzzy_date, fuzzy_issuer
from ipatests.test_xmlrpc.xmlrpc_test import fuzzy_hex, XMLRPC_test
from ipatests.test_xmlrpc.xmlrpc_test import fuzzy_set_optional_oc
from ipatests.test_xmlrpc.xmlrpc_test import raises_exact
from ipatests.test_xmlrpc import objectclasses
from ipatests.test_xmlrpc.testcert import get_testcert, subject_base
@@ -1113,7 +1114,8 @@ class test_service_allowed_to(Declarative):
summary=u'Added group "%s"' % group1,
result=dict(
cn=[group1],
objectclass=objectclasses.group + [u'posixgroup'],
objectclass=fuzzy_set_optional_oc(
objectclasses.posixgroup, 'ipantgroupattrs'),
ipauniqueid=[fuzzy_uuid],
gidnumber=[fuzzy_digits],
dn=group1_dn
@@ -1165,7 +1167,8 @@ class test_service_allowed_to(Declarative):
summary=u'Added group "%s"' % group2,
result=dict(
cn=[group2],
objectclass=objectclasses.group + [u'posixgroup'],
objectclass=fuzzy_set_optional_oc(
objectclasses.posixgroup, 'ipantgroupattrs'),
ipauniqueid=[fuzzy_uuid],
gidnumber=[fuzzy_digits],
dn=group2_dn
+3 -1
View File
@@ -27,6 +27,7 @@ from ipapython.dn import DN
from ipatests.test_xmlrpc import objectclasses
from ipatests.test_xmlrpc.xmlrpc_test import (
Declarative, fuzzy_guid, fuzzy_domain_sid, fuzzy_string, fuzzy_uuid,
fuzzy_set_optional_oc,
fuzzy_digits)
import pytest
@@ -110,7 +111,8 @@ class test_trustconfig(Declarative):
cn=[testgroup],
description=[u'Test group'],
gidnumber=[fuzzy_digits],
objectclass=objectclasses.group + [u'posixgroup'],
objectclass=fuzzy_set_optional_oc(
objectclasses.posixgroup, 'ipantgroupattrs'),
ipauniqueid=[fuzzy_uuid],
dn=testgroup_dn,
),
+20 -11
View File
@@ -38,7 +38,8 @@ from ipatests.util import (
assert_deepequal, assert_equal, assert_not_equal, raises)
from ipatests.test_xmlrpc.xmlrpc_test import (
XMLRPC_test, fuzzy_digits, fuzzy_uuid, fuzzy_password,
Fuzzy, fuzzy_dergeneralizedtime, add_sid, add_oc, raises_exact)
fuzzy_user_or_group_sid, fuzzy_set_optional_oc,
Fuzzy, fuzzy_dergeneralizedtime, raises_exact)
from ipapython.dn import DN
from ipapython.ipaldap import ldap_initialize
@@ -124,7 +125,7 @@ def user_npg(request, group):
del tracker.attrs['mepmanagedentry']
tracker.attrs.update(
description=[], memberof_group=[group.cn],
objectclass=add_oc(objectclasses.user_base, u'ipantuserattrs')
objectclass=objectclasses.user_base + [u'ipantuserattrs'],
)
return tracker.make_fixture(request)
@@ -138,7 +139,7 @@ def user_npg2(request, group):
del tracker.attrs['mepmanagedentry']
tracker.attrs.update(
gidnumber=[u'1000'], description=[], memberof_group=[group.cn],
objectclass=add_oc(objectclasses.user_base, u'ipantuserattrs')
objectclass=objectclasses.user_base + [u'ipantuserattrs'],
)
return tracker.make_fixture(request)
@@ -645,7 +646,7 @@ class TestCreate(XMLRPC_test):
testuser.attrs.update(gidnumber=[u'1000'])
testuser.attrs.update(
description=[],
objectclass=add_oc(objectclasses.user_base, u'ipantuserattrs')
objectclass=objectclasses.user_base + [u'ipantuserattrs']
)
command = testuser.make_create_command()
result = command()
@@ -659,9 +660,12 @@ class TestCreate(XMLRPC_test):
name=u'tuser1', givenname=u'Test', sn=u'Tuser1', uidnumber=999
)
testuser.track_create()
# When uid is outside of IPA id range, no SID is generated
del testuser.attrs['ipantsecurityidentifier']
testuser.attrs.update(
uidnumber=[u'999'],
gidnumber=[u'999']
gidnumber=[u'999'],
objectclass=objectclasses.user_base + ['mepOriginEntry']
)
command = testuser.make_create_command()
result = command()
@@ -837,7 +841,7 @@ class TestUserWithUPGDisabled(XMLRPC_test):
testuser.attrs.update(gidnumber=[u'1000'])
testuser.attrs.update(
description=[],
objectclass=add_oc(objectclasses.user_base, u'ipantuserattrs')
objectclass=objectclasses.user_base + [u'ipantuserattrs'],
)
command = testuser.make_create_command()
result = command()
@@ -860,7 +864,7 @@ class TestUserWithUPGDisabled(XMLRPC_test):
testuser.attrs.update(gidnumber=[u'1000'])
testuser.attrs.update(
description=[],
objectclass=add_oc(objectclasses.user_base, u'ipantuserattrs')
objectclass=objectclasses.user_base + [u'ipantuserattrs'],
)
command = testuser.make_create_command()
result = command()
@@ -1147,7 +1151,7 @@ def get_user_result(uid, givenname, sn, operation='show', omit=[],
# sn can be None; this should only be used from `get_admin_result`
cn = overrides.get('cn', ['%s %s' % (givenname, sn or '')])
cn[0] = cn[0].strip()
result = add_sid(dict(
result = dict(
homedirectory=[u'/home/%s' % uid],
loginshell=[platformconstants.DEFAULT_SHELL],
uid=[uid],
@@ -1158,7 +1162,7 @@ def get_user_result(uid, givenname, sn, operation='show', omit=[],
mail=[u'%s@%s' % (uid, api.env.domain)],
has_keytab=False,
has_password=False,
))
)
if sn:
result['sn'] = [sn]
if givenname:
@@ -1175,9 +1179,14 @@ def get_user_result(uid, givenname, sn, operation='show', omit=[],
initials=[givenname[0] + (sn or '')[:1]],
ipauniqueid=[fuzzy_uuid],
mepmanagedentry=[get_group_dn(uid)],
objectclass=add_oc(objectclasses.user, u'ipantuserattrs'),
objectclass=fuzzy_set_optional_oc(
objectclasses.user, 'ipantuserattrs'),
krbprincipalname=[u'%s@%s' % (uid, api.env.realm)],
krbcanonicalname=[u'%s@%s' % (uid, api.env.realm)]
krbcanonicalname=[u'%s@%s' % (uid, api.env.realm)],
)
if operation == 'show-all':
result.update(
ipantsecurityidentifier=[fuzzy_user_or_group_sid],
)
if operation in ('show', 'show-all', 'find', 'mod'):
result.update(
+8 -3
View File
@@ -4,6 +4,8 @@
from ipatests.test_xmlrpc import objectclasses
from ipatests.test_xmlrpc.xmlrpc_test import fuzzy_digits, fuzzy_uuid
from ipatests.test_xmlrpc.xmlrpc_test import fuzzy_user_or_group_sid
from ipatests.test_xmlrpc.xmlrpc_test import fuzzy_set_optional_oc
from ipatests.test_xmlrpc.tracker.base import Tracker
from ipatests.util import assert_deepequal, get_group_dn
@@ -21,9 +23,10 @@ class GroupTracker(Tracker):
'idoverrideuser'
}
retrieve_all_keys = retrieve_keys | {u'ipauniqueid', u'objectclass'}
retrieve_all_keys = retrieve_keys | {u'ipauniqueid', u'objectclass',
'ipantsecurityidentifier'}
create_keys = retrieve_all_keys
create_keys = retrieve_all_keys - {u'ipantsecurityidentifier'}
update_keys = retrieve_keys - {u'dn'}
add_member_keys = retrieve_keys | {u'description'}
@@ -91,7 +94,9 @@ class GroupTracker(Tracker):
description=[self.description],
gidnumber=[fuzzy_digits],
ipauniqueid=[fuzzy_uuid],
objectclass=objectclasses.posixgroup,
objectclass=fuzzy_set_optional_oc(
objectclasses.posixgroup, 'ipantgroupattrs'),
ipantsecurityidentifier=[fuzzy_user_or_group_sid],
)
self.exists = True
+10 -4
View File
@@ -11,7 +11,8 @@ import six
from ipatests.util import assert_deepequal, get_group_dn
from ipatests.test_xmlrpc import objectclasses
from ipatests.test_xmlrpc.xmlrpc_test import (
fuzzy_digits, fuzzy_uuid, raises_exact)
fuzzy_set_optional_oc,
fuzzy_digits, fuzzy_uuid, fuzzy_user_or_group_sid, raises_exact)
from ipatests.test_xmlrpc.tracker.base import Tracker
from ipatests.test_xmlrpc.tracker.kerberos_aliases import KerberosAliasMixin
from ipatests.test_xmlrpc.tracker.certmapdata import CertmapdataMixin
@@ -40,7 +41,8 @@ class UserTracker(CertmapdataMixin, KerberosAliasMixin, Tracker):
u'l', u'mobile', u'krbextradata', u'krblastpwdchange',
u'krbpasswordexpiration', u'pager', u'st', u'manager', u'cn',
u'ipauniqueid', u'objectclass', u'mepmanagedentry',
u'displayname', u'gecos', u'initials', u'preserved'}
u'displayname', u'gecos', u'initials', u'preserved',
'ipantsecurityidentifier'}
retrieve_preserved_keys = (retrieve_keys - {u'memberof_group'}) | {
u'preserved'}
@@ -50,6 +52,7 @@ class UserTracker(CertmapdataMixin, KerberosAliasMixin, Tracker):
u'krbextradata', u'krbpasswordexpiration', u'krblastpwdchange',
u'krbprincipalkey', u'userpassword', u'randompassword'}
create_keys = create_keys - {u'nsaccountlock'}
create_keys = create_keys - {'ipantsecurityidentifier'}
update_keys = retrieve_keys - {u'dn'}
activate_keys = retrieve_keys
@@ -122,7 +125,8 @@ class UserTracker(CertmapdataMixin, KerberosAliasMixin, Tracker):
api.env.container_deleteuser,
api.env.basedn
)
self.attrs[u'objectclass'] = objectclasses.user_base
self.attrs[u'objectclass'] = objectclasses.user_base \
+ ['ipantuserattrs']
return self.make_command(
'user_del', self.uid,
@@ -173,7 +177,8 @@ class UserTracker(CertmapdataMixin, KerberosAliasMixin, Tracker):
displayname=[u'%s %s' % (self.givenname, self.sn)],
cn=[u'%s %s' % (self.givenname, self.sn)],
initials=[u'%s%s' % (self.givenname[0], self.sn[0])],
objectclass=objectclasses.user,
objectclass=fuzzy_set_optional_oc(
objectclasses.user, 'ipantuserattrs'),
description=[u'__no_upg__'],
ipauniqueid=[fuzzy_uuid],
uidnumber=[fuzzy_digits],
@@ -188,6 +193,7 @@ class UserTracker(CertmapdataMixin, KerberosAliasMixin, Tracker):
mepmanagedentry=[get_group_dn(self.uid)],
memberof_group=[u'ipausers'],
nsaccountlock=[u'false'],
ipantsecurityidentifier=[fuzzy_user_or_group_sid],
)
for key in self.kwargs:
+6 -6
View File
@@ -137,6 +137,12 @@ fuzzy_bytes = Fuzzy(type=bytes)
def fuzzy_set_ci(s):
return Fuzzy(test=lambda other: set(x.lower() for x in other) == set(y.lower() for y in s))
def fuzzy_set_optional_oc(s, oc):
return Fuzzy(test=lambda other: set(x.lower() for x in other if x != oc)
== set(y.lower() for y in s if y != oc))
try:
if not api.Backend.rpcclient.isconnected():
api.Backend.rpcclient.connect()
@@ -152,12 +158,6 @@ adtrust_is_enabled = api.Command['adtrust_is_enabled']()['result']
sidgen_was_run = api.Command['sidgen_was_run']()['result']
def add_sid(d, check_sidgen=False):
if adtrust_is_enabled and (not check_sidgen or sidgen_was_run):
d['ipantsecurityidentifier'] = (fuzzy_user_or_group_sid,)
return d
def add_oc(l, oc, check_sidgen=False):
if adtrust_is_enabled and (not check_sidgen or sidgen_was_run):
return l + [oc]
+4 -4
View File
@@ -9,7 +9,7 @@ msgid ""
msgstr ""
"Project-Id-Version: freeipa 4.9.0.dev201908140712+gitc9938e3d8\n"
"Report-Msgid-Bugs-To: https://pagure.io/freeipa/new_issue\n"
"POT-Creation-Date: 2021-03-31 09:12+0300\n"
"POT-Creation-Date: 2021-11-25 18:48+0100\n"
"PO-Revision-Date: 2021-01-27 07:52+0000\n"
"Last-Translator: Alexander Bokovoy <abokovoy@redhat.com>\n"
"Language-Team: German <https://translate.fedoraproject.org/projects/freeipa/"
@@ -2368,9 +2368,6 @@ msgstr "Maximale Anzahl der zurückgegebenen Einträge"
msgid "Maximum number of records to search (-1 is unlimited)"
msgstr "Maximale Anzahl an Ergebnissen für Suche (-1 ist unbegrenzt)"
msgid "Maximum password life must be greater than minimum."
msgstr "Maximale Lebensdauer muss größer als das Minimum sein."
msgid "Maximum password lifetime (in days)"
msgstr "Maximale Passwortlebensdauer (in Tagen)"
@@ -4625,3 +4622,6 @@ msgstr "Benutzer und Host können nicht gleichzeitig gesetzt sein."
msgid "users cannot be set when type is 'hostgroup'"
msgstr "users kann nicht gesetzt sein wenn Typ 'hostgroup' ist"
#~ msgid "Maximum password life must be greater than minimum."
#~ msgstr "Maximale Lebensdauer muss größer als das Minimum sein."
Loaded 100 of 111 files, more files were not shown because too many files have changed in this diff. Show more