#4607 ipa-getkeytab fails if -k points to empty file or a symlink to nonexistent file
Closed: fixed 5 years ago by cheimes. Opened 9 years ago by adelton.

For certain deployments like containerized setups with configuration and data in volumes, it would be useful to be able to create symlinks like /etc/krb5.keytab -> /data/etc/krb5.keytab, /etc/http.keytab -> /data/etc/http.keytab in the image and then be able to run ipa-getkeytab and have the keytab land in the target directory, on the volume. Since ipa-getkeytab is called as part of standard installers, we cannot really give it the different path directly.

However, ipa-getkeytab does not seem to like it when the path is a symlink (to existing empty or nonexisting file).

The error I get is

Failed to add key to the keytab

Looking at ipa-getkeytab.c, the call failing is actually krb5_kt_add_entry and it's probably OK if the file is not correct keytab format. But perhaps ipa-getkeytab could check what the file looks like (is empty; symlink target does not exist) and initialize the file to be a correct empty keytab.


Processing 4.2 backlog. This ticket was found as something that is not a priority for the nearest releases.

But as usual, please feel free to discuss your use cases or contribute patches, to make that happen sooner!

Metadata Update from @adelton:
- Issue assigned to someone
- Issue set to the milestone: Future Releases

7 years ago

Metadata Update from @pvoborni:
- Issue close_status updated to: None
- Issue tagged with: containers

6 years ago

The case fails deep within MIT KRB5 library. In case the keytab file is a dangling symlink, open("dangling-symlink", O_RDWR) fails with ENOENT. A missing file fails with the same errno.

In bot cases krb5_kt_add_entry uses k5_create_secure_file to create the file. That function calls open() again with flags O_RDWR | O_CREAT | O_EXCL. O_EXCL does not follow symlinks and fails.

It should be possible to work around the issue by calling readlink to resolve the symbolic link before passing it to Kerberos.

diff --git a/client/ipa-getkeytab.c b/client/ipa-getkeytab.c
index 8a7ddd5ab..681cada91 100644
--- a/client/ipa-getkeytab.c
+++ b/client/ipa-getkeytab.c
@@ -677,6 +677,7 @@ int main(int argc, const char *argv[])
        static const char *server = NULL;
        static const char *principal = NULL;
        static const char *keytab = NULL;
+       char keytab_resolved[PATH_MAX + 1] = {0};
        static const char *enctypes_string = NULL;
        static const char *binddn = NULL;
        static const char *bindpw = NULL;
@@ -860,10 +861,29 @@ int main(int argc, const char *argv[])
                }
        }

-       ret = asprintf(&ktname, "WRFILE:%s", keytab);
-       if (ret == -1) {
-               exit(3);
-       }
+    /* Resolve symlink in case keytab is a dangling symlink.
+     * see https://pagure.io/freeipa/issue/4607
+     * For simplicity, only one level is resolved.
+     */
+    errno = 0;
+    ret = readlink(keytab, keytab_resolved, PATH_MAX + 1);
+    if ((ret == -1)) {
+        if (errno == EINVAL) {
+            /* not a symlink, use keytab */
+            ret = asprintf(&ktname, "WRFILE:%s", keytab);
+            if (ret == -1) {
+                exit(3);
+            }
+        } else {
+            perror(keytab);
+            exit(3);
+        }
+    } else {
+        ret = asprintf(&ktname, "WRFILE:%s", keytab_resolved);
+        if (ret == -1) {
+            exit(3);
+        }
+    }

Out of curiosity. What is the reason to use a symlink to keytab?
This may lead to host compromise, isn't it?

Jan explained this in the initial message. It's useful for certain container setups with a read-only /etc.

You are correct. Symlinks must be resolved with care, as they can be abused to mount symlink attacks. My initial PoC is vulnerable. The current patch only resolves the symlink when the symlink is owned by the current effective user and group.

