Add a prototype client tool to configure a client of the IPA server

Right now it does only discovery (or fallback)
This commit is contained in:
Simo Sorce
2007-08-16 18:00:16 -04:00
parent 0a6007bcf2
commit 0e419aa4bf
9 changed files with 967 additions and 2 deletions

View File

@@ -1,4 +1,4 @@
SUBDIRS=ipa-server ipa-admintools ipa-python SUBDIRS=ipa-server ipa-admintools ipa-python ipa-client
PRJ_PREFIX=freeipa PRJ_PREFIX=freeipa
@@ -31,6 +31,13 @@ PYTHON_VERSION=$(PYTHON_MAJOR).$(PYTHON_MINOR).$(PYTHON_RELEASE)
PYTHON_TARBALL_PREFIX=$(PRJ_PREFIX)-python-$(PYTHON_VERSION) PYTHON_TARBALL_PREFIX=$(PRJ_PREFIX)-python-$(PYTHON_VERSION)
PYTHON_TARBALL=$(PYTHON_TARBALL_PREFIX).tgz PYTHON_TARBALL=$(PYTHON_TARBALL_PREFIX).tgz
CLI_MAJOR=0
CLI_MINOR=1
CLI_RELEASE=0
CLI_VERSION=$(CLI_MAJOR).$(CLI_MINOR).$(CLI_RELEASE)
CLI_TARBALL_PREFIX=$(PRJ_PREFIX)-client-$(CLI_VERSION)
CLI_TARBALL=$(CLI_TARBALL_PREFIX).tgz
ifeq ($(DEBUG),1) ifeq ($(DEBUG),1)
export CFLAGS = -g -Wall -Wshadow export CFLAGS = -g -Wall -Wshadow
export LDFLAGS = -g export LDFLAGS = -g
@@ -63,6 +70,9 @@ version-update:
sed s/VERSION/$(PYTHON_VERSION)/ ipa-python/freeipa-python.spec.in \ sed s/VERSION/$(PYTHON_VERSION)/ ipa-python/freeipa-python.spec.in \
> ipa-python/freeipa-python.spec > ipa-python/freeipa-python.spec
sed s/VERSION/$(CLI_VERSION)/ ipa-client/freeipa-client.spec.in \
> ipa-client/freeipa-client.spec
archive: archive:
-mkdir -p dist -mkdir -p dist
@@ -96,6 +106,12 @@ tarballs:
cd dist; tar cfz $(PYTHON_TARBALL) $(PYTHON_TARBALL_PREFIX) cd dist; tar cfz $(PYTHON_TARBALL) $(PYTHON_TARBALL_PREFIX)
rm -fr dist/$(PYTHON_TARBALL_PREFIX) rm -fr dist/$(PYTHON_TARBALL_PREFIX)
# ipa-client
mv dist/freeipa/ipa-client dist/$(CLI_TARBALL_PREFIX)
rm -f dist/$(CLI_TARBALL)
cd dist; tar cfz $(CLI_TARBALL) $(CLI_TARBALL_PREFIX)
rm -fr dist/$(CLI_TARBALL_PREFIX)
rpmroot: rpmroot:
mkdir -p $(RPMBUILD)/BUILD mkdir -p $(RPMBUILD)/BUILD
mkdir -p $(RPMBUILD)/RPMS mkdir -p $(RPMBUILD)/RPMS
@@ -121,7 +137,13 @@ rpm-ipa-python:
cp rpmbuild/RPMS/noarch/$(PRJ_PREFIX)-python-$(PYTHON_VERSION)-*.rpm dist/. cp rpmbuild/RPMS/noarch/$(PRJ_PREFIX)-python-$(PYTHON_VERSION)-*.rpm dist/.
cp rpmbuild/SRPMS/$(PRJ_PREFIX)-python-$(PYTHON_VERSION)-*.src.rpm dist/. cp rpmbuild/SRPMS/$(PRJ_PREFIX)-python-$(PYTHON_VERSION)-*.src.rpm dist/.
rpms: rpmroot rpm-ipa-server rpm-ipa-admin rpm-ipa-python rpm-ipa-client:
cp dist/$(CLI_TARBALL) $(RPMBUILD)/SOURCES/.
rpmbuild --define "_topdir $(RPMBUILD)" -ba ipa-client/freeipa-client.spec
cp rpmbuild/RPMS/*/$(PRJ_PREFIX)-client-$(CLI_VERSION)-*.rpm dist/.
cp rpmbuild/SRPMS/$(PRJ_PREFIX)-client-$(CLI_VERSION)-*.src.rpm dist/.
rpms: rpmroot rpm-ipa-server rpm-ipa-admin rpm-ipa-python rpm-ipa-client
dist: version-update archive tarballs archive-cleanup rpms dist: version-update archive tarballs archive-cleanup rpms

23
ipa-client/Makefile Normal file
View File

@@ -0,0 +1,23 @@
SUBDIRS=ipa-install
PYTHONDIR=$(DESTDIR)/usr/share/ipa/ipaclient
all:
@for subdir in $(SUBDIRS); do \
(cd $$subdir && $(MAKE) $@) || exit 1; \
done
install-ipaclient:
-mkdir -p $(PYTHONDIR)
install -m 644 ipaclient/*.py $(PYTHONDIR)
install: install-ipaclient
@for subdir in $(SUBDIRS); do \
(cd $$subdir && $(MAKE) $@) || exit 1; \
done
clean:
@for subdir in $(SUBDIRS); do \
(cd $$subdir && $(MAKE) $@) || exit 1; \
done
rm -f *~
rm -f ipaclient/*~

47
ipa-client/freeipa-client.spec Executable file
View File

@@ -0,0 +1,47 @@
Name: freeipa-client
Version: 0.1.0
Release: 1%{?dist}
Summary: FreeIPA client
Group: System Environment/Base
License: GPL
URL: http://www.freeipa.org
Source0: %{name}-%{version}.tgz
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
Requires: python python-ldap freeipa-python
%description
FreeIPA is a server for identity, policy, and audit.
The client package provide install and configuration scripts for clients.
%prep
%setup -q
%build
make DESTDIR=%{buildroot}
%install
rm -rf %{buildroot}
mkdir -p %{buildroot}%{_sbindir}
make install DESTDIR=%{buildroot}
%clean
rm -rf %{buildroot}
%files
%defattr(-,root,root,-)
%{_sbindir}/ipa-client-install
%dir %{_usr}/share/ipa
%{_usr}/share/ipa/*
%changelog
* Thu Aug 16 2007 Simo Sorce <ssorce@redhat.com> - 0.1.0-1
- Initial rpm version

View File

@@ -0,0 +1,47 @@
Name: freeipa-client
Version: VERSION
Release: 1%{?dist}
Summary: FreeIPA client
Group: System Environment/Base
License: GPL
URL: http://www.freeipa.org
Source0: %{name}-%{version}.tgz
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
Requires: python python-ldap freeipa-python
%description
FreeIPA is a server for identity, policy, and audit.
The client package provide install and configuration scripts for clients.
%prep
%setup -q
%build
make DESTDIR=%{buildroot}
%install
rm -rf %{buildroot}
mkdir -p %{buildroot}%{_sbindir}
make install DESTDIR=%{buildroot}
%clean
rm -rf %{buildroot}
%files
%defattr(-,root,root,-)
%{_sbindir}/ipa-client-install
%dir %{_usr}/share/ipa
%{_usr}/share/ipa/*
%changelog
* Thu Aug 16 2007 Simo Sorce <ssorce@redhat.com> - 0.1.0-1
- Initial rpm version

View File

@@ -0,0 +1,10 @@
SBINDIR=$(DESTDIR)/usr/sbin
all: ;
install:
-mkdir $(SBINDIR)
install -m 755 ipa-client-install $(SBINDIR)
clean:
rm -f *~ *.pyc

View File

@@ -0,0 +1,109 @@
#! /usr/bin/python -E
# Authors: Simo Sorce <ssorce@redhat.com>
# Karl MacMillan <kmacmillan@mentalrootkit.com>
#
# Copyright (C) 2007 Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; version 2 only
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
VERSION = "%prog .1"
import sys
sys.path.append("/usr/share/ipa")
import socket
import logging
from optparse import OptionParser
import ipaclient.ipadiscovery
from ipaserver.util import run
def parse_options():
parser = OptionParser(version=VERSION)
parser.add_option("--domain", dest="domain", help="domain name")
parser.add_option("--server", dest="server", help="IPA server")
parser.add_option("--realm", dest="realm_name", help="realm name")
parser.add_option("-d", "--debug", dest="debug", action="store_true",
dest="debug", default=False, help="print debugging information")
parser.add_option("-u", "--unattended", dest="unattended",
help="unattended installation never prompts the user")
options, args = parser.parse_args()
return options
def logging_setup(options):
# Always log everything (i.e., DEBUG) to the log
# file.
logging.basicConfig(level=logging.DEBUG,
format='%(asctime)s %(levelname)s %(message)s',
filename='ipaclient-install.log',
filemode='w')
console = logging.StreamHandler()
# If the debug option is set, also log debug messages to the console
if options.debug:
console.setLevel(logging.DEBUG)
else:
# Otherwise, log critical and error messages
console.setLevel(logging.ERROR)
formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s')
console.setFormatter(formatter)
logging.getLogger('').addHandler(console)
def main():
options = parse_options()
logging_setup(options)
# Create the discovery instance
ds = ipaclient.ipadiscovery.IPADiscovery()
ret = ds.search()
if ret == -10:
print "Can't get the fully qualified name of this host"
print "Please check that the client is properly configured"
return ret
if ret == -1:
logging.debug("Domain not found")
if options.domain:
dom = options.domain
elif options.unattended:
return ret
else:
print "Failed to determine your DNS domain (DNS misconfigured?)"
dom = raw_input("Please provide your domain name (ex: example.com):")
ret = ds.search(domain=dom)
if ret == -2:
logging.debug("IPA Server not found")
if options.server:
srv = options.server
elif options.unattended:
return ret
else:
print "Failed to find the IPA Server (DNS misconfigured?)"
srv = raw_input("Please provide your server name (ex: ipa.example.com):")
ret = ds.search(domain=dom, server=srv)
if ret != 0:
print "Failed to verify that "+srv+" is an IPA Server, aborting!"
return ret
print "Discovery was successful!"
print "Realm: "+ds.getRealmName()
print "DNS Domain: "+ds.getDomainName()
print "IPA Server: "+ds.getServerName()
return 0
main()

View File

@@ -0,0 +1,23 @@
#! /usr/bin/python -E
# Authors: Karl MacMillan <kmacmillan@mentalrootkit.com>
# see inline
#
# Copyright (C) 2007 Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; version 2 or later
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
__all__ = ["ipadiscovery"]

View File

@@ -0,0 +1,445 @@
#
# Copyright 2001, 2005 Red Hat, Inc.
#
# This is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
import struct
import socket
import sys
import acutil
DNS_C_IN = 1
DNS_C_CS = 2
DNS_C_CHAOS = 3
DNS_C_HS = 4
DNS_C_ANY = 255
DNS_T_A = 1
DNS_T_NS = 2
DNS_T_CNAME = 5
DNS_T_SOA = 6
DNS_T_NULL = 10
DNS_T_WKS = 11
DNS_T_PTR = 12
DNS_T_HINFO = 13
DNS_T_MX = 15
DNS_T_TXT = 16
DNS_T_SRV = 33
DNS_T_ANY = 255
DEBUG_DNSCLIENT = False
class DNSQueryHeader:
FORMAT = "!HBBHHHH"
def __init__(self):
self.dns_id = 0
self.dns_rd = 0
self.dns_tc = 0
self.dns_aa = 0
self.dns_opcode = 0
self.dns_qr = 0
self.dns_rcode = 0
self.dns_z = 0
self.dns_ra = 0
self.dns_qdcount = 0
self.dns_ancount = 0
self.dns_nscount = 0
self.dns_arcount = 0
def pack(self):
return struct.pack(DNSQueryHeader.FORMAT,
self.dns_id,
(self.dns_rd & 1) |
(self.dns_tc & 1) << 1 |
(self.dns_aa & 1) << 2 |
(self.dns_opcode & 15) << 3 |
(self.dns_qr & 1) << 7,
(self.dns_rcode & 15) |
(self.dns_z & 7) << 4 |
(self.dns_ra & 1) << 7,
self.dns_qdcount,
self.dns_ancount,
self.dns_nscount,
self.dns_arcount)
def unpack(self, data):
(self.dns_id, byte1, byte2, self.dns_qdcount, self.dns_ancount,
self.dns_nscount, self.dns_arcount) = struct.unpack(DNSQueryHeader.FORMAT, data[0:self.size()])
self.dns_rd = byte1 & 1
self.dns_tc = (byte1 >> 1) & 1
self.dns_aa = (byte1 >> 2) & 1
self.dns_opcode = (byte1 >> 3) & 15
self.dns_qr = (byte1 >> 7) & 1
self.dns_rcode = byte2 & 15
self.dns_z = (byte2 >> 4) & 7
self.dns_ra = (byte1 >> 7) & 1
def size(self):
return struct.calcsize(DNSQueryHeader.FORMAT)
def unpackQueryHeader(data):
header = DNSQueryHeader()
header.unpack(data)
return header
class DNSResult:
FORMAT = "!HHIH"
QFORMAT = "!HH"
def __init__(self):
self.dns_name = ""
self.dns_type = 0
self.dns_class = 0
self.dns_ttl = 0
self.dns_rlength = 0
self.rdata = None
def unpack(self, data):
(self.dns_type, self.dns_class, self.dns_ttl,
self.dns_rlength) = struct.unpack(DNSResult.FORMAT, data[0:self.size()])
def qunpack(self, data):
(self.dns_type, self.dns_class) = struct.unpack(DNSResult.QFORMAT, data[0:self.qsize()])
def size(self):
return struct.calcsize(DNSResult.FORMAT)
def qsize(self):
return struct.calcsize(DNSResult.QFORMAT)
class DNSRData:
def __init__(self):
pass
#typedef struct dns_rr_a {
# u_int32_t address;
#} dns_rr_a_t;
#
#typedef struct dns_rr_cname {
# const char *cname;
#} dns_rr_cname_t;
#
#typedef struct dns_rr_hinfo {
# const char *cpu, *os;
#} dns_rr_hinfo_t;
#
#typedef struct dns_rr_mx {
# u_int16_t preference;
# const char *exchange;
#} dns_rr_mx_t;
#
#typedef struct dns_rr_null {
# unsigned const char *data;
#} dns_rr_null_t;
#
#typedef struct dns_rr_ns {
# const char *nsdname;
#} dns_rr_ns_t;
#
#typedef struct dns_rr_ptr {
# const char *ptrdname;
#} dns_rr_ptr_t;
#
#typedef struct dns_rr_soa {
# const char *mname;
# const char *rname;
# u_int32_t serial;
# int32_t refresh;
# int32_t retry;
# int32_t expire;
# int32_t minimum;
#} dns_rr_soa_t;
#
#typedef struct dns_rr_txt {
# const char *data;
#} dns_rr_txt_t;
#
#typedef struct dns_rr_srv {
# const char *server;
# u_int16_t priority;
# u_int16_t weight;
# u_int16_t port;
#} dns_rr_srv_t;
def dnsNameToLabel(name):
out = ""
name = name.split(".")
for part in name:
out += chr(len(part)) + part
return out
def dnsFormatQuery(query, qclass, qtype):
header = DNSQueryHeader()
header.dns_id = 0 # FIXME: id = 0
header.dns_rd = 1 # don't know why the original code didn't request recursion for non SOA requests
header.dns_qr = 0 # query
header.dns_opcode = 0 # standard query
header.dns_qdcount = 1 # single query
qlabel = dnsNameToLabel(query)
if not qlabel:
return ""
out = header.pack() + qlabel
out += chr(qtype >> 8)
out += chr(qtype & 0xff)
out += chr(qclass >> 8)
out += chr(qclass & 0xff)
return out
def dnsParseLabel(label, base):
# returns (output, rest)
if not label:
return ("", None)
update = 1
rest = label
output = ""
skip = 0
try:
while ord(rest[0]):
if ord(rest[0]) & 0xc0:
rest = base[((ord(rest[0]) & 0x3f) << 8) + ord(rest[1]):]
if update:
skip += 2
update = 0
continue
output += rest[1:ord(rest[0]) + 1] + "."
if update:
skip += ord(rest[0]) + 1
rest = rest[ord(rest[0]) + 1:]
except IndexError:
return ("", None)
return (label[skip+update:], output)
def dnsParseA(data, base):
rdata = DNSRData()
if len(data) < 4:
rdata.address = 0
return None
rdata.address = (ord(data[0])<<24) | (ord(data[1])<<16) | (ord(data[2])<<8) | (ord(data[3])<<0)
if DEBUG_DNSCLIENT:
print "A = %d.%d.%d.%d." % (ord(data[0]), ord(data[1]), ord(data[2]), ord(data[3]))
return rdata
def dnsParseText(data):
if len(data) < 1:
return ("", None)
tlen = ord(data[0])
if len(data) < tlen + 1:
return ("", None)
return (data[tlen+1:], data[1:tlen+1])
def dnsParseNS(data, base):
rdata = DNSRData()
(rest, rdata.nsdname) = dnsParseLabel(data, base)
if DEBUG_DNSCLIENT:
print "NS DNAME = \"%s\"." % (rdata.nsdname)
return rdata
def dnsParseCNAME(data, base):
rdata = DNSRData()
(rest, rdata.cname) = dnsParseLabel(data, base)
if DEBUG_DNSCLIENT:
print "CNAME = \"%s\"." % (rdata.cname)
return rdata
def dnsParseSOA(data, base):
rdata = DNSRData()
format = "!IIIII"
(rest, rdata.mname) = dnsParseLabel(data, base)
if rdata.mname is None:
return None
(rest, rdata.rname) = dnsParseLabel(rest, base)
if rdata.rname is None:
return None
if len(rest) < struct.calcsize(format):
return None
(rdata.serial, rdata.refresh, rdata.retry, rdata.expire,
rdata.minimum) = struct.unpack(format, rest[:struct.calcsize(format)])
if DEBUG_DNSCLIENT:
print "SOA(mname) = \"%s\"." % rdata.mname
print "SOA(rname) = \"%s\"." % rdata.rname
print "SOA(serial) = %d." % rdata.serial
print "SOA(refresh) = %d." % rdata.refresh
print "SOA(retry) = %d." % rdata.retry
print "SOA(expire) = %d." % rdata.expire
print "SOA(minimum) = %d." % rdata.minimum
return rdata
def dnsParseNULL(data, base):
# um, yeah
return None
def dnsParseWKS(data, base):
return None
def dnsParseHINFO(data, base):
rdata = DNSRData()
(rest, rdata.cpu) = dnsParseText(data)
if rest:
(rest, rdata.os) = dnsParseText(rest)
if DEBUG_DNSCLIENT:
print "HINFO(cpu) = \"%s\"." % rdata.cpu
print "HINFO(os) = \"%s\"." % rdata.os
return rdata
def dnsParseMX(data, base):
rdata = DNSRData()
if len(data) < 2:
return None
rdata.preference = (ord(data[0]) << 8) | ord(data[1])
(rest, rdata.exchange) = dnsParseLabel(data[2:], base)
if DEBUG_DNSCLIENT:
print "MX(exchanger) = \"%s\"." % rdata.exchange
print "MX(preference) = %d." % rdata.preference
return rdata
def dnsParseTXT(data, base):
rdata = DNSRData()
(rest, rdata.data) = dnsParseText(data)
if DEBUG_DNSCLIENT:
print "TXT = \"%s\"." % rdata.data
return rdata
def dnsParsePTR(data, base):
rdata = DNSRData()
(rest, rdata.ptrdname) = dnsParseLabel(data, base)
if DEBUG_DNSCLIENT:
print "PTR = \"%s\"." % rdata.ptrdname
def dnsParseSRV(data, base):
rdata = DNSRData()
format = "!HHH"
flen = struct.calcsize(format)
if len(data) < flen:
return None
(rdata.priority, rdata.weight, rdata.port) = struct.unpack(format, data[:flen])
(rest, rdata.server) = dnsParseLabel(data[flen:], base)
if DEBUG_DNSCLIENT:
print "SRV(server) = \"%s\"." % rdata.server
print "SRV(weight) = %d." % rdata.weight
print "SRV(priority) = %d." % rdata.priority
print "SRV(port) = %d." % rdata.port
return rdata
def dnsParseResults(results):
try:
header = unpackQueryHeader(results)
except struct.error:
return []
if header.dns_qr != 1: # should be a response
return []
if header.dns_rcode != 0: # should be no error
return []
rest = results[header.size():]
rrlist = []
for i in xrange(header.dns_qdcount):
if not rest:
return []
rr = DNSResult()
(rest, label) = dnsParseLabel(rest, results)
if label is None:
return []
if len(rest) < rr.qsize():
return []
rr.qunpack(rest)
rest = rest[rr.qsize():]
if DEBUG_DNSCLIENT:
print "Queried for '%s', class = %d, type = %d." % (label,
rr.dns_class, rr.dns_type)
for i in xrange(header.dns_ancount + header.dns_nscount + header.dns_arcount):
(rest, label) = dnsParseLabel(rest, results)
if label is None:
return []
rr = DNSResult()
rr.dns_name = label
if len(rest) < rr.size():
return []
rr.unpack(rest)
rest = rest[rr.size():]
if DEBUG_DNSCLIENT:
print "Answer %d for '%s', class = %d, type = %d, ttl = %d." % (i,
rr.dns_name, rr.dns_class, rr.dns_type,
rr.dns_ttl)
if len(rest) < rr.dns_rlength:
if DEBUG_DNSCLIENT:
print "Answer too short."
return []
fmap = { DNS_T_A: dnsParseA, DNS_T_NS: dnsParseNS,
DNS_T_CNAME: dnsParseCNAME, DNS_T_SOA: dnsParseSOA,
DNS_T_NULL: dnsParseNULL, DNS_T_WKS: dnsParseWKS,
DNS_T_PTR: dnsParsePTR, DNS_T_HINFO: dnsParseHINFO,
DNS_T_MX: dnsParseMX, DNS_T_TXT: dnsParseTXT,
DNS_T_SRV: dnsParseSRV}
if not rr.dns_type in fmap:
if DEBUG_DNSCLIENT:
print "Don't know how to parse RR type %d!" % rr.dns_type
else:
rr.rdata = fmap[rr.dns_type](rest[:rr.dns_rlength], results)
rest = rest[rr.dns_rlength:]
rrlist += [rr]
if not rrlist:
rrlist = [rr]
return rrlist
def query(query, qclass, qtype):
qdata = dnsFormatQuery(query, qclass, qtype)
if not qdata:
return []
answer = acutil.res_send(qdata)
if not answer:
return []
return dnsParseResults(answer)
if __name__ == '__main__':
DEBUG_DNSCLIENT = True
print "Sending query."
rr = query(len(sys.argv) > 1 and sys.argv[1] or "devserv.devel.redhat.com.",
DNS_C_IN, DNS_T_ANY)
sys.exit(0)

View File

@@ -0,0 +1,239 @@
#! /usr/bin/python -E
# Authors: Simo Sorce <ssorce@redhat.com>
#
# Copyright (C) 2007 Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; version 2 or later
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
import socket
import logging
import dnsclient
import ldap
from ldap import LDAPError
class IPADiscovery:
def __init__(self):
self.realm = None
self.domain = None
self.server = None
def getServerName(self):
return str(self.server)
def getDomainName(self):
return str(self.domain)
def getRealmName(self):
return str(self.realm)
def search(self, domain = "", server = ""):
hostname = ""
qname = ""
results = []
result = []
krbret = []
ldapret = []
if not server:
if not domain: #domain not provided do full DNS discovery
# get the local host name
hostname = socket.getfqdn()
if not hostname:
return -10 #bad host configuration
# first, check for an LDAP server for the local domain
p = hostname.find(".")
if p == -1: #no domain name
return -1
domain = hostname[p+1:]
while not self.server:
logging.debug("[ipadnssearchldap("+domain+")]")
self.server = self.ipadnssearchldap(domain)
if self.server:
self.domain = domain
else:
p = domain.find(".")
if p == -1: #no ldap server found and last component of the domain already tested
return -1
domain = domain[p+1:]
else:
logging.debug("[ipadnssearchldap]")
self.server = self.ipadnssearchldap(domain)
if self.server:
self.domain = domain
else:
return -2 #no ldap server found
#search for kerberos TODO: move this after ipacheckldap()
logging.debug("[ipadnssearchkrb]")
krbret = self.ipadnssearchkrb(self.domain)
if not krbret:
return -3 #no krb server found
self.realm = krbret[0]
else: #server forced on us, this means DNS doesn't work :/
self.domain = domain
self.server = server
logging.debug("[ipacheckldap]")
# check ldap now
ldapret = self.ipacheckldap(self.server, self.realm);
if not ldapret:
return -4 # not an IPA server (or broken config)
self.server = ldapret[0]
self.realm = ldapret[1]
return 0
def ipacheckldap(self, thost, trealm):
lret = []
lres = []
lattr = ""
linfo = ""
lrealms = []
i = 0
#now verify the server is really an IPA server
try:
logging.debug("Init ldap with: ldap://"+thost+":389")
lh = ldap.initialize("ldap://"+thost+":389")
lh.simple_bind_s("","")
logging.debug("Search rootdse")
lret = lh.search_s("", ldap.SCOPE_BASE, "(objectClass=*)")
for lattr in lret[0][1]:
if lattr.lower() == "namingcontexts":
lbase = lret[0][1][lattr][0]
logging.debug("Search for (info=*) in "+lbase+"(base)")
lret = lh.search_s(lbase, ldap.SCOPE_BASE, "(info=IPA*)")
if not lret:
return []
logging.debug("Found: "+str(lret))
for lattr in lret[0][1]:
if lattr.lower() == "info":
linfo = lret[0][1][lattr][0].lower()
break
if not linfo:
return []
#search and return known realms
logging.debug("Search for (objectClass=krbRealmContainer) in "+lbase+"(sub)")
lret = lh.search_s("cn=kerberos,"+lbase, ldap.SCOPE_SUBTREE, "(objectClass=krbRealmContainer)")
if not lret:
#something very wrong
return []
logging.debug("Found: "+str(lret))
for lres in lret:
for lattr in lres[1]:
if lattr.lower() == "cn":
lrealms.append(lres[1][lattr][0])
if trealm:
for r in lrealms:
if trealm == r:
return [thost, trealm]
# must match or something is very wrong
return []
else:
if len(lrealms) != 1:
#which one? we can't attach to a multi-realm server without DNS working
return []
else:
return [thost, lrealms[0]]
#we shouldn't get here
return []
except LDAPError, err:
#no good
logging.error("Ldap Error: "+str(err))
return []
def ipadnssearchldap(self, tdomain):
servers = ""
rserver = ""
qname = "_ldap._tcp."+tdomain
# terminate the name
if not qname.endswith("."):
qname += "."
results = dnsclient.query(qname, dnsclient.DNS_C_IN, dnsclient.DNS_T_SRV)
for result in results:
if result.dns_type == dnsclient.DNS_T_SRV:
rserver = result.rdata.server.rstrip(".")
if result.rdata.port and result.rdata.port != 389:
rserver += ":" + str(result.rdata.port)
if servers:
servers += "," + rserver
else:
servers = rserver
break
return servers
def ipadnssearchkrb(self, tdomain):
realm = ""
kdc = ""
# now, check for a Kerberos realm the local host or domain is in
qname = "_kerberos." + tdomain
# terminate the name
if not qname.endswith("."):
qname += "."
results = dnsclient.query(qname, dnsclient.DNS_C_IN, dnsclient.DNS_T_TXT)
for result in results:
if result.dns_type == dnsclient.DNS_T_TXT:
realm = result.rdata.data
if realm:
break
if realm:
# now fetch server information for the realm
qname = "_kerberos._udp." + tdomain
# terminate the name
if not qname.endswith("."):
qname += "."
results = dnsclient.query(qname, dnsclient.DNS_C_IN, dnsclient.DNS_T_SRV)
for result in results:
if result.dns_type == dnsclient.DNS_T_SRV:
qname = result.rdata.server.rstrip(".")
if result.rdata.port and result.rdata.port != 88:
qname += ":" + str(result.rdata.port)
if kdc:
kdc += "," + qname
else:
kdc = qname
print "["+realm+", "+kdc+"]"
return [realm, kdc]