mirror of
				https://salsa.debian.org/freeipa-team/freeipa.git
				synced 2025-02-25 18:55:28 -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:
		@@ -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):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user