|
|
541bac |
From 41379f7ad6a9442dd55cc43d832427911e86db31 Mon Sep 17 00:00:00 2001
|
|
|
541bac |
From: Sumit Bose <sbose@redhat.com>
|
|
|
541bac |
Date: Fri, 23 Oct 2020 16:53:43 +0200
|
|
|
541bac |
Subject: [PATCH 2/7] computer: add create-msa sub-command
|
|
|
541bac |
|
|
|
541bac |
Add new sub-command to create a managed service account in AD. This can
|
|
|
541bac |
be used if LDAP access to AD is needed but the host is already joined to
|
|
|
541bac |
a different domain.
|
|
|
541bac |
|
|
|
541bac |
Resolves: https://bugzilla.redhat.com/show_bug.cgi?id=1854112
|
|
|
541bac |
---
|
|
|
541bac |
doc/adcli.xml | 140 ++++++++++++++++++++++++++++++++++++++
|
|
|
541bac |
library/adenroll.c | 164 ++++++++++++++++++++++++++++++++++++++-------
|
|
|
541bac |
tools/computer.c | 125 ++++++++++++++++++++++++++++++++++
|
|
|
541bac |
tools/tools.c | 1 +
|
|
|
541bac |
tools/tools.h | 4 ++
|
|
|
541bac |
5 files changed, 409 insertions(+), 25 deletions(-)
|
|
|
541bac |
|
|
|
541bac |
diff --git a/doc/adcli.xml b/doc/adcli.xml
|
|
|
541bac |
index cc44fd8..14921f9 100644
|
|
|
541bac |
--- a/doc/adcli.xml
|
|
|
541bac |
+++ b/doc/adcli.xml
|
|
|
541bac |
@@ -98,6 +98,10 @@
|
|
|
541bac |
<arg choice="opt">--domain=domain.example.com</arg>
|
|
|
541bac |
<arg choice="plain">computer</arg>
|
|
|
541bac |
</cmdsynopsis>
|
|
|
541bac |
+ <cmdsynopsis>
|
|
|
541bac |
+ <command>adcli create-msa</command>
|
|
|
541bac |
+ <arg choice="opt">--domain=domain.example.com</arg>
|
|
|
541bac |
+ </cmdsynopsis>
|
|
|
541bac |
</refsynopsisdiv>
|
|
|
541bac |
|
|
|
541bac |
<refsect1 id='general_overview'>
|
|
|
541bac |
@@ -885,6 +889,142 @@ Password for Administrator:
|
|
|
541bac |
|
|
|
541bac |
</refsect1>
|
|
|
541bac |
|
|
|
541bac |
+<refsect1 id='managed_service_account'>
|
|
|
541bac |
+ <title>Create a managed service account</title>
|
|
|
541bac |
+
|
|
|
541bac |
+ <para><command>adcli create-msa</command> creates a managed service
|
|
|
541bac |
+ account (MSA) in the given Active Directory domain. This is useful if a
|
|
|
541bac |
+ computer should not fully join the Active Directory domain but LDAP
|
|
|
541bac |
+ access is needed. A typical use case is that the computer is already
|
|
|
541bac |
+ joined an Active Directory domain and needs access to another Active
|
|
|
541bac |
+ Directory domain in the same or a trusted forest where the host
|
|
|
541bac |
+ credentials from the joined Active Directory domain are
|
|
|
541bac |
+ not valid, e.g. there is only a one-way trust.</para>
|
|
|
541bac |
+
|
|
|
541bac |
+<programlisting>
|
|
|
541bac |
+$ adcli create-msa --domain=domain.example.com
|
|
|
541bac |
+Password for Administrator:
|
|
|
541bac |
+</programlisting>
|
|
|
541bac |
+
|
|
|
541bac |
+ <para>The managed service account, as maintained by adcli, cannot have
|
|
|
541bac |
+ additional service principals names (SPNs) associated with it. An SPN
|
|
|
541bac |
+ is defined within the context of a Kerberos service which is tied to a
|
|
|
541bac |
+ machine account in Active Directory. Since a machine can be joined to a
|
|
|
541bac |
+ single Active Directory domain, managed service account in a different
|
|
|
541bac |
+ Active Directory domain will not have the SPNs that otherwise are part
|
|
|
541bac |
+ of another Active Directory domain's machine.</para>
|
|
|
541bac |
+
|
|
|
541bac |
+ <para>Since it is expected that a client will most probably join to the
|
|
|
541bac |
+ Active Directory domain matching its DNS domain the managed service
|
|
|
541bac |
+ account will be needed for a different Active directory domain and as a
|
|
|
541bac |
+ result the Active Directory domain name is a mandatory option. If
|
|
|
541bac |
+ called with no other options <command>adcli create-msa</command>
|
|
|
541bac |
+ will use the short hostname with an additional random suffix as
|
|
|
541bac |
+ computer name to avoid name collisions.</para>
|
|
|
541bac |
+
|
|
|
541bac |
+ <para>LDAP attribute sAMAccountName has a limit of 20 characters.
|
|
|
541bac |
+ However, machine account's NetBIOS name must be at most 16 characters
|
|
|
541bac |
+ long, including a trailing '$' sign. Since it is not expected that the
|
|
|
541bac |
+ managed service accounts created by adcli will be used on the NetBIOS
|
|
|
541bac |
+ level the remaining 4 characters can be used to add uniqueness. Managed
|
|
|
541bac |
+ service account names will have a suffix of 3 random characters from
|
|
|
541bac |
+ number and upper- and lowercase ASCII ranges appended to the chosen
|
|
|
541bac |
+ short host name, using '!' as a separator. For a host with the
|
|
|
541bac |
+ shortname 'myhost', a managed service account will have a common name
|
|
|
541bac |
+ (CN attribute) 'myhost!A2c' and a NetBIOS name
|
|
|
541bac |
+ (sAMAccountName attribute) will be 'myhost!A2c$'. A corresponding
|
|
|
541bac |
+ Kerberos principal in the Active Directory domain where the managed
|
|
|
541bac |
+ service account was created would be
|
|
|
541bac |
+ 'myhost!A2c$@DOMAIN.EXAMPLE.COM'.</para>
|
|
|
541bac |
+
|
|
|
541bac |
+ <para>A keytab for the managed service account is stored into a file
|
|
|
541bac |
+ specified with -K option. If it is not specified, the file is named
|
|
|
541bac |
+ after the default keytab file, with lowercase Active Directory domain
|
|
|
541bac |
+ of the managed service account as a suffix. On most systems it would be
|
|
|
541bac |
+ <filename>/etc/krb5.keytab</filename> with a suffix of
|
|
|
541bac |
+ 'domain.example.com', e.g.
|
|
|
541bac |
+ <filename>/etc/krb5.keytad.domain.example.com</filename>.</para>
|
|
|
541bac |
+
|
|
|
541bac |
+ <para><command>adcli create-msa</command> can be called multiple
|
|
|
541bac |
+ times to reset the password of the managed service account. To identify
|
|
|
541bac |
+ the right account with the random component in the name the
|
|
|
541bac |
+ corresponding principal is read from the keytab. If the keytab got
|
|
|
541bac |
+ deleted <command>adcli</command> will try to identify an existing
|
|
|
541bac |
+ managed service account with the help of the fully-qualified name, if
|
|
|
541bac |
+ this fails a new managed service account will be created.</para>
|
|
|
541bac |
+
|
|
|
541bac |
+ <para>The managed service account password can be updated with
|
|
|
541bac |
+<programlisting>
|
|
|
541bac |
+$ adcli update --domain=domain.example.com --host-keytab=/etc/krb5.keytad.domain.example.com
|
|
|
541bac |
+</programlisting>
|
|
|
541bac |
+ and the managed service account can be deleted with
|
|
|
541bac |
+<programlisting>
|
|
|
541bac |
+$ adcli delete-computer --domain=domain.example.com 'myhost!A2c'
|
|
|
541bac |
+</programlisting>
|
|
|
541bac |
+ </para>
|
|
|
541bac |
+
|
|
|
541bac |
+ <para>In addition to the global options, you can specify the following
|
|
|
541bac |
+ options to control how this operation is done.</para>
|
|
|
541bac |
+
|
|
|
541bac |
+ <variablelist>
|
|
|
541bac |
+ <varlistentry>
|
|
|
541bac |
+ <term><option>-N, --computer-name=<parameter>computer</parameter></option></term>
|
|
|
541bac |
+ <listitem><para>The short non-dotted name of the managed
|
|
|
541bac |
+ service account that will be created in the Active
|
|
|
541bac |
+ Directory domain. The long option name
|
|
|
541bac |
+ <option>--computer-name</option> is
|
|
|
541bac |
+ kept to underline the similarity with the same option
|
|
|
541bac |
+ of the other sub-commands. If not specified,
|
|
|
541bac |
+ then the first portion of the <option>--host-fqdn</option>
|
|
|
541bac |
+ or its default is used with a random suffix.</para></listitem>
|
|
|
541bac |
+ </varlistentry>
|
|
|
541bac |
+ <varlistentry>
|
|
|
541bac |
+ <term><option>-O, --domain-ou=<parameter>OU=xxx</parameter></option></term>
|
|
|
541bac |
+ <listitem><para>The full distinguished name of the OU in
|
|
|
541bac |
+ which to create the managed service account. If not
|
|
|
541bac |
+ specified, then the managed service account will be
|
|
|
541bac |
+ created in a default location.</para></listitem>
|
|
|
541bac |
+ </varlistentry>
|
|
|
541bac |
+ <varlistentry>
|
|
|
541bac |
+ <term><option>-H, --host-fqdn=<parameter>host</parameter></option></term>
|
|
|
541bac |
+ <listitem><para>Override the local machine's fully
|
|
|
541bac |
+ qualified DNS domain name. If not specified, the local
|
|
|
541bac |
+ machine's hostname will be retrieved via
|
|
|
541bac |
+ <function>gethostname()</function>.
|
|
|
541bac |
+ If <function>gethostname()</function> only returns a short name
|
|
|
541bac |
+ <function>getaddrinfo()</function> with the AI_CANONNAME hint
|
|
|
541bac |
+ is called to expand the name to a fully qualified DNS
|
|
|
541bac |
+ domain name.</para></listitem>
|
|
|
541bac |
+ </varlistentry>
|
|
|
541bac |
+ <varlistentry>
|
|
|
541bac |
+ <term><option>-K, --host-keytab=<parameter>/path/to/keytab</parameter></option></term>
|
|
|
541bac |
+ <listitem><para>Specify the path to the host keytab where
|
|
|
541bac |
+ credentials of the managed service account will be
|
|
|
541bac |
+ written after a successful creation. If not specified,
|
|
|
541bac |
+ the default location will be used, usually
|
|
|
541bac |
+ <filename>/etc/krb5.keytab</filename> with
|
|
|
541bac |
+ the lower-cased Active Directory domain name added as a
|
|
|
541bac |
+ suffix e.g.
|
|
|
541bac |
+ <filename>/etc/krb5.keytab.domain.example.com</filename>.
|
|
|
541bac |
+ </para></listitem>
|
|
|
541bac |
+ </varlistentry>
|
|
|
541bac |
+ <varlistentry>
|
|
|
541bac |
+ <term><option>--show-details</option></term>
|
|
|
541bac |
+ <listitem><para>After a successful creation print out
|
|
|
541bac |
+ information about the created object. This is output in
|
|
|
541bac |
+ a format that should be both human and machine
|
|
|
541bac |
+ readable.</para></listitem>
|
|
|
541bac |
+ </varlistentry>
|
|
|
541bac |
+ <varlistentry>
|
|
|
541bac |
+ <term><option>--show-password</option></term>
|
|
|
541bac |
+ <listitem><para>After a successful creation print out
|
|
|
541bac |
+ the managed service account password. This is output in
|
|
|
541bac |
+ a format that should be both human and machine
|
|
|
541bac |
+ readable.</para></listitem>
|
|
|
541bac |
+ </varlistentry>
|
|
|
541bac |
+ </variablelist>
|
|
|
541bac |
+</refsect1>
|
|
|
541bac |
+
|
|
|
541bac |
<refsect1 id='delegation'>
|
|
|
541bac |
<title>Delegated Permissions</title>
|
|
|
541bac |
<para>It is common practice in AD to not use an account from the Domain
|
|
|
541bac |
diff --git a/library/adenroll.c b/library/adenroll.c
|
|
|
541bac |
index 5ae1f7b..dbfda36 100644
|
|
|
541bac |
--- a/library/adenroll.c
|
|
|
541bac |
+++ b/library/adenroll.c
|
|
|
541bac |
@@ -155,6 +155,20 @@ struct _adcli_enroll {
|
|
|
541bac |
char *description;
|
|
|
541bac |
};
|
|
|
541bac |
|
|
|
541bac |
+static void
|
|
|
541bac |
+check_if_service (adcli_enroll *enroll,
|
|
|
541bac |
+ LDAP *ldap,
|
|
|
541bac |
+ LDAPMessage *results)
|
|
|
541bac |
+{
|
|
|
541bac |
+ char **objectclasses = NULL;
|
|
|
541bac |
+
|
|
|
541bac |
+ objectclasses = _adcli_ldap_parse_values (ldap, results, "objectClass");
|
|
|
541bac |
+ enroll->is_service = _adcli_strv_has_ex (objectclasses,
|
|
|
541bac |
+ "msDS-ManagedServiceAccount",
|
|
|
541bac |
+ strcasecmp) == 1 ? true : false;
|
|
|
541bac |
+ _adcli_strv_free (objectclasses);
|
|
|
541bac |
+}
|
|
|
541bac |
+
|
|
|
541bac |
static adcli_result
|
|
|
541bac |
ensure_host_fqdn (adcli_result res,
|
|
|
541bac |
adcli_enroll *enroll)
|
|
|
541bac |
@@ -471,13 +485,15 @@ ensure_keytab_principals (adcli_result res,
|
|
|
541bac |
{
|
|
|
541bac |
krb5_context k5;
|
|
|
541bac |
krb5_error_code code;
|
|
|
541bac |
- int count;
|
|
|
541bac |
+ int count = 0;
|
|
|
541bac |
int at, i;
|
|
|
541bac |
|
|
|
541bac |
/* Prepare the principals we're going to add to the keytab */
|
|
|
541bac |
|
|
|
541bac |
- return_unexpected_if_fail (enroll->service_principals);
|
|
|
541bac |
- count = _adcli_strv_len (enroll->service_principals);
|
|
|
541bac |
+ if (!enroll->is_service) {
|
|
|
541bac |
+ return_unexpected_if_fail (enroll->service_principals);
|
|
|
541bac |
+ count = _adcli_strv_len (enroll->service_principals);
|
|
|
541bac |
+ }
|
|
|
541bac |
|
|
|
541bac |
k5 = adcli_conn_get_krb5_context (enroll->conn);
|
|
|
541bac |
return_unexpected_if_fail (k5 != NULL);
|
|
|
541bac |
@@ -556,8 +572,12 @@ static adcli_result
|
|
|
541bac |
lookup_computer_container (adcli_enroll *enroll,
|
|
|
541bac |
LDAP *ldap)
|
|
|
541bac |
{
|
|
|
541bac |
- char *attrs[] = { "wellKnownObjects", NULL };
|
|
|
541bac |
- char *prefix = "B:32:AA312825768811D1ADED00C04FD8D5CD:";
|
|
|
541bac |
+ char *attrs[] = { enroll->is_service ? "otherWellKnownObjects"
|
|
|
541bac |
+ : "wellKnownObjects", NULL };
|
|
|
541bac |
+ const char *prefix = enroll->is_service ? "B:32:1EB93889E40C45DF9F0C64D23BBB6237:"
|
|
|
541bac |
+ : "B:32:AA312825768811D1ADED00C04FD8D5CD:";
|
|
|
541bac |
+ const char *filter = enroll->is_service ? "(&(objectClass=container)(cn=Managed Service Accounts))"
|
|
|
541bac |
+ : "(&(objectClass=container)(cn=Computers))";
|
|
|
541bac |
int prefix_len;
|
|
|
541bac |
LDAPMessage *results;
|
|
|
541bac |
const char *base;
|
|
|
541bac |
@@ -586,7 +606,7 @@ lookup_computer_container (adcli_enroll *enroll,
|
|
|
541bac |
"Couldn't lookup computer container: %s", base);
|
|
|
541bac |
}
|
|
|
541bac |
|
|
|
541bac |
- values = _adcli_ldap_parse_values (ldap, results, "wellKnownObjects");
|
|
|
541bac |
+ values = _adcli_ldap_parse_values (ldap, results, attrs[0]);
|
|
|
541bac |
ldap_msgfree (results);
|
|
|
541bac |
|
|
|
541bac |
prefix_len = strlen (prefix);
|
|
|
541bac |
@@ -604,8 +624,7 @@ lookup_computer_container (adcli_enroll *enroll,
|
|
|
541bac |
|
|
|
541bac |
/* Try harder */
|
|
|
541bac |
if (!enroll->computer_container) {
|
|
|
541bac |
- ret = ldap_search_ext_s (ldap, base, LDAP_SCOPE_BASE,
|
|
|
541bac |
- "(&(objectClass=container)(cn=Computers))",
|
|
|
541bac |
+ ret = ldap_search_ext_s (ldap, base, LDAP_SCOPE_BASE, filter,
|
|
|
541bac |
attrs, 0, NULL, NULL, NULL, -1, &results);
|
|
|
541bac |
if (ret == LDAP_SUCCESS) {
|
|
|
541bac |
enroll->computer_container = _adcli_ldap_parse_dn (ldap, results);
|
|
|
541bac |
@@ -747,7 +766,7 @@ static adcli_result
|
|
|
541bac |
create_computer_account (adcli_enroll *enroll,
|
|
|
541bac |
LDAP *ldap)
|
|
|
541bac |
{
|
|
|
541bac |
- char *vals_objectClass[] = { "computer", NULL };
|
|
|
541bac |
+ char *vals_objectClass[] = { enroll->is_service ? "msDS-ManagedServiceAccount" : "computer", NULL };
|
|
|
541bac |
LDAPMod objectClass = { LDAP_MOD_ADD, "objectClass", { vals_objectClass, } };
|
|
|
541bac |
char *vals_sAMAccountName[] = { enroll->computer_sam, NULL };
|
|
|
541bac |
LDAPMod sAMAccountName = { LDAP_MOD_ADD, "sAMAccountName", { vals_sAMAccountName, } };
|
|
|
541bac |
@@ -806,7 +825,7 @@ create_computer_account (adcli_enroll *enroll,
|
|
|
541bac |
m = 0;
|
|
|
541bac |
for (c = 0; c < mods_count - 1; c++) {
|
|
|
541bac |
/* Skip empty LDAP sttributes */
|
|
|
541bac |
- if (all_mods[c]->mod_vals.modv_strvals[0] != NULL) {
|
|
|
541bac |
+ if (all_mods[c]->mod_vals.modv_strvals != NULL && all_mods[c]->mod_vals.modv_strvals[0] != NULL) {
|
|
|
541bac |
mods[m++] = all_mods[c];
|
|
|
541bac |
}
|
|
|
541bac |
}
|
|
|
541bac |
@@ -936,7 +955,7 @@ locate_computer_account (adcli_enroll *enroll,
|
|
|
541bac |
LDAPMessage **rresults,
|
|
|
541bac |
LDAPMessage **rentry)
|
|
|
541bac |
{
|
|
|
541bac |
- char *attrs[] = { "1.1", NULL };
|
|
|
541bac |
+ char *attrs[] = { "objectClass", NULL };
|
|
|
541bac |
LDAPMessage *results = NULL;
|
|
|
541bac |
LDAPMessage *entry = NULL;
|
|
|
541bac |
const char *base;
|
|
|
541bac |
@@ -948,7 +967,9 @@ locate_computer_account (adcli_enroll *enroll,
|
|
|
541bac |
/* If we don't yet know our computer dn, then try and find it */
|
|
|
541bac |
value = _adcli_ldap_escape_filter (enroll->computer_sam);
|
|
|
541bac |
return_unexpected_if_fail (value != NULL);
|
|
|
541bac |
- if (asprintf (&filter, "(&(objectClass=computer)(sAMAccountName=%s))", value) < 0)
|
|
|
541bac |
+ if (asprintf (&filter, "(&(objectClass=%s)(sAMAccountName=%s))",
|
|
|
541bac |
+ enroll->is_service ? "msDS-ManagedServiceAccount" : "computer",
|
|
|
541bac |
+ value) < 0)
|
|
|
541bac |
return_unexpected_if_reached ();
|
|
|
541bac |
free (value);
|
|
|
541bac |
|
|
|
541bac |
@@ -962,8 +983,11 @@ locate_computer_account (adcli_enroll *enroll,
|
|
|
541bac |
if (ret == LDAP_SUCCESS) {
|
|
|
541bac |
entry = ldap_first_entry (ldap, results);
|
|
|
541bac |
|
|
|
541bac |
- /* If we found a computer account, make note of dn */
|
|
|
541bac |
+ /* If we found a computer/service account, make note of dn */
|
|
|
541bac |
if (entry) {
|
|
|
541bac |
+ if (!enroll->is_service_explicit) {
|
|
|
541bac |
+ check_if_service ( enroll, ldap, results);
|
|
|
541bac |
+ }
|
|
|
541bac |
dn = ldap_get_dn (ldap, entry);
|
|
|
541bac |
free (enroll->computer_dn);
|
|
|
541bac |
enroll->computer_dn = strdup (dn);
|
|
|
541bac |
@@ -1003,7 +1027,7 @@ load_computer_account (adcli_enroll *enroll,
|
|
|
541bac |
LDAPMessage **rresults,
|
|
|
541bac |
LDAPMessage **rentry)
|
|
|
541bac |
{
|
|
|
541bac |
- char *attrs[] = { "1.1", NULL };
|
|
|
541bac |
+ char *attrs[] = { "objectClass", NULL };
|
|
|
541bac |
LDAPMessage *results = NULL;
|
|
|
541bac |
LDAPMessage *entry = NULL;
|
|
|
541bac |
int ret;
|
|
|
541bac |
@@ -1081,6 +1105,12 @@ locate_or_create_computer_account (adcli_enroll *enroll,
|
|
|
541bac |
if (res == ADCLI_SUCCESS && entry == NULL)
|
|
|
541bac |
res = create_computer_account (enroll, ldap);
|
|
|
541bac |
|
|
|
541bac |
+ /* Service account already exists, just continue and update the
|
|
|
541bac |
+ * password */
|
|
|
541bac |
+ if (enroll->is_service && entry != NULL) {
|
|
|
541bac |
+ res = ADCLI_SUCCESS;
|
|
|
541bac |
+ }
|
|
|
541bac |
+
|
|
|
541bac |
if (results)
|
|
|
541bac |
ldap_msgfree (results);
|
|
|
541bac |
|
|
|
541bac |
@@ -1413,6 +1443,11 @@ update_computer_account (adcli_enroll *enroll)
|
|
|
541bac |
LDAP *ldap;
|
|
|
541bac |
char *value = NULL;
|
|
|
541bac |
|
|
|
541bac |
+ /* No updates for service accounts */
|
|
|
541bac |
+ if (enroll->is_service) {
|
|
|
541bac |
+ return;
|
|
|
541bac |
+ }
|
|
|
541bac |
+
|
|
|
541bac |
ldap = adcli_conn_get_ldap_connection (enroll->conn);
|
|
|
541bac |
return_if_fail (ldap != NULL);
|
|
|
541bac |
|
|
|
541bac |
@@ -1501,6 +1536,11 @@ update_service_principals (adcli_enroll *enroll)
|
|
|
541bac |
LDAP *ldap;
|
|
|
541bac |
int ret;
|
|
|
541bac |
|
|
|
541bac |
+ /* No updates for service accounts */
|
|
|
541bac |
+ if (enroll->is_service) {
|
|
|
541bac |
+ return ADCLI_SUCCESS;
|
|
|
541bac |
+ }
|
|
|
541bac |
+
|
|
|
541bac |
ldap = adcli_conn_get_ldap_connection (enroll->conn);
|
|
|
541bac |
return_unexpected_if_fail (ldap != NULL);
|
|
|
541bac |
|
|
|
541bac |
@@ -1614,6 +1654,8 @@ load_keytab_entry (krb5_context k5,
|
|
|
541bac |
enroll->computer_name = name;
|
|
|
541bac |
name[len - 1] = '\0';
|
|
|
541bac |
_adcli_info ("Found computer name in keytab: %s", name);
|
|
|
541bac |
+ adcli_conn_set_computer_name (enroll->conn,
|
|
|
541bac |
+ enroll->computer_name);
|
|
|
541bac |
name = NULL;
|
|
|
541bac |
|
|
|
541bac |
} else if (!enroll->host_fqdn && _adcli_str_has_prefix (name, "host/") && strchr (name, '.')) {
|
|
|
541bac |
@@ -2002,17 +2044,25 @@ adcli_enroll_prepare (adcli_enroll *enroll,
|
|
|
541bac |
|
|
|
541bac |
adcli_clear_last_error ();
|
|
|
541bac |
|
|
|
541bac |
- /* Basic discovery and figuring out enroll params */
|
|
|
541bac |
- res = ensure_host_fqdn (res, enroll);
|
|
|
541bac |
- res = ensure_computer_name (res, enroll);
|
|
|
541bac |
- res = ensure_computer_sam (res, enroll);
|
|
|
541bac |
- res = ensure_user_principal (res, enroll);
|
|
|
541bac |
- res = ensure_computer_password (res, enroll);
|
|
|
541bac |
- if (!(flags & ADCLI_ENROLL_NO_KEYTAB))
|
|
|
541bac |
+ if (enroll->is_service) {
|
|
|
541bac |
+ /* Ensure basic params for service accounts */
|
|
|
541bac |
+ res = ensure_computer_sam (res, enroll);
|
|
|
541bac |
+ res = ensure_computer_password (res, enroll);
|
|
|
541bac |
res = ensure_host_keytab (res, enroll);
|
|
|
541bac |
- res = ensure_service_names (res, enroll);
|
|
|
541bac |
- res = ensure_service_principals (res, enroll);
|
|
|
541bac |
- res = ensure_keytab_principals (res, enroll);
|
|
|
541bac |
+ res = ensure_keytab_principals (res, enroll);
|
|
|
541bac |
+ } else {
|
|
|
541bac |
+ /* Basic discovery and figuring out enroll params */
|
|
|
541bac |
+ res = ensure_host_fqdn (res, enroll);
|
|
|
541bac |
+ res = ensure_computer_name (res, enroll);
|
|
|
541bac |
+ res = ensure_computer_sam (res, enroll);
|
|
|
541bac |
+ res = ensure_user_principal (res, enroll);
|
|
|
541bac |
+ res = ensure_computer_password (res, enroll);
|
|
|
541bac |
+ if (!(flags & ADCLI_ENROLL_NO_KEYTAB))
|
|
|
541bac |
+ res = ensure_host_keytab (res, enroll);
|
|
|
541bac |
+ res = ensure_service_names (res, enroll);
|
|
|
541bac |
+ res = ensure_service_principals (res, enroll);
|
|
|
541bac |
+ res = ensure_keytab_principals (res, enroll);
|
|
|
541bac |
+ }
|
|
|
541bac |
|
|
|
541bac |
return res;
|
|
|
541bac |
}
|
|
|
541bac |
@@ -2157,6 +2207,58 @@ enroll_join_or_update_tasks (adcli_enroll *enroll,
|
|
|
541bac |
return update_keytab_for_principals (enroll, flags);
|
|
|
541bac |
}
|
|
|
541bac |
|
|
|
541bac |
+static adcli_result
|
|
|
541bac |
+adcli_enroll_add_description_for_service_account (adcli_enroll *enroll)
|
|
|
541bac |
+{
|
|
|
541bac |
+ const char *fqdn;
|
|
|
541bac |
+ char *desc;
|
|
|
541bac |
+
|
|
|
541bac |
+ fqdn = adcli_conn_get_host_fqdn (enroll->conn);
|
|
|
541bac |
+ return_unexpected_if_fail (fqdn != NULL);
|
|
|
541bac |
+ if (asprintf (&desc, "Please do not edit, Service account for %s, "
|
|
|
541bac |
+ "managed by adcli.", fqdn) < 0) {
|
|
|
541bac |
+ return_unexpected_if_reached ();
|
|
|
541bac |
+ }
|
|
|
541bac |
+
|
|
|
541bac |
+ adcli_enroll_set_description (enroll, desc);
|
|
|
541bac |
+ free (desc);
|
|
|
541bac |
+
|
|
|
541bac |
+ return ADCLI_SUCCESS;
|
|
|
541bac |
+}
|
|
|
541bac |
+
|
|
|
541bac |
+static adcli_result
|
|
|
541bac |
+adcli_enroll_add_keytab_for_service_account (adcli_enroll *enroll)
|
|
|
541bac |
+{
|
|
|
541bac |
+ krb5_context k5;
|
|
|
541bac |
+ krb5_error_code code;
|
|
|
541bac |
+ char def_keytab_name[MAX_KEYTAB_NAME_LEN];
|
|
|
541bac |
+ char *lc_dom_name;
|
|
|
541bac |
+ int ret;
|
|
|
541bac |
+
|
|
|
541bac |
+ if (adcli_enroll_get_keytab_name (enroll) == NULL) {
|
|
|
541bac |
+ k5 = adcli_conn_get_krb5_context (enroll->conn);
|
|
|
541bac |
+ return_unexpected_if_fail (k5 != NULL);
|
|
|
541bac |
+
|
|
|
541bac |
+ code = krb5_kt_default_name (k5, def_keytab_name,
|
|
|
541bac |
+ sizeof (def_keytab_name));
|
|
|
541bac |
+ return_unexpected_if_fail (code == 0);
|
|
|
541bac |
+
|
|
|
541bac |
+ lc_dom_name = strdup (adcli_conn_get_domain_name (enroll->conn));
|
|
|
541bac |
+ return_unexpected_if_fail (lc_dom_name != NULL);
|
|
|
541bac |
+ _adcli_str_down (lc_dom_name);
|
|
|
541bac |
+
|
|
|
541bac |
+
|
|
|
541bac |
+ ret = asprintf (&enroll->keytab_name, "%s.%s", def_keytab_name,
|
|
|
541bac |
+ lc_dom_name);
|
|
|
541bac |
+ free (lc_dom_name);
|
|
|
541bac |
+ return_unexpected_if_fail (ret > 0);
|
|
|
541bac |
+ }
|
|
|
541bac |
+
|
|
|
541bac |
+ _adcli_info ("Using service account keytab: %s", enroll->keytab_name);
|
|
|
541bac |
+
|
|
|
541bac |
+ return ADCLI_SUCCESS;
|
|
|
541bac |
+}
|
|
|
541bac |
+
|
|
|
541bac |
adcli_result
|
|
|
541bac |
adcli_enroll_join (adcli_enroll *enroll,
|
|
|
541bac |
adcli_enroll_flags flags)
|
|
|
541bac |
@@ -2172,7 +2274,14 @@ adcli_enroll_join (adcli_enroll *enroll,
|
|
|
541bac |
if (res != ADCLI_SUCCESS)
|
|
|
541bac |
return res;
|
|
|
541bac |
|
|
|
541bac |
- res = ensure_default_service_names (enroll);
|
|
|
541bac |
+ if (enroll->is_service) {
|
|
|
541bac |
+ res = adcli_enroll_add_description_for_service_account (enroll);
|
|
|
541bac |
+ if (res == ADCLI_SUCCESS) {
|
|
|
541bac |
+ res = adcli_enroll_add_keytab_for_service_account (enroll);
|
|
|
541bac |
+ }
|
|
|
541bac |
+ } else {
|
|
|
541bac |
+ res = ensure_default_service_names (enroll);
|
|
|
541bac |
+ }
|
|
|
541bac |
if (res != ADCLI_SUCCESS)
|
|
|
541bac |
return res;
|
|
|
541bac |
|
|
|
541bac |
@@ -2281,6 +2390,11 @@ adcli_enroll_update (adcli_enroll *enroll,
|
|
|
541bac |
}
|
|
|
541bac |
free (value);
|
|
|
541bac |
|
|
|
541bac |
+ /* We only support password changes for service accounts */
|
|
|
541bac |
+ if (enroll->is_service && (flags & ADCLI_ENROLL_PASSWORD_VALID)) {
|
|
|
541bac |
+ return ADCLI_SUCCESS;
|
|
|
541bac |
+ }
|
|
|
541bac |
+
|
|
|
541bac |
return enroll_join_or_update_tasks (enroll, flags);
|
|
|
541bac |
}
|
|
|
541bac |
|
|
|
541bac |
diff --git a/tools/computer.c b/tools/computer.c
|
|
|
541bac |
index 5a97d8b..63fd374 100644
|
|
|
541bac |
--- a/tools/computer.c
|
|
|
541bac |
+++ b/tools/computer.c
|
|
|
541bac |
@@ -1074,3 +1074,128 @@ adcli_tool_computer_show (adcli_conn *conn,
|
|
|
541bac |
adcli_enroll_unref (enroll);
|
|
|
541bac |
return 0;
|
|
|
541bac |
}
|
|
|
541bac |
+
|
|
|
541bac |
+int
|
|
|
541bac |
+adcli_tool_computer_managed_service_account (adcli_conn *conn,
|
|
|
541bac |
+ int argc,
|
|
|
541bac |
+ char *argv[])
|
|
|
541bac |
+{
|
|
|
541bac |
+ adcli_enroll *enroll;
|
|
|
541bac |
+ adcli_result res;
|
|
|
541bac |
+ int show_password = 0;
|
|
|
541bac |
+ int details = 0;
|
|
|
541bac |
+ int opt;
|
|
|
541bac |
+
|
|
|
541bac |
+ struct option options[] = {
|
|
|
541bac |
+ { "domain", required_argument, NULL, opt_domain },
|
|
|
541bac |
+ { "domain-realm", required_argument, NULL, opt_domain_realm },
|
|
|
541bac |
+ { "domain-controller", required_argument, NULL, opt_domain_controller },
|
|
|
541bac |
+ { "use-ldaps", no_argument, 0, opt_use_ldaps },
|
|
|
541bac |
+ { "login-user", required_argument, NULL, opt_login_user },
|
|
|
541bac |
+ { "login-ccache", optional_argument, NULL, opt_login_ccache },
|
|
|
541bac |
+ { "host-fqdn", required_argument, 0, opt_host_fqdn },
|
|
|
541bac |
+ { "computer-name", required_argument, 0, opt_computer_name },
|
|
|
541bac |
+ { "host-keytab", required_argument, 0, opt_host_keytab },
|
|
|
541bac |
+ { "no-password", no_argument, 0, opt_no_password },
|
|
|
541bac |
+ { "stdin-password", no_argument, 0, opt_stdin_password },
|
|
|
541bac |
+ { "prompt-password", no_argument, 0, opt_prompt_password },
|
|
|
541bac |
+ { "domain-ou", required_argument, NULL, opt_domain_ou },
|
|
|
541bac |
+ { "show-details", no_argument, NULL, opt_show_details },
|
|
|
541bac |
+ { "show-password", no_argument, NULL, opt_show_password },
|
|
|
541bac |
+ { "verbose", no_argument, NULL, opt_verbose },
|
|
|
541bac |
+ { "help", no_argument, NULL, 'h' },
|
|
|
541bac |
+ { 0 },
|
|
|
541bac |
+ };
|
|
|
541bac |
+
|
|
|
541bac |
+ static adcli_tool_desc usages[] = {
|
|
|
541bac |
+ { 0, "usage: adcli create-msa --domain=xxxx" },
|
|
|
541bac |
+ { 0 },
|
|
|
541bac |
+ };
|
|
|
541bac |
+
|
|
|
541bac |
+ enroll = adcli_enroll_new (conn);
|
|
|
541bac |
+ if (enroll == NULL) {
|
|
|
541bac |
+ warnx ("unexpected memory problems");
|
|
|
541bac |
+ return -1;
|
|
|
541bac |
+ }
|
|
|
541bac |
+
|
|
|
541bac |
+ while ((opt = adcli_tool_getopt (argc, argv, options)) != -1) {
|
|
|
541bac |
+ switch (opt) {
|
|
|
541bac |
+ case opt_one_time_password:
|
|
|
541bac |
+ adcli_conn_set_allowed_login_types (conn, ADCLI_LOGIN_COMPUTER_ACCOUNT);
|
|
|
541bac |
+ adcli_conn_set_computer_password (conn, optarg);
|
|
|
541bac |
+ break;
|
|
|
541bac |
+ case opt_show_details:
|
|
|
541bac |
+ details = 1;
|
|
|
541bac |
+ break;
|
|
|
541bac |
+ case opt_show_password:
|
|
|
541bac |
+ show_password = 1;
|
|
|
541bac |
+ break;
|
|
|
541bac |
+ case 'h':
|
|
|
541bac |
+ case '?':
|
|
|
541bac |
+ case ':':
|
|
|
541bac |
+ adcli_tool_usage (options, usages);
|
|
|
541bac |
+ adcli_tool_usage (options, common_usages);
|
|
|
541bac |
+ adcli_enroll_unref (enroll);
|
|
|
541bac |
+ return opt == 'h' ? 0 : 2;
|
|
|
541bac |
+ default:
|
|
|
541bac |
+ res = parse_option ((Option)opt, optarg, conn, enroll);
|
|
|
541bac |
+ if (res != ADCLI_SUCCESS) {
|
|
|
541bac |
+ adcli_enroll_unref (enroll);
|
|
|
541bac |
+ return res;
|
|
|
541bac |
+ }
|
|
|
541bac |
+ break;
|
|
|
541bac |
+ }
|
|
|
541bac |
+ }
|
|
|
541bac |
+
|
|
|
541bac |
+ argc -= optind;
|
|
|
541bac |
+ argv += optind;
|
|
|
541bac |
+
|
|
|
541bac |
+ if (argc == 1)
|
|
|
541bac |
+ adcli_conn_set_domain_name (conn, argv[0]);
|
|
|
541bac |
+ else if (argc > 1) {
|
|
|
541bac |
+ warnx ("extra arguments specified");
|
|
|
541bac |
+ adcli_enroll_unref (enroll);
|
|
|
541bac |
+ return 2;
|
|
|
541bac |
+ }
|
|
|
541bac |
+
|
|
|
541bac |
+ if (adcli_conn_get_domain_name (conn) == NULL) {
|
|
|
541bac |
+ warnx ("domain name is required");
|
|
|
541bac |
+ adcli_enroll_unref (enroll);
|
|
|
541bac |
+ return 2;
|
|
|
541bac |
+ }
|
|
|
541bac |
+
|
|
|
541bac |
+ adcli_enroll_set_is_service (enroll, true);
|
|
|
541bac |
+ adcli_conn_set_allowed_login_types (conn, ADCLI_LOGIN_USER_ACCOUNT);
|
|
|
541bac |
+
|
|
|
541bac |
+ res = adcli_enroll_load (enroll);
|
|
|
541bac |
+ if (res != ADCLI_SUCCESS) {
|
|
|
541bac |
+ /* ignored */
|
|
|
541bac |
+ }
|
|
|
541bac |
+
|
|
|
541bac |
+ res = adcli_conn_connect (conn);
|
|
|
541bac |
+ if (res != ADCLI_SUCCESS) {
|
|
|
541bac |
+ warnx ("couldn't connect to %s domain: %s",
|
|
|
541bac |
+ adcli_conn_get_domain_name (conn),
|
|
|
541bac |
+ adcli_get_last_error ());
|
|
|
541bac |
+ adcli_enroll_unref (enroll);
|
|
|
541bac |
+ return -res;
|
|
|
541bac |
+ }
|
|
|
541bac |
+
|
|
|
541bac |
+ res = adcli_enroll_join (enroll, 0);
|
|
|
541bac |
+ if (res != ADCLI_SUCCESS) {
|
|
|
541bac |
+ warnx ("Adding service account for %s failed: %s",
|
|
|
541bac |
+ adcli_conn_get_domain_name (conn),
|
|
|
541bac |
+ adcli_get_last_error ());
|
|
|
541bac |
+ adcli_enroll_unref (enroll);
|
|
|
541bac |
+ return -res;
|
|
|
541bac |
+ }
|
|
|
541bac |
+
|
|
|
541bac |
+ if (details)
|
|
|
541bac |
+ dump_details (conn, enroll, show_password);
|
|
|
541bac |
+ else if (show_password)
|
|
|
541bac |
+ dump_password (conn, enroll);
|
|
|
541bac |
+
|
|
|
541bac |
+ adcli_enroll_unref (enroll);
|
|
|
541bac |
+
|
|
|
541bac |
+ return 0;
|
|
|
541bac |
+}
|
|
|
541bac |
diff --git a/tools/tools.c b/tools/tools.c
|
|
|
541bac |
index 1b6d879..d0dcf98 100644
|
|
|
541bac |
--- a/tools/tools.c
|
|
|
541bac |
+++ b/tools/tools.c
|
|
|
541bac |
@@ -60,6 +60,7 @@ struct {
|
|
|
541bac |
{ "reset-computer", adcli_tool_computer_reset, "Reset a computer account", },
|
|
|
541bac |
{ "delete-computer", adcli_tool_computer_delete, "Delete a computer account", },
|
|
|
541bac |
{ "show-computer", adcli_tool_computer_show, "Show computer account attributes stored in AD", },
|
|
|
541bac |
+ { "create-msa", adcli_tool_computer_managed_service_account, "Create a managed service account in the given AD domain", },
|
|
|
541bac |
{ "create-user", adcli_tool_user_create, "Create a user account", },
|
|
|
541bac |
{ "delete-user", adcli_tool_user_delete, "Delete a user account", },
|
|
|
541bac |
{ "create-group", adcli_tool_group_create, "Create a group", },
|
|
|
541bac |
diff --git a/tools/tools.h b/tools/tools.h
|
|
|
541bac |
index 3702875..82d5e4e 100644
|
|
|
541bac |
--- a/tools/tools.h
|
|
|
541bac |
+++ b/tools/tools.h
|
|
|
541bac |
@@ -82,6 +82,10 @@ int adcli_tool_computer_show (adcli_conn *conn,
|
|
|
541bac |
int argc,
|
|
|
541bac |
char *argv[]);
|
|
|
541bac |
|
|
|
541bac |
+int adcli_tool_computer_managed_service_account (adcli_conn *conn,
|
|
|
541bac |
+ int argc,
|
|
|
541bac |
+ char *argv[]);
|
|
|
541bac |
+
|
|
|
541bac |
int adcli_tool_user_create (adcli_conn *conn,
|
|
|
541bac |
int argc,
|
|
|
541bac |
char *argv[]);
|
|
|
541bac |
--
|
|
|
541bac |
2.28.0
|
|
|
541bac |
|