Metadata Update from @cheimes:
- Custom field on_review adjusted to https://github.com/freeipa/freeipa/pull/2676 (was: 0)
- Custom field rhbz reset (from todo)
- Issue assigned to cheimes (was: someone)
- Issue set to the milestone: FreeIPA 4.7.3 (was: Future Releases)

5 years ago

If you indeed try to address the issue, I wonder if supporting writing to an existing but empty file, instead of creating the file via dangling symlink, might be easier done safely.

It's also worth considering using separate keytab files for services - keeps the credentials separate, and you may not have to touch /etc at all, depending.

It's mainly for two keytabs with hard-coded names: /etc/krb5.keytab and /etc/named.keytab. Jan had to patch the paths, see https://github.com/freeipa/freeipa-container/blob/master/patches/ipa-data-fedora-29.patch#L10

Would it be reasonable to consider a container a separate platform and override the values there?

Not really, running inside of container is really orthogonal complexity, as you can run any platform in the container.

master:

  • 53e0b22 ipa-getkeytab: resolve symlink

ipa-4-7:

  • f65f725 ipa-getkeytab: resolve symlink

Metadata Update from @cheimes:
- Issue close_status updated to: fixed
- Issue status updated to: Closed (was: Open)

5 years ago

master:

  • 885af7f Fix assign instead of compare

ipa-4-7:

  • 77d0996 Fix assign instead of compare

What is the expected behaviour with this patch? In Fedora rawhide container with freeipa-server-4.8.0-2.fc31.x86_64 when /etc/krb5.keytab is a dangling symlink to /data/etc/krb5.keytab, when I remove the

-    KRB5_KEYTAB = "/etc/krb5.keytab"
+    KRB5_KEYTAB = "/data/etc/krb5.keytab"

bits from https://github.com/freeipa/freeipa-container/blob/master/patches/ipa-data-fedora-31.patch, I get

  [5/10]: creating a keytab for the directory
  [6/10]: creating a keytab for the machine
  [error] FileNotFoundError: [Errno 2] No such file or directory: '/etc/krb5.keytab'
[Errno 2] No such file or directory: '/etc/krb5.keytab'
The ipa-server-install command failed. See /var/log/ipaserver-install.log for more information

and /var/log/ipaserver-install.log ends with

2019-08-15T08:14:52Z DEBUG Starting external process
2019-08-15T08:14:52Z DEBUG args=['/usr/sbin/kadmin.local', '-q', 'ktadd -k /etc/dirsrv/ds.keytab ldap/ipa.example.test@EXAMPLE.TEST', '-x', 'ipa-setup-override-restrictions']
2019-08-15T08:14:52Z DEBUG Process finished, return code=0
2019-08-15T08:14:52Z DEBUG stdout=Authenticating as principal root/admin@EXAMPLE.TEST with password.
Entry for principal ldap/ipa.example.test@EXAMPLE.TEST with kvno 2, encryption type aes256-cts-hmac-sha1-96 added to keytab WRFILE:/etc/dirsrv/ds.keytab.
Entry for principal ldap/ipa.example.test@EXAMPLE.TEST with kvno 2, encryption type aes128-cts-hmac-sha1-96 added to keytab WRFILE:/etc/dirsrv/ds.keytab.
Entry for principal ldap/ipa.example.test@EXAMPLE.TEST with kvno 2, encryption type camellia128-cts-cmac added to keytab WRFILE:/etc/dirsrv/ds.keytab.
Entry for principal ldap/ipa.example.test@EXAMPLE.TEST with kvno 2, encryption type camellia256-cts-cmac added to keytab WRFILE:/etc/dirsrv/ds.keytab.

