--- Begin Message ---
Package: release.debian.org
Severity: normal
X-Debbugs-Cc: freeipa@packages.debian.org
Control: affects -1 + src:freeipa
User: release.debian.org@packages.debian.org
Usertags: unblock
Please unblock package freeipa
[ Reason ]
It is marked for autoremoval due to #1108050. The package hasn't aged enough
yet, but by the time it has I will be gone for holidays. Feel free to wait for
two weeks before unblocking.
We are only able to ship the client, so I'm not sure if the recent CVE's affect
only the server, but this fixes them nevertheless.
[ Impact ]
* New upstream release.
- CVE-2024-11029 (Closes: #1093383)
- CVE-2025-4404 (Closes: #1108050)
* control: Demote libnss-myhostname to Suggests. (ref. #1006829)
* patches: Fix samba lock directory location. (Closes: #1012593)
* patches: Map nobody group to nogroup on Debian. (Closes: #1012592)
[ Tests ]
There are no automated tests for the client, as it'd require the server.
[ Risks ]
The upstream version bump includes only five commits to the code, in total.
Most of them affect the server only, meaning that risk to the client is minimal.
No new bugs have been filed since the upload.
[ Checklist ]
[x] all changes are documented in the d/changelog
[x] I reviewed all changes and I approve them
[ ] attach debdiff against the package in testing
[ Other info ]
The debdiff is huge because of some release tooling change which now makes
the tarball include a ton of autotools/translation fluff. So the git diff is
included instead.
unblock freeipa/4.12.4-1
git diff debian/4.12.2-3..debian/4.12.4-1:
diff --git a/VERSION.m4 b/VERSION.m4
index d57acf130..8aa7f65c6 100644
--- a/VERSION.m4
+++ b/VERSION.m4
@@ -21,7 +21,7 @@
########################################################
define(IPA_VERSION_MAJOR, 4)
define(IPA_VERSION_MINOR, 12)
-define(IPA_VERSION_RELEASE, 2)
+define(IPA_VERSION_RELEASE, 4)
########################################################
# For 'pre' releases the version will be #
diff --git a/daemons/ipa-kdb/ipa_kdb.c b/daemons/ipa-kdb/ipa_kdb.c
index 903e19e83..531ee223e 100644
--- a/daemons/ipa-kdb/ipa_kdb.c
+++ b/daemons/ipa-kdb/ipa_kdb.c
@@ -530,26 +530,43 @@ int ipadb_get_connection(struct ipadb_context *ipactx)
/* get adtrust options using default refresh interval */
ret = ipadb_reinit_mspac(ipactx, false, &stmsg);
- if (ret && stmsg)
- krb5_klog_syslog(LOG_WARNING, "MS-PAC generator: %s", stmsg);
+ if (ret) {
+ if (stmsg) {
+ krb5_klog_syslog(LOG_WARNING, "MS-PAC generator: %s", stmsg);
+ }
+ /* Initialization of the MS-PAC generator is an optional dependency.
+ * Fail only if the connection was lost. */
+ if (!ipactx->lcontext) {
+ goto done;
+ }
+ }
ret = 0;
done:
ldap_msgfree(res);
+ /* LDAP context should never be null on success, but keep this test out of
+ * security to make sure we do not return an invalid context. */
+ if (ret == 0 && !ipactx->lcontext) {
+ krb5_klog_syslog(LOG_WARNING, "Internal malfunction: LDAP connection "
+ "process resulted in an invalid context "
+ "(please report this incident)");
+ ret = LDAP_SERVER_DOWN;
+ }
+
if (ret) {
+ /* Cleanup LDAP context if connection failed. */
if (ipactx->lcontext) {
ldap_unbind_ext_s(ipactx->lcontext, NULL, NULL);
ipactx->lcontext = NULL;
}
- if (ret == LDAP_SERVER_DOWN) {
- return ETIMEDOUT;
- }
- return EIO;
+
+ /* Replace LDAP error code by POSIX error code. */
+ ret = ret == LDAP_SERVER_DOWN ? ETIMEDOUT : EIO;
}
- return 0;
+ return ret;
}
static krb5_principal ipadb_create_local_tgs(krb5_context kcontext,
diff --git a/daemons/ipa-otpd/oauth2.c b/daemons/ipa-otpd/oauth2.c
index a33cf5171..52d7d7c9c 100644
--- a/daemons/ipa-otpd/oauth2.c
+++ b/daemons/ipa-otpd/oauth2.c
@@ -31,6 +31,7 @@
#include <unistd.h>
#include <fcntl.h>
#include <sys/random.h>
+#include <sys/uio.h>
#include "internal.h"
@@ -93,6 +94,7 @@ static void oauth2_on_child_writable(verto_ctx *vctx, verto_ev *ev)
(void)vctx; /* Unused */
ssize_t io;
struct child_ctx *child_ctx;
+ struct iovec iov[3];
child_ctx = verto_get_private(ev);
if (child_ctx == NULL) {
@@ -102,15 +104,18 @@ static void oauth2_on_child_writable(verto_ctx *vctx, verto_ev *ev)
}
if (child_ctx->oauth2_state == OAUTH2_GET_DEVICE_CODE) {
- /* no input needed */
- verto_del(ev);
- return;
- }
-
+ io = write(verto_get_fd(ev), child_ctx->item->idp.ipaidpClientSecret,
+ strlen(child_ctx->item->idp.ipaidpClientSecret));
+ } else {
+ iov[0].iov_base = child_ctx->item->idp.ipaidpClientSecret;
+ iov[0].iov_len = strlen(child_ctx->item->idp.ipaidpClientSecret);
+ iov[1].iov_base = "\n";
+ iov[1].iov_len = 1;
+ iov[2].iov_base = child_ctx->saved_item->oauth2.device_code_reply;
+ iov[2].iov_len = strlen(child_ctx->saved_item->oauth2.device_code_reply);
- io = write(verto_get_fd(ev),
- child_ctx->saved_item->oauth2.device_code_reply,
- strlen(child_ctx->saved_item->oauth2.device_code_reply));
+ io = writev(verto_get_fd(ev), iov, 3);
+ }
otpd_queue_item_free(child_ctx->saved_item);
if (io < 0) {
@@ -429,8 +434,7 @@ int oauth2(struct otpd_queue_item **item, enum oauth2_state oauth2_state)
args[args_idx++] = (*item)->idp.ipaidpClientID;
if ((*item)->idp.ipaidpClientSecret) {
- args[args_idx++] = "--client-secret";
- args[args_idx++] = (*item)->idp.ipaidpClientSecret;
+ args[args_idx++] = "--client-secret-stdin";
}
if ((*item)->idp.ipaidpScope) {
diff --git a/debian/changelog b/debian/changelog
index 43dfdcf22..411eaaf42 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,14 @@
+freeipa (4.12.4-1) unstable; urgency=medium
+
+ * New upstream release.
+ - CVE-2024-11029 (Closes: #1093383)
+ - CVE-2025-4404 (Closes: #1108050)
+ * control: Demote libnss-myhostname to Suggests. (ref. #1006829)
+ * patches: Fix samba lock directory location. (Closes: #1012593)
+ * patches: Map nobody group to nogroup on Debian. (Closes: #1012592)
+
+ -- Timo Aaltonen <tjaalton@debian.org> Wed, 25 Jun 2025 12:40:45 +0300
+
freeipa (4.12.2-3) unstable; urgency=medium
* control: Add libnss-myhostname to client depends. (Closes: #1006829)
diff --git a/debian/control b/debian/control
index 2076c4df0..55e38b653 100644
--- a/debian/control
+++ b/debian/control
@@ -75,7 +75,6 @@ Depends:
freeipa-common (= ${source:Version}),
krb5-user,
libnss3-tools,
- libnss-myhostname,
libnss-sss,
libpam-sss,
libsasl2-modules-gssapi-mit,
@@ -94,6 +93,7 @@ Recommends:
chrony,
sssd-passkey,
Suggests:
+ libnss-myhostname,
libpam-krb5,
Description: FreeIPA centralized identity framework -- client
FreeIPA is an integrated solution to provide centrally managed Identity
diff --git a/debian/control.common b/debian/control.common
index 2b13c8cb4..871f4df4f 100644
--- a/debian/control.common
+++ b/debian/control.common
@@ -22,7 +22,6 @@ Depends:
freeipa-common (= ${source:Version}),
krb5-user,
libnss3-tools,
- libnss-myhostname,
libnss-sss,
libpam-sss,
libsasl2-modules-gssapi-mit,
@@ -41,6 +40,7 @@ Recommends:
chrony,
sssd-passkey,
Suggests:
+ libnss-myhostname,
libpam-krb5,
Description: FreeIPA centralized identity framework -- client
FreeIPA is an integrated solution to provide centrally managed Identity
diff --git a/debian/patches/Make-name-of-nobody-group-configurable-and-use-nogro.patch b/debian/patches/Make-name-of-nobody-group-configurable-and-use-nogro.patch
new file mode 100644
index 000000000..8c4a3bb6d
--- /dev/null
+++ b/debian/patches/Make-name-of-nobody-group-configurable-and-use-nogro.patch
@@ -0,0 +1,60 @@
+From 8a9c5629214cd71e88a5ac9c1b639faad9fc4ec1 Mon Sep 17 00:00:00 2001
+From: Frederik Himpe <frederik@frehi.be>
+Date: Tue, 25 Feb 2025 13:49:23 +0100
+Subject: [PATCH] Make name of nobody group configurable and use nogroup on
+ Debian
+
+Fixes: https://pagure.io/freeipa/issue/9753
+
+Signed-off-by: Frederik Himpe <frederik@frehi.be>
+Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
+---
+ ipaplatform/base/constants.py | 1 +
+ ipaplatform/debian/constants.py | 1 +
+ ipaserver/install/adtrustinstance.py | 6 ++++--
+ 3 files changed, 6 insertions(+), 2 deletions(-)
+
+diff --git a/ipaplatform/base/constants.py b/ipaplatform/base/constants.py
+index f1ef7efff..4c8038a84 100644
+--- a/ipaplatform/base/constants.py
++++ b/ipaplatform/base/constants.py
+@@ -124,6 +124,7 @@ class BaseConstantsNamespace:
+ NAMED_OPTIONS_VAR = "OPTIONS"
+ NAMED_OPENSSL_ENGINE = None
+ NAMED_ZONE_COMMENT = ""
++ NOBODY_GROUP = Group("nobody")
+ PKI_USER = User("pkiuser")
+ PKI_GROUP = Group("pkiuser")
+ # ntpd init variable used for daemon options
+diff --git a/ipaplatform/debian/constants.py b/ipaplatform/debian/constants.py
+index 7216694ad..f8ee8cf9f 100644
+--- a/ipaplatform/debian/constants.py
++++ b/ipaplatform/debian/constants.py
+@@ -29,5 +29,6 @@ class DebianConstantsNamespace(BaseConstantsNamespace):
+ ODS_USER = User("opendnssec")
+ ODS_GROUP = Group("opendnssec")
+ SECURE_NFS_VAR = "NEED_GSSD"
++ NOBODY_GROUP = Group("nogroup")
+
+ constants = DebianConstantsNamespace()
+diff --git a/ipaserver/install/adtrustinstance.py b/ipaserver/install/adtrustinstance.py
+index fd5a5a282..df2586ef1 100644
+--- a/ipaserver/install/adtrustinstance.py
++++ b/ipaserver/install/adtrustinstance.py
+@@ -123,9 +123,11 @@ def make_netbios_name(s):
+ def map_Guests_to_nobody():
+ env = {'LC_ALL': 'C'}
+ args = [paths.NET, '-s', '/dev/null', 'groupmap', 'add',
+- 'sid=S-1-5-32-546', 'unixgroup=nobody', 'type=builtin']
++ 'sid=S-1-5-32-546',
++ 'unixgroup="' + constants.NOBODY_GROUP + '"', 'type=builtin']
+
+- logger.debug("Map BUILTIN\\Guests to a group 'nobody'")
++ logger.debug("Map BUILTIN\\Guests to a group '%s'",
++ constants.NOBODY_GROUP)
+ ipautil.run(args, env=env, raiseonerr=False, capture_error=True)
+
+
+--
+2.48.1
+
diff --git a/debian/patches/Make-path-of-Samba-lock-directory-configurable-and-u.patch b/debian/patches/Make-path-of-Samba-lock-directory-configurable-and-u.patch
new file mode 100644
index 000000000..c4dd9801a
--- /dev/null
+++ b/debian/patches/Make-path-of-Samba-lock-directory-configurable-and-u.patch
@@ -0,0 +1,54 @@
+From 1e47185289fbbe29eedca82a4872d1d075b26c26 Mon Sep 17 00:00:00 2001
+From: Frederik Himpe <frederik@frehi.be>
+Date: Thu, 27 Feb 2025 11:28:30 +0100
+Subject: [PATCH] Make path of Samba lock directory configurable and use
+ /run/samba on Debian
+
+Signed-off-by: Frederik Himpe <frederik@frehi.be>
+Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
+---
+ ipaplatform/base/paths.py | 1 +
+ ipaplatform/debian/paths.py | 1 +
+ ipaserver/install/adtrustinstance.py | 2 +-
+ 3 files changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/ipaplatform/base/paths.py b/ipaplatform/base/paths.py
+index 6a62d7bd0..f794aae6d 100644
+--- a/ipaplatform/base/paths.py
++++ b/ipaplatform/base/paths.py
+@@ -350,6 +350,7 @@ class BasePathNamespace:
+ KRA_CS_CFG_PATH = "/var/lib/pki/pki-tomcat/conf/kra/CS.cfg"
+ KRACERT_P12 = "/root/kracert.p12"
+ SAMBA_DIR = "/var/lib/samba"
++ SAMBA_LOCKDIR = "/var/lib/samba/lock"
+ SSSD_DB = "/var/lib/sss/db"
+ SSSD_MC_GROUP = "/var/lib/sss/mc/group"
+ SSSD_MC_PASSWD = "/var/lib/sss/mc/passwd"
+diff --git a/ipaplatform/debian/paths.py b/ipaplatform/debian/paths.py
+index 7a8099680..229f185f0 100644
+--- a/ipaplatform/debian/paths.py
++++ b/ipaplatform/debian/paths.py
+@@ -44,6 +44,7 @@ class DebianPathNamespace(BasePathNamespace):
+ OPENSSL_DIR = "/usr/lib/ssl"
+ OPENSSL_CERTS_DIR = "/usr/lib/ssl/certs"
+ OPENSSL_PRIVATE_DIR = "/usr/lib/ssl/private"
++ SAMBA_LOCKDIR = "/run/samba"
+ ETC_DEBIAN_VERSION = "/etc/debian_version"
+ # Old versions of freeipa wrote all trusted certificates to a single
+ # file, which is not supported by ca-certificates.
+diff --git a/ipaserver/install/adtrustinstance.py b/ipaserver/install/adtrustinstance.py
+index df2586ef1..bc3a282e6 100644
+--- a/ipaserver/install/adtrustinstance.py
++++ b/ipaserver/install/adtrustinstance.py
+@@ -962,7 +962,7 @@ class ADTRUSTInstance(service.Service):
+ # 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")):
++ paths.SAMBA_LOCKDIR):
+ if os.path.isdir(smbpath):
+ tdb_files = [
+ os.path.join(smbpath, tdb_file)
+--
+2.48.1
+
diff --git a/debian/patches/series b/debian/patches/series
index a9d368b1b..4189f9bfc 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -6,3 +6,5 @@ fix-sssd-socket-activation.diff
# send upstream
map-ssh-service.diff
+Make-path-of-Samba-lock-directory-configurable-and-u.patch
+Make-name-of-nobody-group-configurable-and-use-nogro.patch
diff --git a/install/oddjob/com.redhat.idm.trust-fetch-domains.in b/install/oddjob/com.redhat.idm.trust-fetch-domains.in
index 45c1f1463..b86be0212 100644
--- a/install/oddjob/com.redhat.idm.trust-fetch-domains.in
+++ b/install/oddjob/com.redhat.idm.trust-fetch-domains.in
@@ -15,6 +15,7 @@ import six
import gssapi
from ipalib.install.kinit import kinit_keytab, kinit_password
+from ipapython.admintool import admin_cleanup_global_argv
if six.PY3:
unicode = str
@@ -52,11 +53,13 @@ def parse_options():
"--password",
action="store",
dest="password",
- help="Display debugging information",
+ help="Password for Active Directory administrator",
+ sensitive=True
)
options, args = parser.parse_args()
safe_options = parser.get_safe_opts(options)
+ admin_cleanup_global_argv(parser, options, sys.argv)
# We only use first argument of the passed args but as D-BUS interface
# in oddjobd cannot expose optional, we fill in empty slots from IPA side
diff --git a/install/share/bootstrap-template.ldif b/install/share/bootstrap-template.ldif
index 325eb8450..94972eb72 100644
--- a/install/share/bootstrap-template.ldif
+++ b/install/share/bootstrap-template.ldif
@@ -239,6 +239,7 @@ objectClass: ipasshuser
uid: admin
krbPrincipalName: admin@$REALM
krbPrincipalName: root@$REALM
+krbCanonicalName: admin@$REALM
cn: Administrator
sn: Administrator
uidNumber: $IDSTART
diff --git a/install/tools/ipa-adtrust-install.in b/install/tools/ipa-adtrust-install.in
index cb2b78e50..1efccdb67 100644
--- a/install/tools/ipa-adtrust-install.in
+++ b/install/tools/ipa-adtrust-install.in
@@ -29,19 +29,17 @@ import sys
import six
-from optparse import SUPPRESS_HELP # pylint: disable=deprecated-module
-
from ipalib.install import sysrestore
from ipaserver.install import adtrust, service
from ipaserver.install.installutils import (
read_password,
check_server_configuration,
run_script)
-from ipapython.admintool import ScriptError
+from ipapython.admintool import ScriptError, admin_cleanup_global_argv
from ipapython import version
from ipapython import ipautil
from ipalib import api, errors, krb_utils
-from ipapython.config import IPAOptionParser
+from ipapython.config import IPAOptionParser, SUPPRESS_HELP
from ipaplatform.paths import paths
from ipapython.ipa_log_manager import standard_logging_setup
@@ -95,6 +93,7 @@ def parse_options():
options, _args = parser.parse_args()
safe_options = parser.get_safe_opts(options)
+ admin_cleanup_global_argv(parser, options, sys.argv)
return safe_options, options
diff --git a/install/tools/ipa-ca-install.in b/install/tools/ipa-ca-install.in
index 9f3d16669..b437e761f 100644
--- a/install/tools/ipa-ca-install.in
+++ b/install/tools/ipa-ca-install.in
@@ -42,6 +42,7 @@ from ipalib.constants import DOMAIN_LEVEL_1
from ipapython.config import IPAOptionParser
from ipapython.ipa_log_manager import standard_logging_setup
from ipaplatform.paths import paths
+from ipapython.admintool import admin_cleanup_global_argv
logger = logging.getLogger(os.path.basename(__file__))
@@ -132,6 +133,7 @@ def parse_options():
options, args = parser.parse_args()
safe_options = parser.get_safe_opts(options)
+ admin_cleanup_global_argv(parser, options, sys.argv)
if args:
parser.error("Too many arguments provided")
diff --git a/install/tools/ipa-compat-manage.in b/install/tools/ipa-compat-manage.in
index 459f39fc8..9650abd6f 100644
--- a/install/tools/ipa-compat-manage.in
+++ b/install/tools/ipa-compat-manage.in
@@ -24,13 +24,13 @@ from __future__ import print_function
import sys
from ipaplatform.paths import paths
try:
- from optparse import OptionParser # pylint: disable=deprecated-module
from ipapython import ipautil, config
from ipaserver.install import installutils
from ipaserver.install.ldapupdate import LDAPUpdate
from ipalib import api, errors
from ipapython.ipa_log_manager import standard_logging_setup
from ipapython.dn import DN
+ from ipapython.admintool import admin_cleanup_global_argv
except ImportError as e:
print("""\
There was a problem importing one of the required Python modules. The
@@ -46,7 +46,8 @@ nis_config_dn = DN(('cn', 'NIS Server'), ('cn', 'plugins'), ('cn', 'config'))
def parse_options():
usage = "%prog [options] <enable|disable|status>\n"
usage += "%prog [options]\n"
- parser = OptionParser(usage=usage, formatter=config.IPAFormatter())
+ parser = config.IPAOptionParser(usage=usage,
+ formatter=config.IPAFormatter())
parser.add_option("-d", "--debug", action="store_true", dest="debug",
help="Display debugging information about the update(s)")
@@ -55,6 +56,7 @@ def parse_options():
config.add_standard_options(parser)
options, args = parser.parse_args()
+ admin_cleanup_global_argv(parser, options, sys.argv)
return options, args
diff --git a/install/tools/ipa-csreplica-manage.in b/install/tools/ipa-csreplica-manage.in
index 6f248cc50..2fab27a94 100644
--- a/install/tools/ipa-csreplica-manage.in
+++ b/install/tools/ipa-csreplica-manage.in
@@ -32,8 +32,8 @@ from ipaserver.install import (replication, installutils, bindinstance,
from ipalib import api, errors
from ipalib.constants import FQDN
from ipalib.util import has_managed_topology, print_replication_status
-from ipapython import ipautil, ipaldap, version
-from ipapython.admintool import ScriptError
+from ipapython import ipautil, ipaldap, version, config
+from ipapython.admintool import admin_cleanup_global_argv, ScriptError
from ipapython.dn import DN
logger = logging.getLogger(os.path.basename(__file__))
@@ -54,11 +54,10 @@ commands = {
def parse_options():
- from optparse import OptionParser # pylint: disable=deprecated-module
-
- parser = OptionParser(version=version.VERSION)
+ parser = config.IPAOptionParser(version=version.VERSION)
parser.add_option("-H", "--host", dest="host", help="starting host")
- parser.add_option("-p", "--password", dest="dirman_passwd", help="Directory Manager password")
+ parser.add_option("-p", "--password", dest="dirman_passwd", sensitive=True,
+ help="Directory Manager password")
parser.add_option("-v", "--verbose", dest="verbose", action="store_true", default=False,
help="provide additional information")
parser.add_option("-f", "--force", dest="force", action="store_true", default=False,
@@ -66,6 +65,7 @@ def parse_options():
parser.add_option("--from", dest="fromhost", help="Host to get data from")
options, args = parser.parse_args()
+ admin_cleanup_global_argv(parser, options, sys.argv)
valid_syntax = False
diff --git a/install/tools/ipa-managed-entries.in b/install/tools/ipa-managed-entries.in
index e9be41b7a..ff2fd6a58 100644
--- a/install/tools/ipa-managed-entries.in
+++ b/install/tools/ipa-managed-entries.in
@@ -24,7 +24,6 @@ import logging
import os
import re
import sys
-from optparse import OptionParser # pylint: disable=deprecated-module
from ipaplatform.paths import paths
from ipapython import config
@@ -32,6 +31,7 @@ from ipaserver.install import installutils
from ipalib import api, errors
from ipapython.ipa_log_manager import standard_logging_setup
from ipapython.dn import DN
+from ipapython.admintool import admin_cleanup_global_argv
logger = logging.getLogger(os.path.basename(__file__))
@@ -39,7 +39,8 @@ logger = logging.getLogger(os.path.basename(__file__))
def parse_options():
usage = "%prog [options] <status|enable|disable>\n"
usage += "%prog [options]\n"
- parser = OptionParser(usage=usage, formatter=config.IPAFormatter())
+ parser = config.IPAOptionParser(usage=usage,
+ formatter=config.IPAFormatter())
parser.add_option("-d", "--debug", action="store_true", dest="debug",
help="Display debugging information about the update(s)")
@@ -50,9 +51,10 @@ def parse_options():
action="store_true",
help="List available Managed Entries")
parser.add_option("-p", "--password", dest="dirman_password",
- help="Directory Manager password")
+ sensitive=True, help="Directory Manager password")
options, args = parser.parse_args()
+ admin_cleanup_global_argv(parser, options, sys.argv)
return options, args
diff --git a/install/tools/ipa-replica-conncheck.in b/install/tools/ipa-replica-conncheck.in
index 8eee82483..81b7d13ac 100644
--- a/install/tools/ipa-replica-conncheck.in
+++ b/install/tools/ipa-replica-conncheck.in
@@ -23,15 +23,15 @@ from __future__ import print_function
import logging
from ipapython import ipachangeconf
-from ipapython.config import IPAOptionParser
+from ipapython.config import (IPAOptionParser, OptionGroup,
+ OptionValueError)
+from ipapython.admintool import admin_cleanup_global_argv
from ipapython.dn import DN
from ipapython import version
from ipapython import ipautil, certdb
from ipalib import api, errors, x509
from ipalib.constants import FQDN
from ipaserver.install import installutils
-# pylint: disable=deprecated-module
-from optparse import OptionGroup, OptionValueError
# pylint: enable=deprecated-module
from ipapython.ipa_log_manager import standard_logging_setup
import copy
@@ -189,6 +189,7 @@ def parse_options():
options, _args = parser.parse_args()
safe_options = parser.get_safe_opts(options)
+ admin_cleanup_global_argv(parser, options, sys.argv)
if options.master and options.replica:
parser.error("on-master and on-replica options are mutually exclusive!")
diff --git a/install/tools/ipa-replica-manage.in b/install/tools/ipa-replica-manage.in
index d6e6ef57c..7e5b31a59 100644
--- a/install/tools/ipa-replica-manage.in
+++ b/install/tools/ipa-replica-manage.in
@@ -43,6 +43,7 @@ from ipalib.util import (
print_replication_status,
verify_host_resolvable,
)
+from ipapython.admintool import admin_cleanup_global_argv
from ipapython.ipa_log_manager import standard_logging_setup
from ipapython.dn import DN
from ipapython.config import IPAOptionParser
@@ -84,7 +85,8 @@ class NoRUVsFound(Exception):
def parse_options():
parser = IPAOptionParser(version=version.VERSION)
parser.add_option("-H", "--host", dest="host", help="starting host")
- parser.add_option("-p", "--password", dest="dirman_passwd", help="Directory Manager password")
+ parser.add_option("-p", "--password", dest="dirman_passwd", sensitive=True,
+ help="Directory Manager password")
parser.add_option("-v", "--verbose", dest="verbose", action="store_true", default=False,
help="provide additional information")
parser.add_option("-d", "--debug", dest="debug", action="store_true", default=False,
@@ -95,7 +97,7 @@ def parse_options():
help="DANGER: clean up references to a ghost master")
parser.add_option("--binddn", dest="binddn", default=None, type="dn",
help="Bind DN to use with remote server")
- parser.add_option("--bindpw", dest="bindpw", default=None,
+ parser.add_option("--bindpw", dest="bindpw", default=None, sensitive=True,
help="Password for Bind DN to use with remote server")
parser.add_option("--winsync", dest="winsync", action="store_true", default=False,
help="This is a Windows Sync Agreement")
@@ -103,13 +105,15 @@ def parse_options():
help="Full path and filename of CA certificate to use with TLS/SSL to the remote server")
parser.add_option("--win-subtree", dest="win_subtree", default=None,
help="DN of Windows subtree containing the users you want to sync (default cn=Users,<domain suffix)")
- parser.add_option("--passsync", dest="passsync", default=None,
+ parser.add_option("--passsync", dest="passsync",
+ default=None, sensitive=True,
help="Password for the IPA system user used by the Windows PassSync plugin to synchronize passwords")
parser.add_option("--from", dest="fromhost", help="Host to get data from")
parser.add_option("--no-lookup", dest="nolookup", action="store_true", default=False,
help="do not perform DNS lookup checks")
options, args = parser.parse_args()
+ admin_cleanup_global_argv(parser, options, sys.argv)
valid_syntax = False
diff --git a/install/updates/90-post_upgrade_plugins.update b/install/updates/90-post_upgrade_plugins.update
index bb48f483c..458d81045 100644
--- a/install/updates/90-post_upgrade_plugins.update
+++ b/install/updates/90-post_upgrade_plugins.update
@@ -27,6 +27,7 @@ plugin: update_mapping_Guests_to_nobody
plugin: fix_kra_people_entry
plugin: update_pwpolicy
plugin: update_pwpolicy_grace
+plugin: add_admin_krbcanonicalname
# last
# DNS version 1
diff --git a/ipaclient/install/ipa_client_automount.py b/ipaclient/install/ipa_client_automount.py
index 4439932bd..9f49ff9ed 100644
--- a/ipaclient/install/ipa_client_automount.py
+++ b/ipaclient/install/ipa_client_automount.py
@@ -34,7 +34,6 @@ import SSSDConfig
from six.moves.urllib.parse import urlsplit
-from optparse import OptionParser # pylint: disable=deprecated-module
from ipapython import ipachangeconf
from ipaclient import discovery
from ipaclient.install.client import (
@@ -52,6 +51,7 @@ from ipaplatform.tasks import tasks
from ipaplatform import services
from ipaplatform.paths import paths
from ipapython.admintool import ScriptError
+from ipapython.config import IPAOptionParser
logger = logging.getLogger(os.path.basename(__file__))
@@ -59,7 +59,7 @@ logger = logging.getLogger(os.path.basename(__file__))
def parse_options():
usage = "%prog [options]\n"
- parser = OptionParser(usage=usage)
+ parser = IPAOptionParser(usage=usage)
parser.add_option("--server", dest="server", help="FQDN of IPA server")
parser.add_option(
"--location",
diff --git a/ipaclient/install/ipa_client_samba.py b/ipaclient/install/ipa_client_samba.py
index 81d670c34..5c33abb4c 100755
--- a/ipaclient/install/ipa_client_samba.py
+++ b/ipaclient/install/ipa_client_samba.py
@@ -9,7 +9,6 @@ import logging
import os
import gssapi
from urllib.parse import urlsplit
-from optparse import OptionParser # pylint: disable=deprecated-module
from contextlib import contextmanager
from ipaclient import discovery
@@ -31,6 +30,7 @@ from ipaplatform.constants import constants
from ipaplatform import services
from ipapython.admintool import ScriptError
from samba import generate_random_password
+from ipapython.config import IPAOptionParser
logger = logging.getLogger(os.path.basename(__file__))
logger.setLevel(logging.DEBUG)
@@ -68,7 +68,7 @@ def use_api_as_principal(principal, keytab):
def parse_options():
usage = "%prog [options]\n"
- parser = OptionParser(usage=usage)
+ parser = IPAOptionParser(usage=usage)
parser.add_option(
"--server",
dest="server",
diff --git a/ipalib/cli.py b/ipalib/cli.py
index d9c2ac165..667b213fd 100644
--- a/ipalib/cli.py
+++ b/ipalib/cli.py
@@ -30,7 +30,6 @@ import textwrap
import sys
import getpass
import code
-import optparse # pylint: disable=deprecated-module
import os
import pprint
import fcntl
@@ -71,6 +70,8 @@ from ipalib.text import _
from ipalib import api
from ipapython.dnsutil import DNSName
from ipapython.admintool import ScriptError
+from ipapython.config import (IPAOptionParser, IPAFormatter,
+ OptionGroup, make_option)
import datetime
@@ -1121,7 +1122,8 @@ class Collector:
def __todict__(self):
return dict(self.__options)
-class CLIOptionParserFormatter(optparse.IndentedHelpFormatter):
+
+class CLIOptionParserFormatter(IPAFormatter):
def format_argument(self, name, help_string):
result = []
opt_width = self.help_position - self.current_indent - 2
@@ -1141,7 +1143,8 @@ class CLIOptionParserFormatter(optparse.IndentedHelpFormatter):
result.append("\n")
return "".join(result)
-class CLIOptionParser(optparse.OptionParser):
+
+class CLIOptionParser(IPAOptionParser):
"""
This OptionParser subclass adds an ability to print positional
arguments in CLI help. Custom formatter is used to format the argument
@@ -1151,13 +1154,13 @@ class CLIOptionParser(optparse.OptionParser):
self._arguments = []
if 'formatter' not in kwargs:
kwargs['formatter'] = CLIOptionParserFormatter()
- optparse.OptionParser.__init__(self, *args, **kwargs)
+ IPAOptionParser.__init__(self, *args, **kwargs)
def format_option_help(self, formatter=None):
"""
Prepend argument help to standard OptionParser's option help
"""
- option_help = optparse.OptionParser.format_option_help(self, formatter)
+ option_help = IPAOptionParser.format_option_help(self, formatter)
if isinstance(formatter, CLIOptionParserFormatter):
heading = unicode(_("Positional arguments"))
@@ -1272,7 +1275,7 @@ class cli(backend.Executioner):
"""Get or create an option group for the given name"""
option_group = option_groups.get(group_name)
if option_group is None:
- option_group = optparse.OptionGroup(parser, group_name)
+ option_group = OptionGroup(parser, group_name)
parser.add_option_group(option_group)
option_groups[group_name] = option_group
return option_group
@@ -1298,7 +1301,7 @@ class cli(backend.Executioner):
option_names = ['--%s' % cli_name]
if option.cli_short_name:
option_names.append('-%s' % option.cli_short_name)
- opt = optparse.make_option(*option_names, **kw)
+ opt = make_option(*option_names, **kw)
if option.option_group is None:
parser.add_option(opt)
else:
@@ -1312,7 +1315,7 @@ class cli(backend.Executioner):
group = _get_option_group(unicode(_('Deprecated options')))
for alias in option.deprecated_cli_aliases:
name = '--%s' % alias
- group.add_option(optparse.make_option(name, **new_kw))
+ group.add_option(make_option(name, **new_kw))
for arg in cmd.args():
name = self.__get_arg_name(arg, format_name=False)
@@ -1442,7 +1445,7 @@ class cli(backend.Executioner):
)
-class IPAHelpFormatter(optparse.IndentedHelpFormatter):
+class IPAHelpFormatter(IPAFormatter):
"""Formatter suitable for printing IPA command help
The default help formatter reflows text to fit the terminal, but it
diff --git a/ipalib/plugable.py b/ipalib/plugable.py
index 2e2861df0..a87e6e891 100644
--- a/ipalib/plugable.py
+++ b/ipalib/plugable.py
@@ -33,7 +33,6 @@ import sys
import threading
import os
from os import path
-import optparse # pylint: disable=deprecated-module
import textwrap
import collections
import importlib
@@ -47,6 +46,7 @@ from ipalib.util import classproperty
from ipalib.base import ReadOnly, lock, islocked
from ipalib.constants import DEFAULT_CONFIG
from ipapython import ipa_log_manager, ipautil
+from ipapython.config import IPAOptionParser, IPAFormatter
from ipapython.ipa_log_manager import (
LOGGING_FORMAT_FILE,
LOGGING_FORMAT_STDERR)
@@ -526,7 +526,7 @@ class API(ReadOnly):
def build_global_parser(self, parser=None, context=None):
"""
- Add global options to an optparse.OptionParser instance.
+ Add global options to an IPAOptionParser instance.
"""
def config_file_callback(option, opt, value, parser):
if not os.path.isfile(value):
@@ -536,7 +536,7 @@ class API(ReadOnly):
parser.values.conf = value
if parser is None:
- parser = optparse.OptionParser(
+ parser = IPAOptionParser(
add_help_option=False,
formatter=IPAHelpFormatter(),
usage='%prog [global-options] COMMAND [command-options]',
@@ -821,7 +821,7 @@ class API(ReadOnly):
return self.__next[plugin]
-class IPAHelpFormatter(optparse.IndentedHelpFormatter):
+class IPAHelpFormatter(IPAFormatter):
def format_epilog(self, epilog):
text_width = self.width - self.current_indent
indent = " " * self.current_indent
diff --git a/ipapython/admintool.py b/ipapython/admintool.py
index fdb4400d8..602223ef6 100644
--- a/ipapython/admintool.py
+++ b/ipapython/admintool.py
@@ -26,7 +26,6 @@ import logging
import sys
import os
import traceback
-from optparse import OptionGroup # pylint: disable=deprecated-module
from ipaplatform.osinfo import osinfo
from ipapython import version
@@ -40,6 +39,45 @@ SERVER_NOT_CONFIGURED = 2
logger = logging.getLogger(__name__)
+def admin_cleanup_global_argv(option_parser, options, argv):
+ """Takes option parser and generated options and scrubs sensitive arguments
+ from the global program arguments. Note that this only works for GNU GLIBC
+ as Python has no generic way to get access to the original argv values to
+ modify them in place.
+
+ The code assumes Python behavior, e.g. there are two additional args in the
+ list (/path/to/python -I ...) than what's passed as 'argv' here.
+ """
+ import ctypes
+ import ctypes.util
+ try:
+ _c = ctypes.CDLL(ctypes.util.find_library("c"))
+ if _c._name is None:
+ return
+ _argv = ctypes.POINTER(ctypes.c_voidp).in_dll(_c, "_dl_argv")
+ # since we run as 'python -I <executable> ...', add two args
+ _argc = len(argv) + 2
+ all_options = []
+ if '_get_all_options' in dir(option_parser):
+ # OptParse parser
+ all_options = option_parser._get_all_options()
+ elif '_actions' in dir(option_parser):
+ # ArgParse parser
+ all_options = option_parser._actions
+
+ for opt in all_options:
+ if getattr(opt, 'sensitive', False):
+ v = getattr(options, opt.dest)
+ for i in range(0, _argc):
+ vi = ctypes.cast(_argv[i],
+ ctypes.c_char_p
+ ).value.decode('utf-8')
+ if vi == v:
+ ctypes.memset(_argv[i], ord('X'), len(v))
+ except Exception:
+ pass
+
+
class ScriptError(Exception):
"""An exception that records an error message and a return value
"""
@@ -113,7 +151,7 @@ class AdminTool:
:param parser: The parser to add options to
:param debug_option: Add a --debug option as an alias to --verbose
"""
- group = OptionGroup(parser, "Logging and output options")
+ group = config.OptionGroup(parser, "Logging and output options")
group.add_option("-v", "--verbose", dest="verbose", default=False,
action="store_true", help="print debugging information")
if debug_option:
@@ -149,6 +187,7 @@ class AdminTool:
cls._option_parsers[cls] = cls.option_parser
options, args = cls.option_parser.parse_args(argv[1:])
+ admin_cleanup_global_argv(cls.option_parser, options, argv)
command_class = cls.get_command_class(options, args)
command = command_class(options, args)
diff --git a/ipapython/config.py b/ipapython/config.py
index f53d0f998..7af4dfdeb 100644
--- a/ipapython/config.py
+++ b/ipapython/config.py
@@ -18,9 +18,9 @@
#
from __future__ import absolute_import
-# pylint: disable=deprecated-module
-from optparse import (
- Option, Values, OptionParser, IndentedHelpFormatter, OptionValueError)
+# pylint: disable=deprecated-module, disable=unused-import
+from optparse import (Option, Values, OptionGroup, OptionParser, SUPPRESS_HELP,
+ IndentedHelpFormatter, OptionValueError, make_option)
# pylint: enable=deprecated-module
from copy import copy
from configparser import ConfigParser as SafeConfigParser
@@ -113,10 +113,14 @@ class IPAOptionParser(OptionParser):
description=None,
formatter=None,
add_help_option=True,
- prog=None):
- OptionParser.__init__(self, usage, option_list, option_class,
- version, conflict_handler, description,
- formatter, add_help_option, prog)
+ prog=None,
+ epilog=None):
+ OptionParser.__init__(self, usage=usage, option_list=option_list,
+ option_class=option_class, version=version,
+ conflict_handler=conflict_handler,
+ description=description, formatter=formatter,
+ add_help_option=add_help_option, prog=prog,
+ epilog=epilog)
def get_safe_opts(self, opts):
"""
diff --git a/ipapython/install/cli.py b/ipapython/install/cli.py
index ab212be4e..a048b3c7c 100644
--- a/ipapython/install/cli.py
+++ b/ipapython/install/cli.py
@@ -9,12 +9,11 @@ Command line support.
import collections
import enum
import logging
-import optparse # pylint: disable=deprecated-module
import signal
import six
-from ipapython import admintool
+from ipapython import admintool, config
from ipapython.ipa_log_manager import standard_logging_setup
from ipapython.ipautil import (CheckedIPAddress, CheckedIPAddressLoopback,
private_ccache)
@@ -158,7 +157,7 @@ class ConfigureTool(admintool.AdminTool):
try:
opt_group = groups[group_cls]
except KeyError:
- opt_group = groups[group_cls] = optparse.OptionGroup(
+ opt_group = groups[group_cls] = config.OptionGroup(
parser, "{0} options".format(group_cls.description))
parser.add_option_group(opt_group)
@@ -232,7 +231,7 @@ class ConfigureTool(admintool.AdminTool):
if not hidden:
help = knob_cls.description
else:
- help = optparse.SUPPRESS_HELP
+ help = config.SUPPRESS_HELP
opt_group.add_option(
*opt_strs,
@@ -256,7 +255,7 @@ class ConfigureTool(admintool.AdminTool):
# fake option parser to parse positional arguments
# (because optparse does not support positional argument parsing)
- fake_option_parser = optparse.OptionParser()
+ fake_option_parser = config.IPAOptionParser()
self.add_options(fake_option_parser, True)
fake_option_map = {option.dest: option
diff --git a/ipaserver/install/ipa_acme_manage.py b/ipaserver/install/ipa_acme_manage.py
index dc2359f49..0decab394 100644
--- a/ipaserver/install/ipa_acme_manage.py
+++ b/ipaserver/install/ipa_acme_manage.py
@@ -7,14 +7,12 @@ import enum
import pki.util
import logging
-from optparse import OptionGroup # pylint: disable=deprecated-module
-
from ipalib import api, errors, x509
from ipalib import _
from ipalib.facts import is_ipa_configured
from ipaplatform.paths import paths
from ipapython.admintool import AdminTool
-from ipapython import cookie, dogtag
+from ipapython import cookie, dogtag, config
from ipapython.ipautil import run
from ipapython.certdb import NSSDatabase, EXTERNAL_CA_TRUST_FLAGS
from ipaserver.install import cainstance
@@ -143,7 +141,7 @@ class IPAACMEManage(AdminTool):
@classmethod
def add_options(cls, parser):
- group = OptionGroup(parser, 'Pruning')
+ group = config.OptionGroup(parser, 'Pruning')
group.add_option(
"--enable", dest="enable", action="store_true",
default=False, help="Enable certificate pruning")
diff --git a/ipaserver/install/ipa_backup.py b/ipaserver/install/ipa_backup.py
index 982e5dfc4..b6af63813 100644
--- a/ipaserver/install/ipa_backup.py
+++ b/ipaserver/install/ipa_backup.py
@@ -20,7 +20,6 @@
from __future__ import absolute_import, print_function
import logging
-import optparse # pylint: disable=deprecated-module
import os
import shutil
import sys
@@ -32,7 +31,7 @@ import six
from ipaplatform.paths import paths
from ipaplatform import services
from ipalib import api, errors
-from ipapython import version
+from ipapython import version, config
from ipapython.ipautil import run, write_tmp_file
from ipapython import admintool, certdb
from ipapython.dn import DN
@@ -245,7 +244,7 @@ class Backup(admintool.AdminTool):
parser.add_option(
"--gpg-keyring", dest="gpg_keyring",
- help=optparse.SUPPRESS_HELP)
+ help=config.SUPPRESS_HELP)
parser.add_option(
"--gpg", dest="gpg", action="store_true",
default=False, help="Encrypt the backup")
diff --git a/ipaserver/install/ipa_cacert_manage.py b/ipaserver/install/ipa_cacert_manage.py
index f6ab736fa..048245237 100644
--- a/ipaserver/install/ipa_cacert_manage.py
+++ b/ipaserver/install/ipa_cacert_manage.py
@@ -22,14 +22,13 @@ from __future__ import print_function, absolute_import
import datetime
import logging
import os
-from optparse import OptionGroup # pylint: disable=deprecated-module
import gssapi
from ipalib.constants import (
RENEWAL_CA_NAME, RENEWAL_REUSE_CA_NAME, RENEWAL_SELFSIGNED_CA_NAME,
IPA_CA_CN)
from ipalib.install import certmonger, certstore
-from ipapython import admintool, ipautil
+from ipapython import admintool, ipautil, config
from ipapython.certdb import (EMPTY_TRUST_FLAGS,
EXTERNAL_CA_TRUST_FLAGS,
TrustFlags,
@@ -61,7 +60,7 @@ class CACertManage(admintool.AdminTool):
"-p", "--password", dest='password',
help="Directory Manager password")
- renew_group = OptionGroup(parser, "Renew options")
+ renew_group = config.OptionGroup(parser, "Renew options")
renew_group.add_option(
"--self-signed", dest='self_signed',
action='store_true',
@@ -89,7 +88,7 @@ class CACertManage(admintool.AdminTool):
"certificate chain")
parser.add_option_group(renew_group)
- install_group = OptionGroup(parser, "Install options")
+ install_group = config.OptionGroup(parser, "Install options")
install_group.add_option(
"-n", "--nickname", dest='nickname',
help="Nickname for the certificate")
@@ -98,7 +97,7 @@ class CACertManage(admintool.AdminTool):
help="Trust flags for the certificate in certutil format")
parser.add_option_group(install_group)
- delete_group = OptionGroup(parser, "Delete options")
+ delete_group = config.OptionGroup(parser, "Delete options")
delete_group.add_option(
"-f", "--force", action='store_true',
help="Force removing the CA even if chain validation fails")
diff --git a/ipaserver/install/ipa_kra_install.py b/ipaserver/install/ipa_kra_install.py
index 3e4cd67fa..8a09179f7 100644
--- a/ipaserver/install/ipa_kra_install.py
+++ b/ipaserver/install/ipa_kra_install.py
@@ -22,13 +22,12 @@ from __future__ import print_function, absolute_import
import logging
import sys
import tempfile
-from optparse import SUPPRESS_HELP # pylint: disable=deprecated-module
from textwrap import dedent
from ipalib import api
from ipalib.constants import DOMAIN_LEVEL_1
from ipaplatform.paths import paths
-from ipapython import admintool
+from ipapython import admintool, config
from ipaserver.install import service
from ipaserver.install import cainstance
from ipaserver.install import custodiainstance
@@ -73,7 +72,7 @@ class KRAInstall(admintool.AdminTool):
parser.add_option(
"--uninstall",
dest="uninstall", action="store_true", default=False,
- help=SUPPRESS_HELP)
+ help=config.SUPPRESS_HELP)
parser.add_option(
"--pki-config-override", dest="pki_config_override",
diff --git a/ipaserver/install/ipa_migrate.py b/ipaserver/install/ipa_migrate.py
index e21937401..1d1342788 100644
--- a/ipaserver/install/ipa_migrate.py
+++ b/ipaserver/install/ipa_migrate.py
@@ -28,6 +28,7 @@ from ipaplatform.paths import paths
from ipapython.dn import DN
from ipapython.ipaldap import LDAPClient, LDAPEntry, realm_to_ldapi_uri
from ipapython.ipa_log_manager import standard_logging_setup
+from ipapython.admintool import admin_cleanup_global_argv
from ipaserver.install.ipa_migrate_constants import (
DS_CONFIG, DB_OBJECTS, DS_INDEXES, BIND_DN, LOG_FILE_NAME,
STRIP_OP_ATTRS, STRIP_ATTRS, STRIP_OC, PROD_ATTRS,
@@ -284,6 +285,18 @@ class LDIFParser(ldif.LDIFParser):
self.mc.process_db_entry(entry_dn=dn, entry_attrs=entry_attrs)
+class SensitiveStoreAction(argparse._StoreAction):
+ def __init__(self, *, sensitive, **options):
+ super(SensitiveStoreAction, self).__init__(**options)
+ self.sensitive = sensitive
+
+ def _get_kwargs(self):
+ names = super(SensitiveStoreAction, self)._get_kwargs()
+ sensitive_name = 'sensitive'
+ names.extend((sensitive_name, getattr(self, sensitive_name)))
+ return names
+
+
#
# Migrate IPA to IPA Class
#
@@ -344,7 +357,8 @@ class IPAMigrate():
help='Password for the Bind DN. If a password '
'is not provided then the user will be '
'prompted to enter it',
- default=None)
+ default=None, sensitive=True,
+ action=SensitiveStoreAction)
parser.add_argument('-j', '--bind-pw-file',
help='A text file containing the clear text '
'password for the Bind DN', default=None)
@@ -2023,6 +2037,7 @@ class IPAMigrate():
parser = argparse.ArgumentParser(description=desc, allow_abbrev=True)
self.add_options(parser)
self.validate_options()
+ admin_cleanup_global_argv(parser, self.args, sys.argv)
# Check for dryrun mode
if self.args.dryrun or self.args.dryrun_record is not None:
diff --git a/ipaserver/install/ipa_restore.py b/ipaserver/install/ipa_restore.py
index 57ad8dd05..539501ab4 100644
--- a/ipaserver/install/ipa_restore.py
+++ b/ipaserver/install/ipa_restore.py
@@ -20,7 +20,6 @@
from __future__ import absolute_import, print_function
import logging
-import optparse # pylint: disable=deprecated-module
import os
import shutil
import sys
@@ -34,7 +33,7 @@ import six
from ipaclient.install.client import update_ipa_nssdb
from ipalib import api, errors
from ipalib.constants import FQDN
-from ipapython import version, ipautil
+from ipapython import version, ipautil, config
from ipapython.ipautil import run, user_input
from ipapython import admintool, certdb
from ipapython.dn import DN
@@ -186,11 +185,11 @@ class Restore(admintool.AdminTool):
super(Restore, cls).add_options(parser, debug_option=True)
parser.add_option(
- "-p", "--password", dest="password",
+ "-p", "--password", dest="password", sensitive=True,
help="Directory Manager password")
parser.add_option(
"--gpg-keyring", dest="gpg_keyring",
- help=optparse.SUPPRESS_HELP)
+ help=config.SUPPRESS_HELP)
parser.add_option(
"--data", dest="data_only", action="store_true",
default=False, help="Restore only the data")
diff --git a/ipaserver/install/ipa_server_certinstall.py b/ipaserver/install/ipa_server_certinstall.py
index e29f00ec3..76ad37ca7 100644
--- a/ipaserver/install/ipa_server_certinstall.py
+++ b/ipaserver/install/ipa_server_certinstall.py
@@ -22,12 +22,11 @@ from __future__ import print_function, absolute_import
import os
import os.path
import tempfile
-import optparse # pylint: disable=deprecated-module
from ipalib import x509
from ipalib.install import certmonger
from ipaplatform.paths import paths
-from ipapython import admintool, dogtag
+from ipapython import admintool, dogtag, config
from ipapython.certdb import NSSDatabase, get_ca_nickname
from ipapython.dn import DN
from ipapython import ipaldap
@@ -65,15 +64,15 @@ class ServerCertInstall(admintool.AdminTool):
help="The password of the PKCS#12 file")
parser.add_option(
"--dirsrv_pin", "--http_pin",
- dest="pin",
- help=optparse.SUPPRESS_HELP)
+ dest="pin", sensitive=True,
+ help=config.SUPPRESS_HELP)
parser.add_option(
"--cert-name",
dest="cert_name", metavar="NAME",
help="Name of the certificate to install")
parser.add_option(
"-p", "--dirman-password",
- dest="dirman_password",
+ dest="dirman_password", sensitive=True,
help="Directory Manager password")
def validate_options(self):
diff --git a/ipaserver/install/plugins/add_admin_krbcanonicalname.py b/ipaserver/install/plugins/add_admin_krbcanonicalname.py
new file mode 100644
index 000000000..e9ffdf55a
--- /dev/null
+++ b/ipaserver/install/plugins/add_admin_krbcanonicalname.py
@@ -0,0 +1,79 @@
+#
+# Copyright (C) 2025 FreeIPA Contributors see COPYING for license
+#
+
+from __future__ import absolute_import
+
+import logging
+
+from ipalib import errors
+from ipalib import Registry
+from ipalib import Updater
+from ipapython.dn import DN
+
+logger = logging.getLogger(__name__)
+
+register = Registry()
+
+
+@register()
+class add_admin_krbcanonicalname(Updater):
+ """
+ Ensures that only the admin user has the krbCanonicalName of
+ admin@$REALM.
+ """
+
+ def execute(self, **options):
+ ldap = self.api.Backend.ldap2
+
+ search_filter = (
+ "(krbcanonicalname=admin@{})".format(self.api.env.realm))
+ try:
+ (entries, _truncated) = ldap.find_entries(
+ filter=search_filter, base_dn=self.api.env.basedn,
+ time_limit=0, size_limit=0)
+ except errors.EmptyResult:
+ logger.debug("add_admin_krbcanonicalname: No user set with "
+ "admin krbcanonicalname")
+ entries = []
+ # fall through
+ except errors.ExecutionError as e:
+ logger.error("add_admin_krbcanonicalname: Can not get list "
+ "of krbcanonicalname: %s", e)
+ return False, []
+
+ admin_set = False
+ # admin should be only user with admin@ as krbcanonicalname
+ # It has a uniquness setting so there can be only one, we
+ # just didn't automatically set it for admin.
+ for entry in entries:
+ if entry.single_value.get('uid') != 'admin':
+ logger.critical(
+ "add_admin_krbcanonicalname: "
+ "entry %s has a krbcanonicalname of admin. Removing.",
+ entry.dn)
+ del entry['krbcanonicalname']
+ ldap.update_entry(entry)
+ else:
+ admin_set = True
+
+ if not admin_set:
+ dn = DN(
+ ('uid', 'admin'),
+ self.api.env.container_user,
+ self.api.env.basedn)
+ entry = ldap.get_entry(dn)
+ entry['krbcanonicalname'] = 'admin@%s' % self.api.env.realm
+ try:
+ ldap.update_entry(entry)
+ except errors.DuplicateEntry:
+ logger.critical(
+ "add_admin_krbcanonicalname: "
+ "Failed to set krbcanonicalname on admin. It is set "
+ "on another entry.")
+ except errors.ExecutionError as e:
+ logger.critical(
+ "add_admin_krbcanonicalname: "
+ "Failed to set krbcanonicalname on admin: %s", e)
+
+ return False, []
diff --git a/ipatests/i18n.py b/ipatests/i18n.py
index 49f5c4c32..57915c286 100644
--- a/ipatests/i18n.py
+++ b/ipatests/i18n.py
@@ -22,7 +22,6 @@ from __future__ import print_function
# WARNING: Do not import ipa modules, this is also used as a
# stand-alone script (invoked from po Makefile).
-import optparse # pylint: disable=deprecated-module
import sys
import gettext
import re
@@ -30,6 +29,7 @@ import os
import traceback
import polib
from collections import namedtuple
+from ipapython import config
import six
@@ -722,9 +722,9 @@ usage ='''
def main():
global verbose, print_traceback, pedantic, show_strings
- parser = optparse.OptionParser(usage=usage)
+ parser = config.IPAOptionParser(usage=usage)
- mode_group = optparse.OptionGroup(parser, 'Operational Mode',
+ mode_group = config.OptionGroup(parser, 'Operational Mode',
'You must select one these modes to run in')
mode_group.add_option('-g', '--test-gettext', action='store_const', const='test_gettext', dest='mode',
@@ -748,7 +748,7 @@ def main():
parser.add_option('--traceback', action='store_true', dest='print_traceback', default=False,
help='print the traceback when an exception occurs')
- param_group = optparse.OptionGroup(parser, 'Run Time Parameters',
+ param_group = config.OptionGroup(parser, 'Run Time Parameters',
'These may be used to modify the run time defaults')
param_group.add_option('--test-lang', action='store', dest='test_lang', default='test',
diff --git a/ipatests/test_integration/test_commands.py b/ipatests/test_integration/test_commands.py
index fd34defe5..421e69043 100644
--- a/ipatests/test_integration/test_commands.py
+++ b/ipatests/test_integration/test_commands.py
@@ -1883,6 +1883,44 @@ class TestIPACommandWithoutReplica(IntegrationTest):
assert old_err_msg not in dirsrv_error_log
assert re.search(new_err_msg, dirsrv_error_log)
+ def test_unique_krbcanonicalname(self):
+ """Verify that the uniqueness for krbcanonicalname is working"""
+ master = self.master
+
+ base_dn = str(master.domain.basedn)
+ hostname = master.hostname
+ realm = master.domain.realm
+ principal = f'test/{hostname}@{realm}'
+ entry_ldif = textwrap.dedent("""
+ dn: krbprincipalname={principal},cn=services,cn=accounts,{base_dn}
+ changetype: add
+ ipakrbprincipalalias: test/{hostname}@{realm}
+ krbprincipalname: {principal}
+ objectclass: ipakrbprincipal
+ objectclass: ipaobject
+ objectclass: ipaservice
+ objectclass: krbprincipal
+ objectclass: krbprincipalaux
+ objectclass: top
+ krbcanonicalname: admin@{realm}
+ managedby: fqdn={hostname},cn=computers,cn=accounts,{base_dn}
+ """).format(
+ base_dn=base_dn,
+ hostname=hostname,
+ principal=principal,
+ realm=realm)
+ tasks.kdestroy_all(master)
+ master.run_command(
+ ['kinit', '-kt', '/etc/krb5.keytab', f'host/{hostname}@{realm}'])
+ args = [
+ 'ldapmodify',
+ '-Y',
+ 'GSSAPI'
+ ]
+ result = master.run_command(args, stdin_text=entry_ldif,
+ raiseonerr=False)
+ assert "entry with the same attribute value" in result.stderr_text
+
class TestIPAautomount(IntegrationTest):
@classmethod
diff --git a/makeapi.in b/makeapi.in
index a801b9253..8fc87d23d 100644
--- a/makeapi.in
+++ b/makeapi.in
@@ -38,6 +38,7 @@ from ipalib.parameters import Param
from ipalib.output import Output
from ipalib.text import Gettext, NGettext, ConcatenatedLazyText
from ipalib.capabilities import capabilities
+from ipapython import config
API_FILE='API.txt'
@@ -84,9 +85,7 @@ OUTPUT_IGNORED_ATTRIBUTES = (
)
def parse_options():
- from optparse import OptionParser # pylint: disable=deprecated-module
-
- parser = OptionParser()
+ parser = config.IPAOptionParser()
parser.add_option("--validate", dest="validate", action="store_true",
default=False, help="Validate the API vs the stored API")
--- End Message ---