mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2024-12-23 23:50:03 -06:00
ipa-getkeytab: resolve symlink
Resolve one level of symbolic links to support a dangling symlink as keytab target. To prevent symlink attacks, only resolve symlink when the symlink is owned by the current effective user and group, or by root. Fixes: https://pagure.io/freeipa/issue/4607 Signed-off-by: Christian Heimes <cheimes@redhat.com> Reviewed-By: Rob Crittenden <rcritten@redhat.com> Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
This commit is contained in:
parent
13f20854b4
commit
53e0b2255d
@ -672,6 +672,56 @@ int read_ipa_config(struct ipa_config **ipacfg)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int resolve_ktname(const char *keytab, char **ktname, char **err_msg)
|
||||||
|
{
|
||||||
|
char keytab_resolved[PATH_MAX + 1];
|
||||||
|
struct stat st;
|
||||||
|
struct stat lst;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
*err_msg = NULL;
|
||||||
|
|
||||||
|
/* Resolve keytab symlink to support dangling symlinks, see
|
||||||
|
* https://pagure.io/freeipa/issue/4607. To prevent symlink attacks,
|
||||||
|
* the symlink is only resolved owned by the current user or by
|
||||||
|
* root. For simplicity, only one level if indirection is resolved.
|
||||||
|
*/
|
||||||
|
if ((stat(keytab, &st) == -1) &&
|
||||||
|
(errno = ENOENT) &&
|
||||||
|
(lstat(keytab, &lst) == 0) &&
|
||||||
|
(S_ISLNK(lst.st_mode))) {
|
||||||
|
/* keytab is a dangling symlink. */
|
||||||
|
if (((lst.st_uid == 0) && (lst.st_gid == 0)) ||
|
||||||
|
((lst.st_uid == geteuid()) && (lst.st_gid == getegid()))) {
|
||||||
|
/* Either root or current user owns symlink, resolve symlink and
|
||||||
|
* return the resolved symlink. */
|
||||||
|
ret = readlink(keytab, keytab_resolved, PATH_MAX + 1);
|
||||||
|
if ((ret == -1) || (ret > PATH_MAX)) {
|
||||||
|
*err_msg = _("Failed to resolve symlink to keytab.\n");
|
||||||
|
return ENOENT;
|
||||||
|
}
|
||||||
|
keytab_resolved[ret] = '\0';
|
||||||
|
ret = asprintf(ktname, "WRFILE:%s", keytab_resolved);
|
||||||
|
if (ret == -1) {
|
||||||
|
*err_msg = strerror(errno);
|
||||||
|
return ENOMEM;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
*err_msg = _("keytab is a dangling symlink and owned by another "
|
||||||
|
"user.\n");
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ret = asprintf(ktname, "WRFILE:%s", keytab);
|
||||||
|
if (ret == -1) {
|
||||||
|
*err_msg = strerror(errno);
|
||||||
|
return ENOMEM;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, const char *argv[])
|
int main(int argc, const char *argv[])
|
||||||
{
|
{
|
||||||
static const char *server = NULL;
|
static const char *server = NULL;
|
||||||
@ -860,11 +910,6 @@ int main(int argc, const char *argv[])
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = asprintf(&ktname, "WRFILE:%s", keytab);
|
|
||||||
if (ret == -1) {
|
|
||||||
exit(3);
|
|
||||||
}
|
|
||||||
|
|
||||||
krberr = krb5_parse_name(krbctx, principal, &sprinc);
|
krberr = krb5_parse_name(krbctx, principal, &sprinc);
|
||||||
if (krberr) {
|
if (krberr) {
|
||||||
fprintf(stderr, _("Invalid Service Principal Name\n"));
|
fprintf(stderr, _("Invalid Service Principal Name\n"));
|
||||||
@ -889,6 +934,12 @@ int main(int argc, const char *argv[])
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = resolve_ktname(keytab, &ktname, &err_msg);
|
||||||
|
if (krberr) {
|
||||||
|
fprintf(stderr, "%s", err_msg);
|
||||||
|
exit(ret);
|
||||||
|
}
|
||||||
|
|
||||||
krberr = krb5_kt_resolve(krbctx, ktname, &kt);
|
krberr = krb5_kt_resolve(krbctx, ktname, &kt);
|
||||||
if (krberr) {
|
if (krberr) {
|
||||||
fprintf(stderr, _("Failed to open Keytab\n"));
|
fprintf(stderr, _("Failed to open Keytab\n"));
|
||||||
|
@ -190,6 +190,23 @@ class test_ipagetkeytab(KeytabRetrievalTest):
|
|||||||
except Exception as errmsg:
|
except Exception as errmsg:
|
||||||
assert('Unable to bind to LDAP. Error initializing principal' in str(errmsg))
|
assert('Unable to bind to LDAP. Error initializing principal' in str(errmsg))
|
||||||
|
|
||||||
|
def test_dangling_symlink(self, test_service):
|
||||||
|
# see https://pagure.io/freeipa/issue/4607
|
||||||
|
test_service.ensure_exists()
|
||||||
|
|
||||||
|
fd, symlink_target = tempfile.mkstemp()
|
||||||
|
os.close(fd)
|
||||||
|
os.unlink(symlink_target)
|
||||||
|
# create dangling symlink
|
||||||
|
os.symlink(self.keytabname, symlink_target)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.assert_success(test_service.name, raiseonerr=True)
|
||||||
|
assert os.path.isfile(symlink_target)
|
||||||
|
assert os.path.samefile(self.keytabname, symlink_target)
|
||||||
|
finally:
|
||||||
|
os.unlink(symlink_target)
|
||||||
|
|
||||||
|
|
||||||
class TestBindMethods(KeytabRetrievalTest):
|
class TestBindMethods(KeytabRetrievalTest):
|
||||||
"""
|
"""
|
||||||
|
Loading…
Reference in New Issue
Block a user