2019-08-15T08:14:52Z DEBUG stderr=
2019-08-15T08:14:52Z DEBUG step duration: krb5kdc __create_ds_keytab 0.33 sec
2019-08-15T08:14:52Z DEBUG   [6/10]: creating a keytab for the machine
2019-08-15T08:14:52Z DEBUG Starting external process
2019-08-15T08:14:52Z DEBUG args=['/usr/sbin/kadmin.local', '-q', 'addprinc -randkey host/ipa.example.test@EXAMPLE.TEST', '-x', 'ipa-setup-override-restrictions']
2019-08-15T08:14:52Z DEBUG Process finished, return code=0
2019-08-15T08:14:52Z DEBUG stdout=Authenticating as principal root/admin@EXAMPLE.TEST with password.
Principal "host/ipa.example.test@EXAMPLE.TEST" created.

2019-08-15T08:14:52Z DEBUG stderr=WARNING: no policy specified for host/ipa.example.test@EXAMPLE.TEST; defaulting to no policy

2019-08-15T08:14:52Z DEBUG Backing up system configuration file '/etc/krb5.keytab'
2019-08-15T08:14:52Z DEBUG   -> Not backing up - '/etc/krb5.keytab' doesn't exist
2019-08-15T08:14:52Z DEBUG Starting external process
2019-08-15T08:14:52Z DEBUG args=['/usr/sbin/kadmin.local', '-q', 'ktadd -k /etc/krb5.keytab host/ipa.example.test@EXAMPLE.TEST', '-x', 'ipa-setup-override-restrictions']
2019-08-15T08:14:53Z DEBUG Process finished, return code=0
2019-08-15T08:14:53Z DEBUG stdout=Authenticating as principal root/admin@EXAMPLE.TEST with password.

2019-08-15T08:14:53Z DEBUG stderr=kadmin.local: Key table file '/etc/krb5.keytab' not found while adding key to keytab

2019-08-15T08:14:53Z DEBUG Traceback (most recent call last):
  File "/usr/lib/python3.7/site-packages/ipaserver/install/service.py", line 603, in start_creation
    run_step(full_msg, method)
  File "/usr/lib/python3.7/site-packages/ipaserver/install/service.py", line 589, in run_step
    method()
  File "/usr/lib/python3.7/site-packages/ipaserver/install/krbinstance.py", line 401, in __create_host_keytab
    os.chown(paths.KRB5_KEYTAB, 0, 0)
FileNotFoundError: [Errno 2] No such file or directory: '/etc/krb5.keytab'

