Unify access to FQDN

FreeIPA's Python and C code used different approaches to get the FQDN of
the host. Some places assumed that gethostname() returns a FQDN. Other
code paths used glibc's resolver to resolve the current node name to a
FQDN.

Python code now uses the ipalib.constants.FQDN where a fully qualified
domain name is expected. The variable is initialized only once and avoids
potential DNS lookups.

C code uses a new helper function ipa_gethostfqdn() in util package. The
function implements similar logic as gethostfqdn() except it uses more
modern getaddrinfo(). The result is cached as well.

Fixes: https://pagure.io/freeipa/issue/8501
Signed-off-by: Christian Heimes <cheimes@redhat.com>
Reviewed-By: Fraser Tweedale <ftweedal@redhat.com>
This commit is contained in:
Christian Heimes 2020-09-11 14:49:16 +02:00 committed by Fraser Tweedale
parent 5155280bb4
commit e28ec76898
23 changed files with 226 additions and 67 deletions

View File

@ -1,9 +1,11 @@
AM_CPPFLAGS := -I$(top_srcdir)/util
AM_CFLAGS := @LDAP_CFLAGS@ @LIBVERTO_CFLAGS@ @KRB5_CFLAGS@ @NSPR_CFLAGS@ AM_CFLAGS := @LDAP_CFLAGS@ @LIBVERTO_CFLAGS@ @KRB5_CFLAGS@ @NSPR_CFLAGS@
AM_LDFLAGS := @LDAP_LIBS@ @LIBVERTO_LIBS@ @KRAD_LIBS@ @KRB5_LIBS@ AM_LDFLAGS := @LDAP_LIBS@ @LIBVERTO_LIBS@ @KRAD_LIBS@ @KRB5_LIBS@
noinst_HEADERS = internal.h noinst_HEADERS = internal.h
appdir = $(libexecdir)/ipa/ appdir = $(libexecdir)/ipa/
app_PROGRAMS = ipa-otpd app_PROGRAMS = ipa-otpd
ipa_otpd_LDADD = $(top_builddir)/util/libutil.la
dist_noinst_DATA = ipa-otpd.socket.in ipa-otpd@.service.in test.py dist_noinst_DATA = ipa-otpd.socket.in ipa-otpd@.service.in test.py
systemdsystemunit_DATA = ipa-otpd.socket ipa-otpd@.service systemdsystemunit_DATA = ipa-otpd.socket ipa-otpd@.service

View File

@ -32,6 +32,7 @@
#include <signal.h> #include <signal.h>
#include <stdbool.h> #include <stdbool.h>
#include "ipa_hostname.h"
/* Our global state. */ /* Our global state. */
struct otpd_context ctx; struct otpd_context ctx;
@ -212,7 +213,7 @@ static krb5_error_code setup_ldap(const char *uri, krb5_boolean bind,
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
char hostname[HOST_NAME_MAX + 1]; char hostname[IPA_HOST_NAME_LEN];
krb5_error_code retval; krb5_error_code retval;
krb5_data hndata; krb5_data hndata;
verto_ev *sig; verto_ev *sig;
@ -227,7 +228,7 @@ int main(int argc, char **argv)
memset(&ctx, 0, sizeof(ctx)); memset(&ctx, 0, sizeof(ctx));
ctx.exitstatus = 1; ctx.exitstatus = 1;
if (gethostname(hostname, sizeof(hostname)) < 0) { if (ipa_gethostfqdn(hostname) < 0) {
otpd_log_err(errno, "Unable to get hostname"); otpd_log_err(errno, "Unable to get hostname");
goto error; goto error;
} }

View File

@ -36,6 +36,7 @@ char *smb_xstrdup(const char *s);
#include <sasl/sasl.h> #include <sasl/sasl.h>
#include <krb5/krb5.h> #include <krb5/krb5.h>
#include <sss_idmap.h> #include <sss_idmap.h>
#include "ipa_hostname.h"
#include "ipa_asn1.h" #include "ipa_asn1.h"
#include "ipa_pwd.h" #include "ipa_pwd.h"
#include "ipa_mspac.h" #include "ipa_mspac.h"
@ -4440,7 +4441,7 @@ static char *sec_key(TALLOC_CTX *mem_ctx, const char *d)
static NTSTATUS save_sid_to_secret(struct ipasam_private *ipasam_state) static NTSTATUS save_sid_to_secret(struct ipasam_private *ipasam_state)
{ {
char hostname[255]; char hostname[IPA_HOST_NAME_LEN];
int ret; int ret;
char *p; char *p;
TALLOC_CTX *tmp_ctx; TALLOC_CTX *tmp_ctx;
@ -4466,13 +4467,12 @@ static NTSTATUS save_sid_to_secret(struct ipasam_private *ipasam_state)
goto done; goto done;
} }
ret = gethostname(hostname, sizeof(hostname)); ret = ipa_gethostfqdn(hostname);
if (ret == -1) { if (ret == -1) {
DEBUG(1, ("gethostname failed.\n")); DEBUG(1, ("gethostname failed.\n"));
status = NT_STATUS_UNSUCCESSFUL; status = NT_STATUS_UNSUCCESSFUL;
goto done; goto done;
} }
hostname[sizeof(hostname)-1] = '\0';
p = strchr(hostname, '.'); p = strchr(hostname, '.');
if (p != NULL) { if (p != NULL) {
*p = '\0'; *p = '\0';
@ -4724,7 +4724,7 @@ static NTSTATUS ipasam_generate_principals(struct ipasam_private *ipasam_state)
int ret; int ret;
krb5_context context; krb5_context context;
NTSTATUS status = NT_STATUS_UNSUCCESSFUL; NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
char hostname[255]; char hostname[IPA_HOST_NAME_LEN];
char *default_realm = NULL; char *default_realm = NULL;
if (!ipasam_state) { if (!ipasam_state) {
@ -4736,12 +4736,11 @@ static NTSTATUS ipasam_generate_principals(struct ipasam_private *ipasam_state)
return status; return status;
} }
ret = gethostname(hostname, sizeof(hostname)); ret = ipa_gethostfqdn(hostname);
if (ret == -1) { if (ret == -1) {
DEBUG(1, ("gethostname failed.\n")); DEBUG(1, ("gethostname failed.\n"));
goto done; goto done;
} }
hostname[sizeof(hostname)-1] = '\0';
rc = krb5_get_default_realm(context, &default_realm); rc = krb5_get_default_realm(context, &default_realm);
if (rc) { if (rc) {

View File

@ -5,6 +5,7 @@ PLUGIN_COMMON_DIR = $(srcdir)/../common
AM_CPPFLAGS = \ AM_CPPFLAGS = \
-I$(srcdir) \ -I$(srcdir) \
-I$(PLUGIN_COMMON_DIR) \ -I$(PLUGIN_COMMON_DIR) \
-I$(top_srcdir)/util \
-DPREFIX=\""$(prefix)"\" \ -DPREFIX=\""$(prefix)"\" \
-DBINDIR=\""$(bindir)"\" \ -DBINDIR=\""$(bindir)"\" \
-DLIBDIR=\""$(libdir)"\" \ -DLIBDIR=\""$(libdir)"\" \
@ -31,6 +32,7 @@ libipa_cldap_la_SOURCES = \
libipa_cldap_la_LDFLAGS = -avoid-version libipa_cldap_la_LDFLAGS = -avoid-version
libipa_cldap_la_LIBADD = \ libipa_cldap_la_LIBADD = \
$(top_builddir)/util/libutil.la \
$(LDAP_LIBS) \ $(LDAP_LIBS) \
$(NDRNBT_LIBS) \ $(NDRNBT_LIBS) \
$(NULL) $(NULL)
@ -49,6 +51,7 @@ ipa_cldap_tests_LDFLAGS = \
-rpath $(shell pkg-config --libs-only-L dirsrv | sed -e 's/-L//') \ -rpath $(shell pkg-config --libs-only-L dirsrv | sed -e 's/-L//') \
$(NULL) $(NULL)
ipa_cldap_tests_LDADD = \ ipa_cldap_tests_LDADD = \
$(top_builddir)/util/libutil.la \
$(CMOCKA_LIBS) \ $(CMOCKA_LIBS) \
$(NDRNBT_LIBS) \ $(NDRNBT_LIBS) \
$(DIRSRV_LIBS) \ $(DIRSRV_LIBS) \

View File

@ -38,6 +38,7 @@
* END COPYRIGHT BLOCK **/ * END COPYRIGHT BLOCK **/
#include "ipa_cldap.h" #include "ipa_cldap.h"
#include "ipa_hostname.h"
#include <endian.h> #include <endian.h>
#include <talloc.h> #include <talloc.h>
#include <ctype.h> #include <ctype.h>
@ -236,7 +237,7 @@ int ipa_cldap_netlogon(struct ipa_cldap_ctx *ctx,
struct ipa_cldap_req *req, struct ipa_cldap_req *req,
struct berval *reply) struct berval *reply)
{ {
char hostname[MAXHOSTNAMELEN + 1]; /* NOTE: lenght hardcoded in kernel */ char hostname[IPA_HOST_NAME_LEN];
char *domain = NULL; char *domain = NULL;
char *our_domain = NULL; char *our_domain = NULL;
char *guid = NULL; char *guid = NULL;
@ -321,13 +322,11 @@ int ipa_cldap_netlogon(struct ipa_cldap_ctx *ctx,
goto done; goto done;
} }
ret = gethostname(hostname, MAXHOSTNAMELEN); ret = ipa_gethostfqdn(hostname);
if (ret == -1) { if (ret == -1) {
ret = errno; ret = errno;
goto done; goto done;
} }
/* Make double sure it is terminated */
hostname[MAXHOSTNAMELEN] = '\0';
dot = strchr(hostname, '.'); dot = strchr(hostname, '.');
if (!dot) { if (!dot) {
/* this name is not fully qualified, therefore invalid */ /* this name is not fully qualified, therefore invalid */

View File

@ -30,6 +30,7 @@ from ipaplatform.paths import paths
from ipaserver.install import (replication, installutils, bindinstance, from ipaserver.install import (replication, installutils, bindinstance,
cainstance) cainstance)
from ipalib import api, errors from ipalib import api, errors
from ipalib.constants import FQDN
from ipalib.util import has_managed_topology from ipalib.util import has_managed_topology
from ipapython import ipautil, ipaldap, version from ipapython import ipautil, ipaldap, version
from ipapython.admintool import ScriptError from ipapython.admintool import ScriptError
@ -343,7 +344,7 @@ def re_initialize(realm, options):
if not options.fromhost: if not options.fromhost:
sys.exit("re-initialize requires the option --from <host name>") sys.exit("re-initialize requires the option --from <host name>")
thishost = installutils.get_fqdn() thishost = FQDN
try: try:
repl = replication.get_cs_replication_manager(realm, options.fromhost, repl = replication.get_cs_replication_manager(realm, options.fromhost,
@ -383,7 +384,7 @@ def force_sync(realm, thishost, fromhost, dirman_passwd):
def set_renewal_master(realm, replica): def set_renewal_master(realm, replica):
if not replica: if not replica:
replica = installutils.get_fqdn() replica = FQDN
ca = cainstance.CAInstance(realm) ca = cainstance.CAInstance(realm)
if ca.is_renewal_master(replica): if ca.is_renewal_master(replica):
@ -434,7 +435,7 @@ def main():
if options.host: if options.host:
host = options.host host = options.host
else: else:
host = installutils.get_fqdn() host = FQDN
options.host = host options.host = host

View File

@ -9,7 +9,6 @@ import argparse
import logging import logging
import os import os
import platform import platform
import socket
import warnings import warnings
from custodia.message.kem import KEY_USAGE_SIG, KEY_USAGE_ENC, KEY_USAGE_MAP from custodia.message.kem import KEY_USAGE_SIG, KEY_USAGE_ENC, KEY_USAGE_MAP
@ -136,7 +135,6 @@ class IPACustodiaTester:
def check(self): def check(self):
self.status() self.status()
self.check_fqdn()
self.check_files() self.check_files()
self.check_client() self.check_client()
self.check_jwk() self.check_jwk()
@ -156,13 +154,6 @@ class IPACustodiaTester:
if self.host == self.args.server: if self.host == self.args.server:
self.warning("Performing self-test only.") self.warning("Performing self-test only.")
def check_fqdn(self):
fqdn = socket.getfqdn()
if self.host != fqdn:
self.warning(
"socket.getfqdn() reports hostname '{}'".format(fqdn)
)
def check_files(self): def check_files(self):
for filename in self.files: for filename in self.files:
if not os.path.isfile(filename): if not os.path.isfile(filename):

View File

@ -28,6 +28,7 @@ from ipapython.dn import DN
from ipapython import version from ipapython import version
from ipapython import ipautil, certdb from ipapython import ipautil, certdb
from ipalib import api, errors, x509 from ipalib import api, errors, x509
from ipalib.constants import FQDN
from ipaserver.install import installutils from ipaserver.install import installutils
# pylint: disable=deprecated-module # pylint: disable=deprecated-module
from optparse import OptionGroup, OptionValueError from optparse import OptionGroup, OptionValueError
@ -205,7 +206,7 @@ def parse_options():
parser.error("No action: you should select either --replica or --master option.") parser.error("No action: you should select either --replica or --master option.")
if not options.hostname: if not options.hostname:
options.hostname = socket.getfqdn() options.hostname = FQDN
return safe_options, options return safe_options, options

View File

@ -38,6 +38,7 @@ from ipaserver.install import bindinstance, cainstance
from ipaserver.install import opendnssecinstance, dnskeysyncinstance from ipaserver.install import opendnssecinstance, dnskeysyncinstance
from ipapython import version, ipaldap from ipapython import version, ipaldap
from ipalib import api, errors from ipalib import api, errors
from ipalib.constants import FQDN
from ipalib.util import has_managed_topology, verify_host_resolvable from ipalib.util import has_managed_topology, verify_host_resolvable
from ipapython.ipa_log_manager import standard_logging_setup from ipapython.ipa_log_manager import standard_logging_setup
from ipapython.dn import DN from ipapython.dn import DN
@ -1525,7 +1526,7 @@ def main(options, args):
if options.host: if options.host:
host = options.host host = options.host
else: else:
host = installutils.get_fqdn() host = FQDN
options.host = host options.host = host

View File

@ -20,13 +20,13 @@
from __future__ import absolute_import from __future__ import absolute_import
import logging import logging
import socket
import six import six
from dns import rdatatype from dns import rdatatype
from dns.exception import DNSException from dns.exception import DNSException
from ipalib import errors from ipalib import errors
from ipalib.constants import FQDN
from ipalib.util import validate_domain_name from ipalib.util import validate_domain_name
from ipapython.dnsutil import query_srv, resolve from ipapython.dnsutil import query_srv, resolve
@ -222,7 +222,7 @@ class IPADiscovery:
if not domain: # domain not provided do full DNS discovery if not domain: # domain not provided do full DNS discovery
# get the local host name # get the local host name
if not hostname: if not hostname:
hostname = socket.getfqdn() hostname = FQDN
logger.debug('Hostname: %s', hostname) logger.debug('Hostname: %s', hostname)
if not hostname: if not hostname:
return BAD_HOST_CONFIG return BAD_HOST_CONFIG

View File

@ -36,7 +36,7 @@ from urllib.parse import urlparse, urlunparse
from ipalib import api, errors, x509 from ipalib import api, errors, x509
from ipalib import sysrestore from ipalib import sysrestore
from ipalib.constants import IPAAPI_USER, MAXHOSTNAMELEN from ipalib.constants import FQDN, IPAAPI_USER, MAXHOSTNAMELEN
from ipalib.install import certmonger, certstore, service from ipalib.install import certmonger, certstore, service
from ipalib.install import hostname as hostname_ from ipalib.install import hostname as hostname_
from ipalib.facts import is_ipa_client_configured, is_ipa_configured from ipalib.facts import is_ipa_client_configured, is_ipa_configured
@ -2121,7 +2121,7 @@ def install_check(options):
hostname = options.hostname hostname = options.hostname
hostname_source = 'Provided as option' hostname_source = 'Provided as option'
else: else:
hostname = socket.getfqdn() hostname = FQDN
hostname_source = "Machine's FQDN" hostname_source = "Machine's FQDN"
if hostname != hostname.lower(): if hostname != hostname.lower():
raise ScriptError( raise ScriptError(
@ -3270,7 +3270,7 @@ def uninstall(options):
pass pass
if hostname is None: if hostname is None:
hostname = socket.getfqdn() hostname = FQDN
ipa_db = certdb.NSSDatabase(paths.IPA_NSSDB_DIR) ipa_db = certdb.NSSDatabase(paths.IPA_NSSDB_DIR)
sys_db = certdb.NSSDatabase(paths.NSS_DB_DIR) sys_db = certdb.NSSDatabase(paths.NSS_DB_DIR)

View File

@ -23,18 +23,14 @@ All constants centralised in one file.
""" """
import os import os
import socket
from ipaplatform.constants import constants as _constants from ipaplatform.constants import constants as _constants
from ipapython.dn import DN from ipapython.dn import DN
from ipapython.fqdn import gethostfqdn
from ipapython.version import VERSION, API_VERSION from ipapython.version import VERSION, API_VERSION
try:
FQDN = socket.getfqdn() FQDN = gethostfqdn()
except Exception:
try:
FQDN = socket.gethostname()
except Exception:
FQDN = None
# TLS related constants # TLS related constants
# * SSL2 and SSL3 are broken. # * SSL2 and SSL3 are broken.

View File

@ -25,7 +25,6 @@ from optparse import (
from copy import copy from copy import copy
from configparser import SafeConfigParser from configparser import SafeConfigParser
from urllib.parse import urlsplit from urllib.parse import urlsplit
import socket
import functools import functools
from dns.exception import DNSException from dns.exception import DNSException
@ -211,8 +210,11 @@ def __discover_config(discover_server = True):
servers = query_srv(name) servers = query_srv(name)
except DNSException: except DNSException:
# try cycling on domain components of FQDN # try cycling on domain components of FQDN
# pylint: disable=ipa-forbidden-import
from ipalib.constants import FQDN
# pylint: enable=ipa-forbidden-import
try: try:
domain = dns.name.from_text(socket.getfqdn()) domain = dns.name.from_text(FQDN)
except DNSException: except DNSException:
return False return False

30
ipapython/fqdn.py Normal file
View File

@ -0,0 +1,30 @@
#
# Copyright (C) 2020 FreeIPA Contributors see COPYING for license
#
"""Get host's FQDN
"""
import socket
def gethostfqdn():
hostname = socket.gethostname()
# optional optimization, consider hostname with dot as FQDN
if "." in hostname:
return hostname
# this call can never fail except for misconfigured nsswitch.conf
# without nss-myhostname provider. The myhostname provider translates
# gethostname() to local interfaces.
gai = socket.getaddrinfo(
hostname,
None, # service/port is irrelevant
family=socket.AF_UNSPEC, # IPv4 or IPv6
type=socket.SOCK_DGRAM, # optimization, TCP/RAW gives same result
# include canonical name in first addrinfo struct
# only use address family when at least one non-local interface
# is configured with that address family
flags=socket.AI_CANONNAME | socket.AI_ADDRCONFIG
)
# first addrinfo struct, fourth field is canonical name
return gai[0][3]

View File

@ -30,11 +30,11 @@ import time
from ipalib import api, _ from ipalib import api, _
from ipalib import errors from ipalib import errors
from ipalib.constants import FQDN
from ipapython import ipautil from ipapython import ipautil
from ipapython.dn import DN from ipapython.dn import DN
from ipapython.dnsutil import query_srv from ipapython.dnsutil import query_srv
from ipapython.ipaldap import ldap_initialize from ipapython.ipaldap import ldap_initialize
from ipaserver.install import installutils
from ipaserver.dcerpc_common import (TRUST_BIDIRECTIONAL, from ipaserver.dcerpc_common import (TRUST_BIDIRECTIONAL,
TRUST_JOIN_EXTERNAL, TRUST_JOIN_EXTERNAL,
trust_type_string) trust_type_string)
@ -1645,7 +1645,7 @@ class TrustDomainJoins:
ld.creds.set_kerberos_state(credentials.MUST_USE_KERBEROS) ld.creds.set_kerberos_state(credentials.MUST_USE_KERBEROS)
ld.creds.guess(ld.parm) ld.creds.guess(ld.parm)
ld.creds.set_workstation(ld.hostname) ld.creds.set_workstation(ld.hostname)
ld.retrieve(installutils.get_fqdn()) ld.retrieve(FQDN)
self.local_domain = ld self.local_domain = ld
def populate_remote_domain(self, realm, realm_server=None, def populate_remote_domain(self, realm, realm_server=None,

View File

@ -50,7 +50,7 @@ import ipaplatform
from ipapython import ipautil, admintool, version, ipaldap from ipapython import ipautil, admintool, version, ipaldap
from ipapython.admintool import ScriptError, SERVER_NOT_CONFIGURED # noqa: E402 from ipapython.admintool import ScriptError, SERVER_NOT_CONFIGURED # noqa: E402
from ipapython.certdb import EXTERNAL_CA_TRUST_FLAGS from ipapython.certdb import EXTERNAL_CA_TRUST_FLAGS
from ipalib.constants import MAXHOSTNAMELEN from ipalib.constants import FQDN, MAXHOSTNAMELEN
from ipalib.util import validate_hostname from ipalib.util import validate_hostname
from ipalib import api, errors, x509 from ipalib import api, errors, x509
from ipalib.install import dnsforwarders from ipalib.install import dnsforwarders
@ -118,16 +118,16 @@ class ReplicaConfig:
subject_base = ipautil.dn_attribute_property('_subject_base') subject_base = ipautil.dn_attribute_property('_subject_base')
def get_fqdn(): def get_fqdn():
fqdn = "" """Get fully qualified domain name of current host
try:
fqdn = socket.getfqdn() :note: used by ansible_freeipa
except Exception: :deprecated: use ipalib.constants.FQDN
try: :return: str
fqdn = socket.gethostname() """
except Exception: return FQDN
fqdn = ""
return fqdn
def verify_fqdn(host_name, no_host_dns=False, local_hostname=True): def verify_fqdn(host_name, no_host_dns=False, local_hostname=True):
""" """

View File

@ -24,9 +24,9 @@ import ldap.schema
import ipapython.version import ipapython.version
from ipalib import api from ipalib import api
from ipalib.constants import FQDN
from ipapython.dn import DN from ipapython.dn import DN
from ipaserver.install.ldapupdate import connect from ipaserver.install.ldapupdate import connect
from ipaserver.install import installutils
SCHEMA_ELEMENT_CLASSES = ( SCHEMA_ELEMENT_CLASSES = (
@ -105,9 +105,7 @@ def update_schema(schema_files, ldapi=False):
""" """
SCHEMA_ELEMENT_CLASSES_KEYS = [x[0] for x in SCHEMA_ELEMENT_CLASSES] SCHEMA_ELEMENT_CLASSES_KEYS = [x[0] for x in SCHEMA_ELEMENT_CLASSES]
conn = connect(ldapi=ldapi, conn = connect(ldapi=ldapi, realm=api.env.realm, fqdn=FQDN)
realm=api.env.realm,
fqdn=installutils.get_fqdn())
old_schema = conn.schema old_schema = conn.schema

View File

@ -31,7 +31,7 @@ from ipaplatform import services
from ipaplatform.paths import paths from ipaplatform.paths import paths
from ipaplatform.tasks import tasks from ipaplatform.tasks import tasks
from ipalib import api, errors, x509 from ipalib import api, errors, x509
from ipalib.constants import DOMAIN_LEVEL_0 from ipalib.constants import DOMAIN_LEVEL_0, FQDN
from ipalib.facts import is_ipa_configured, is_ipa_client_configured from ipalib.facts import is_ipa_configured, is_ipa_client_configured
from ipalib.util import ( from ipalib.util import (
validate_domain_name, validate_domain_name,
@ -44,7 +44,7 @@ from ipaserver.install import (
otpdinstance, custodiainstance, replication, service, otpdinstance, custodiainstance, replication, service,
sysupgrade, cainstance) sysupgrade, cainstance)
from ipaserver.install.installutils import ( from ipaserver.install.installutils import (
BadHostError, get_fqdn, get_server_ip_address, BadHostError, get_server_ip_address,
load_pkcs12, read_password, verify_fqdn, update_hosts_file, load_pkcs12, read_password, verify_fqdn, update_hosts_file,
validate_mask) validate_mask)
@ -493,7 +493,7 @@ def install_check(installer):
if options.host_name: if options.host_name:
host_default = options.host_name host_default = options.host_name
else: else:
host_default = get_fqdn() host_default = FQDN
if installer.interactive and not options.host_name: if installer.interactive and not options.host_name:
host_name = read_host_name(host_default) host_name = read_host_name(host_default)

View File

@ -22,7 +22,6 @@ from __future__ import absolute_import
import logging import logging
import sys import sys
import os import os
import socket
import time import time
import traceback import traceback
import tempfile import tempfile
@ -35,6 +34,7 @@ from ipapython import ipautil
from ipapython.dn import DN from ipapython.dn import DN
from ipapython import kerberos from ipapython import kerberos
from ipalib import api, errors, x509 from ipalib import api, errors, x509
from ipalib.constants import FQDN
from ipaplatform import services from ipaplatform import services
from ipaplatform.constants import User from ipaplatform.constants import User
from ipaplatform.paths import paths from ipaplatform.paths import paths
@ -291,7 +291,7 @@ class Service:
self.steps = [] self.steps = []
self.output_fd = sys.stdout self.output_fd = sys.stdout
self.fqdn = socket.gethostname() self.fqdn = FQDN
if sstore: if sstore:
self.sstore = sstore self.sstore = sstore

View File

@ -28,8 +28,8 @@ import pytest
from ipalib import api from ipalib import api
from ipalib import errors from ipalib import errors
from ipalib.constants import FQDN
from ipaserver.install.ldapupdate import LDAPUpdate, BadSyntax from ipaserver.install.ldapupdate import LDAPUpdate, BadSyntax
from ipaserver.install import installutils
from ipapython import ipaldap from ipapython import ipaldap
from ipaplatform.constants import constants as platformconstants from ipaplatform.constants import constants as platformconstants
from ipapython.dn import DN from ipapython.dn import DN
@ -56,7 +56,6 @@ class TestUpdate:
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
def update_setup(self, request): def update_setup(self, request):
fqdn = installutils.get_fqdn()
pwfile = api.env.dot_ipa + os.sep + ".dmpw" pwfile = api.env.dot_ipa + os.sep + ".dmpw"
if os.path.isfile(pwfile): if os.path.isfile(pwfile):
with open(pwfile, "r") as fp: with open(pwfile, "r") as fp:
@ -64,7 +63,7 @@ class TestUpdate:
else: else:
pytest.skip("No directory manager password") pytest.skip("No directory manager password")
self.updater = LDAPUpdate() self.updater = LDAPUpdate()
self.ld = ipaldap.LDAPClient.from_hostname_secure(fqdn) self.ld = ipaldap.LDAPClient.from_hostname_secure(FQDN)
self.ld.simple_bind(bind_dn=ipaldap.DIRMAN_DN, self.ld.simple_bind(bind_dn=ipaldap.DIRMAN_DN,
bind_password=self.dm_password) bind_password=self.dm_password)
self.testdir = os.path.abspath(os.path.dirname(__file__)) self.testdir = os.path.abspath(os.path.dirname(__file__))

View File

@ -1,17 +1,23 @@
NULL =
AUTOMAKE_OPTIONS = 1.7 subdir-objects AUTOMAKE_OPTIONS = 1.7 subdir-objects
AM_CPPFLAGS = $(CRYPTO_CFLAGS) $(KRB5_CFLAGS) $(LDAP_CFLAGS) $(PWQUALITY_CFLAGS) AM_CPPFLAGS = $(CRYPTO_CFLAGS) $(KRB5_CFLAGS) $(LDAP_CFLAGS) $(PWQUALITY_CFLAGS)
noinst_LTLIBRARIES = libutil.la noinst_LTLIBRARIES = libutil.la
libutil_la_SOURCES = ipa_krb5.c \ libutil_la_SOURCES = \
ipa_hostname.c \
ipa_hostname.h \
ipa_krb5.c \
ipa_krb5.h \ ipa_krb5.h \
ipa_mspac.h \ ipa_mspac.h \
ipa_ldap.c \ ipa_ldap.c \
ipa_ldap.h \ ipa_ldap.h \
ipa_pwd.c \ ipa_pwd.c \
ipa_pwd.h \ ipa_pwd.h \
ipa_pwd_ntlm.c ipa_pwd_ntlm.c \
$(NULL)
libutil_la_LIBADD = $(CRYPTO_LIBS) $(KRB5_LIBS) $(LDAP_LIBS) $(PWQUALITY_LIBS) libutil_la_LIBADD = $(CRYPTO_LIBS) $(KRB5_LIBS) $(LDAP_LIBS) $(PWQUALITY_LIBS)

111
util/ipa_hostname.c Normal file
View File

@ -0,0 +1,111 @@
/*
* Copyright (C) 2020 FreeIPA Contributors see COPYING for license
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <netdb.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "ipa_hostname.h"
int
ipa_gethostname(char *name)
{
int ret;
ret = gethostname(name, IPA_HOST_NAME_LEN - 1);
/* Make double sure it is terminated */
name[IPA_HOST_NAME_LEN - 1] = '\0';
return ret;
}
static int
_get_fqdn(char *fqdn)
{
char hostname[IPA_HOST_NAME_LEN];
char *canonname = NULL;
struct addrinfo hints;
struct addrinfo *ai = NULL;
int r;
r = ipa_gethostname(hostname);
if (r != 0) {
goto error;
}
memset(&hints, 0, sizeof(struct addrinfo));
/* use IPv4 or IPv6 */
hints.ai_family = AF_UNSPEC;
/* optimize, RAW and STREAM return same kind of information */
hints.ai_socktype = SOCK_DGRAM;
/* any protocol */
hints.ai_protocol = 0;
/* get canonical name
* only use IPv4/6 when at least one interface for proto is configured */
hints.ai_flags = AI_CANONNAME | AI_ADDRCONFIG;
r = getaddrinfo(hostname, NULL, &hints, &ai);
if (r != 0) {
/* getaddrinfo() for gethostname() should never fail. The
* nss-myhostname provider should always add a positive match. */
errno = ENOENT;
goto error;
}
/* only the first addrinfo struct holds a canonical name value */
canonname = ai->ai_canonname;
/* check that canon name is filled and not too long */
if (!canonname) {
errno = ENOENT;
goto error;
}
if (strlen(canonname) >= IPA_HOST_NAME_LEN) {
errno = ENAMETOOLONG;
goto error;
}
#if 0
/* refuse non-qualified short names and localhost */
if ((strchr(canonname, '.') == NULL) ||
(strcasecmp(canonname, "localhost.localdomain") == 0)) {
errno = EINVAL;
goto error;
}
#endif
strcpy(fqdn, canonname);
/* Make double sure it is terminated */
fqdn[IPA_HOST_NAME_LEN - 1] = '\0';
freeaddrinfo(ai);
return 0;
error:
fqdn[0] = '\0';
if (ai != NULL) {
freeaddrinfo(ai);
}
return -1;
}
int ipa_gethostfqdn(char *name)
{
static char cached_fqdn[IPA_HOST_NAME_LEN] = {0};
if (!cached_fqdn) {
int res = _get_fqdn(cached_fqdn);
if (res != 0) {
return -1;
}
}
strcpy(name, cached_fqdn);
return 0;
}

19
util/ipa_hostname.h Normal file
View File

@ -0,0 +1,19 @@
/*
* Copyright (C) 2020 FreeIPA Contributors see COPYING for license
*/
#include <limits.h>
#include <unistd.h>
/*
* host name length including NULL byte
*
* NOTE: length hardcoded in kernel
*/
#define IPA_HOST_NAME_LEN (HOST_NAME_MAX + 1)
int
ipa_gethostname(char *name);
int
ipa_gethostfqdn(char *name);