2019-08-15T08:14:53Z DEBUG   [error] FileNotFoundError: [Errno 2] No such file or directory: '/etc/krb5.keytab'
2019-08-15T08:14:53Z DEBUG   File "/usr/lib/python3.7/site-packages/ipapython/admintool.py", line 179, in execute
    return_value = self.run()
  File "/usr/lib/python3.7/site-packages/ipapython/install/cli.py", line 340, in run
    return cfgr.run()
  File "/usr/lib/python3.7/site-packages/ipapython/install/core.py", line 360, in run
    return self.execute()
  File "/usr/lib/python3.7/site-packages/ipapython/install/core.py", line 386, in execute
    for rval in self._executor():
  File "/usr/lib/python3.7/site-packages/ipapython/install/core.py", line 431, in __runner
    exc_handler(exc_info)
  File "/usr/lib/python3.7/site-packages/ipapython/install/core.py", line 460, in _handle_execute_exception
    self._handle_exception(exc_info)
  File "/usr/lib/python3.7/site-packages/ipapython/install/core.py", line 450, in _handle_exception
    six.reraise(*exc_info)
  File "/usr/lib/python3.7/site-packages/six.py", line 693, in reraise
    raise value
  File "/usr/lib/python3.7/site-packages/ipapython/install/core.py", line 421, in __runner
    step()
  File "/usr/lib/python3.7/site-packages/ipapython/install/core.py", line 418, in <lambda>
    step = lambda: next(self.__gen)
  File "/usr/lib/python3.7/site-packages/ipapython/install/util.py", line 81, in run_generator_with_yield_from
    six.reraise(*exc_info)
  File "/usr/lib/python3.7/site-packages/six.py", line 693, in reraise
    raise value
  File "/usr/lib/python3.7/site-packages/ipapython/install/util.py", line 59, in run_generator_with_yield_from
    value = gen.send(prev_value)
  File "/usr/lib/python3.7/site-packages/ipapython/install/core.py", line 655, in _configure
    next(executor)
  File "/usr/lib/python3.7/site-packages/ipapython/install/core.py", line 431, in __runner
    exc_handler(exc_info)
  File "/usr/lib/python3.7/site-packages/ipapython/install/core.py", line 460, in _handle_execute_exception
    self._handle_exception(exc_info)
  File "/usr/lib/python3.7/site-packages/ipapython/install/core.py", line 518, in _handle_exception
    self.__parent._handle_exception(exc_info)
  File "/usr/lib/python3.7/site-packages/ipapython/install/core.py", line 450, in _handle_exception
    six.reraise(*exc_info)
  File "/usr/lib/python3.7/site-packages/six.py", line 693, in reraise
    raise value
  File "/usr/lib/python3.7/site-packages/ipapython/install/core.py", line 515, in _handle_exception
    super(ComponentBase, self)._handle_exception(exc_info)
  File "/usr/lib/python3.7/site-packages/ipapython/install/core.py", line 450, in _handle_exception
    six.reraise(*exc_info)
  File "/usr/lib/python3.7/site-packages/six.py", line 693, in reraise
    raise value
  File "/usr/lib/python3.7/site-packages/ipapython/install/core.py", line 421, in __runner
    step()
  File "/usr/lib/python3.7/site-packages/ipapython/install/core.py", line 418, in <lambda>
    step = lambda: next(self.__gen)
  File "/usr/lib/python3.7/site-packages/ipapython/install/util.py", line 81, in run_generator_with_yield_from
    six.reraise(*exc_info)
  File "/usr/lib/python3.7/site-packages/six.py", line 693, in reraise
    raise value
  File "/usr/lib/python3.7/site-packages/ipapython/install/util.py", line 59, in run_generator_with_yield_from
    value = gen.send(prev_value)
  File "/usr/lib/python3.7/site-packages/ipapython/install/common.py", line 65, in _install
    for unused in self._installer(self.parent):
  File "/usr/lib/python3.7/site-packages/ipaserver/install/server/__init__.py", line 557, in main
    master_install(self)
  File "/usr/lib/python3.7/site-packages/ipaserver/install/server/install.py", line 255, in decorated
    func(installer)
  File "/usr/lib/python3.7/site-packages/ipaserver/install/server/install.py", line 846, in install
    subject_base=options.subject_base)
  File "/usr/lib/python3.7/site-packages/ipaserver/install/krbinstance.py", line 212, in create_instance
    self.start_creation()
  File "/usr/lib/python3.7/site-packages/ipaserver/install/service.py", line 603, in start_creation
    run_step(full_msg, method)
  File "/usr/lib/python3.7/site-packages/ipaserver/install/service.py", line 589, in run_step
    method()
  File "/usr/lib/python3.7/site-packages/ipaserver/install/krbinstance.py", line 401, in __create_host_keytab
    os.chown(paths.KRB5_KEYTAB, 0, 0)

2019-08-15T08:14:53Z DEBUG The ipa-server-install command failed, exception: FileNotFoundError: [Errno 2] No such file or directory: '/etc/krb5.keytab'
2019-08-15T08:14:53Z ERROR [Errno 2] No such file or directory: '/etc/krb5.keytab'
2019-08-15T08:14:53Z ERROR The ipa-server-install command failed. See /var/log/ipaserver-install.log for more information

Christian pointed out on the IRC that this issue is about ipa-getkeytab while the failure I see is from kadmin.local.

Sorry about the confusion.

Login to comment on this ticket.

Metadata