From a5617680bac3b34dbd5b4654b11f35cec180e72d Mon Sep 17 00:00:00 2001
From: Simo Sorce
Date: Mon, 10 Dec 2007 16:31:21 -0500
Subject: Move dnsclient into ipa-python so that I will be able to use it in
ipaconfig
---
ipa-client/ipaclient/Makefile.am | 1 -
ipa-client/ipaclient/__init__.py | 2 +-
ipa-client/ipaclient/dnsclient.py | 445 -----------------------------------
ipa-client/ipaclient/ipadiscovery.py | 14 +-
ipa-python/MANIFEST.in | 2 +-
ipa-python/dnsclient.py | 445 +++++++++++++++++++++++++++++++++++
6 files changed, 454 insertions(+), 455 deletions(-)
delete mode 100644 ipa-client/ipaclient/dnsclient.py
create mode 100644 ipa-python/dnsclient.py
diff --git a/ipa-client/ipaclient/Makefile.am b/ipa-client/ipaclient/Makefile.am
index 038238926..290ffef8d 100644
--- a/ipa-client/ipaclient/Makefile.am
+++ b/ipa-client/ipaclient/Makefile.am
@@ -3,7 +3,6 @@ NULL =
appdir = $(IPA_DATA_DIR)/ipaclient
app_PYTHON = \
__init__.py \
- dnsclient.py \
ipachangeconf.py \
ipadiscovery.py \
ntpconf.py \
diff --git a/ipa-client/ipaclient/__init__.py b/ipa-client/ipaclient/__init__.py
index c07a549a5..ba14d3c4c 100644
--- a/ipa-client/ipaclient/__init__.py
+++ b/ipa-client/ipaclient/__init__.py
@@ -18,5 +18,5 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-__all__ = ["ipadiscovery", "ipachangeconf", "dnsclient"]
+__all__ = ["ipadiscovery", "ipachangeconf"]
diff --git a/ipa-client/ipaclient/dnsclient.py b/ipa-client/ipaclient/dnsclient.py
deleted file mode 100644
index bc8a229cd..000000000
--- a/ipa-client/ipaclient/dnsclient.py
+++ /dev/null
@@ -1,445 +0,0 @@
-#
-# 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)
diff --git a/ipa-client/ipaclient/ipadiscovery.py b/ipa-client/ipaclient/ipadiscovery.py
index 6f44ffd1b..939d41056 100644
--- a/ipa-client/ipaclient/ipadiscovery.py
+++ b/ipa-client/ipaclient/ipadiscovery.py
@@ -20,7 +20,7 @@
import socket
import logging
-import dnsclient
+import ipa.dnsclient
import ldap
from ldap import LDAPError
@@ -191,10 +191,10 @@ class IPADiscovery:
# terminate the name
if not qname.endswith("."):
qname += "."
- results = dnsclient.query(qname, dnsclient.DNS_C_IN, dnsclient.DNS_T_SRV)
+ results = ipa.dnsclient.query(qname, ipa.dnsclient.DNS_C_IN, ipa.dnsclient.DNS_T_SRV)
for result in results:
- if result.dns_type == dnsclient.DNS_T_SRV:
+ if result.dns_type == ipa.dnsclient.DNS_T_SRV:
rserver = result.rdata.server.rstrip(".")
if result.rdata.port and result.rdata.port != 389:
rserver += ":" + str(result.rdata.port)
@@ -214,10 +214,10 @@ class IPADiscovery:
# terminate the name
if not qname.endswith("."):
qname += "."
- results = dnsclient.query(qname, dnsclient.DNS_C_IN, dnsclient.DNS_T_TXT)
+ results = ipa.dnsclient.query(qname, ipa.dnsclient.DNS_C_IN, ipa.dnsclient.DNS_T_TXT)
for result in results:
- if result.dns_type == dnsclient.DNS_T_TXT:
+ if result.dns_type == ipa.dnsclient.DNS_T_TXT:
realm = result.rdata.data
if realm:
break
@@ -228,9 +228,9 @@ class IPADiscovery:
# terminate the name
if not qname.endswith("."):
qname += "."
- results = dnsclient.query(qname, dnsclient.DNS_C_IN, dnsclient.DNS_T_SRV)
+ results = ipa.dnsclient.query(qname, ipa.dnsclient.DNS_C_IN, ipa.dnsclient.DNS_T_SRV)
for result in results:
- if result.dns_type == dnsclient.DNS_T_SRV:
+ if result.dns_type == ipa.dnsclient.DNS_T_SRV:
qname = result.rdata.server.rstrip(".")
if result.rdata.port and result.rdata.port != 88:
qname += ":" + str(result.rdata.port)
diff --git a/ipa-python/MANIFEST.in b/ipa-python/MANIFEST.in
index 49f2126a6..e2cad6f22 100644
--- a/ipa-python/MANIFEST.in
+++ b/ipa-python/MANIFEST.in
@@ -1,3 +1,3 @@
include *.conf
-include freeipa-python.spec*
+include ipa-python.spec*
diff --git a/ipa-python/dnsclient.py b/ipa-python/dnsclient.py
new file mode 100644
index 000000000..bc8a229cd
--- /dev/null
+++ b/ipa-python/dnsclient.py
@@ -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)
--
cgit
From 463a0462d3bbd5491ab70befdb0e559f8dd7872a Mon Sep 17 00:00:00 2001
From: Simo Sorce
Date: Tue, 11 Dec 2007 10:58:39 -0500
Subject: Make admintools discover the domain using DNS calls to find the LDAP
server.
---
ipa-python/config.py | 56 +++++++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 51 insertions(+), 5 deletions(-)
diff --git a/ipa-python/config.py b/ipa-python/config.py
index a17e585bc..c1a3915d0 100644
--- a/ipa-python/config.py
+++ b/ipa-python/config.py
@@ -20,6 +20,10 @@
import ConfigParser
from optparse import OptionParser
+import krbV
+import socket
+import ipa.dnsclient
+
class IPAConfigError(Exception):
def __init__(self, msg=''):
self.msg = msg
@@ -55,11 +59,51 @@ def __parse_config():
p.read("/etc/ipa/ipa.conf")
try:
- config.default_realm = p.get("defaults", "realm")
- config.default_server = p.get("defaults", "server")
+ if not config.default_realm:
+ config.default_realm = p.get("defaults", "realm")
+ if not config.default_server:
+ config.default_server = p.get("defaults", "server")
except:
pass
+def __discover_config():
+ try:
+ if not config.default_realm:
+ krbctx = krbV.default_context()
+ config.default_realm = krbctx.default_realm
+ if not config.default_realm:
+ return False
+
+ if not config.default_server:
+ #try once with REALM -> domain
+ name = "_ldap._tcp."+config.default_realm+"."
+ rs = ipa.dnsclient.query(name, ipa.dnsclient.DNS_C_IN, ipa.dnsclient.DNS_T_SRV)
+ rl = len(rs)
+
+ #try cycling on domain components of FQDN
+ if rl == 0:
+ name = socket.getfqdn()
+ while rl == 0:
+ tok = name.find(".")
+ if tok == -1:
+ return False
+ name = name[tok+1:]
+ q = "_ldap._tcp." + name + "."
+ rs = ipa.dnsclient.query(q, ipa.dnsclient.DNS_C_IN, ipa.dnsclient.DNS_T_SRV)
+ rl = len(rs)
+
+ for r in rs:
+ if r.dns_type == ipa.dnsclient.DNS_T_SRV:
+ rsrv = r.rdata.server.rstrip(".")
+ # we take only the first one returned for now
+ config.default_server = rsrv
+ return True
+
+ #if none found
+ return False
+ except:
+ return False
+
def usage():
return """ --realm\tset the IPA realm
--server\tset the IPA server
@@ -92,15 +136,17 @@ def __parse_args(args):
def init_config(args=None):
- __parse_config()
out_args = None
if args:
out_args = __parse_args(args)
+ __discover_config()
+ __parse_config()
+
if not config.default_realm:
- raise IPAConfigError("realm not specified in config file or on command line")
+ raise IPAConfigError("realm not found, nor specified in config file or on command line")
if not config.default_server:
- raise IPAConfigError("server not specified in config file or on command line")
+ raise IPAConfigError("server not found, nor specified in config file or on command line")
if out_args:
return out_args
--
cgit
From 06140245660d4d68a155796418e80867ce853be4 Mon Sep 17 00:00:00 2001
From: Simo Sorce
Date: Tue, 11 Dec 2007 10:59:07 -0500
Subject: Minor fix
---
ipa-server/ipa-kpasswd/ipa_kpasswd.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ipa-server/ipa-kpasswd/ipa_kpasswd.c b/ipa-server/ipa-kpasswd/ipa_kpasswd.c
index 99dfe678f..ccd17c1f0 100644
--- a/ipa-server/ipa-kpasswd/ipa_kpasswd.c
+++ b/ipa-server/ipa-kpasswd/ipa_kpasswd.c
@@ -306,7 +306,7 @@ int ldap_pwd_change(char *client_name, char *realm_name, krb5_data pwd, char **e
LDAPControl **srvctrl = NULL;
char *exterr1 = NULL;
char *exterr2 = NULL;
- char *err;
+ char *err = NULL;
int msgid;
int ret, rc;
--
cgit
From 4f0b2154146cc3ed3b32b34713089323d96c1c74 Mon Sep 17 00:00:00 2001
From: Simo Sorce
Date: Tue, 11 Dec 2007 12:25:58 -0500
Subject: Make sure we don't keep around old keys. Fixes problem changing
passwords seen only on servers where re-installations where performed (and
old secrets piled up)
---
ipa-server/ipaserver/krbinstance.py | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/ipa-server/ipaserver/krbinstance.py b/ipa-server/ipaserver/krbinstance.py
index ede008a83..76818af7d 100644
--- a/ipa-server/ipaserver/krbinstance.py
+++ b/ipa-server/ipaserver/krbinstance.py
@@ -383,6 +383,11 @@ class KrbInstance(service.Service):
def __export_kadmin_changepw_keytab(self):
self.step("exporting the kadmin keytab")
+ try:
+ if file_exists("/var/kerberos/krb5kdc/kpasswd.keytab"):
+ os.remove("/var/kerberos/krb5kdc/kpasswd.keytab")
+ except os.error:
+ logging.critical("Failed to remove /var/kerberos/krb5kdc/kpasswd.keytab.")
(kwrite, kread, kerr) = os.popen3("/usr/kerberos/sbin/kadmin.local")
kwrite.write("modprinc +requires_preauth kadmin/changepw\n")
kwrite.flush()
--
cgit
From 10ac6c3c6107cd4deaae922bb681588a0ff410a2 Mon Sep 17 00:00:00 2001
From: Rob Crittenden
Date: Tue, 11 Dec 2007 15:06:01 -0500
Subject: Enable searching for multiple things at once
---
ipa-server/xmlrpc-server/funcs.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ipa-server/xmlrpc-server/funcs.py b/ipa-server/xmlrpc-server/funcs.py
index 4943da24d..528b138ba 100644
--- a/ipa-server/xmlrpc-server/funcs.py
+++ b/ipa-server/xmlrpc-server/funcs.py
@@ -316,7 +316,7 @@ class IPAServer:
# construct the giant match for all words
exact_match_filter = "(&"
- partial_match_filter = "(&"
+ partial_match_filter = "(|"
for word in criteria_words:
exact_match_filter += gen_search_pattern(word)
partial_match_filter += gen_search_pattern("*%s*" % word)
--
cgit
From 5c217ce31a4b8c26941c86644b33b904734f61b8 Mon Sep 17 00:00:00 2001
From: Rob Crittenden
Date: Tue, 11 Dec 2007 15:14:29 -0500
Subject: Change from "Common Name" to "Full Name"
---
ipa-server/ipa-gui/ipagui/forms/user.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/ipa-server/ipa-gui/ipagui/forms/user.py b/ipa-server/ipa-gui/ipagui/forms/user.py
index 74369a6ae..43a7a5477 100644
--- a/ipa-server/ipa-gui/ipagui/forms/user.py
+++ b/ipa-server/ipa-gui/ipagui/forms/user.py
@@ -5,8 +5,8 @@ from tg_expanding_form_widget.tg_expanding_form_widget import ExpandingForm
class UserFields(object):
givenname = widgets.TextField(name="givenname", label="First Name")
sn = widgets.TextField(name="sn", label="Last Name")
- cn = widgets.TextField(name="cn", label="Common Names")
- cns = ExpandingForm(name="cns", label="Common Names", fields=[cn])
+ cn = widgets.TextField(name="cn", label="Full Name")
+ cns = ExpandingForm(name="cns", label="Full Name", fields=[cn])
title = widgets.TextField(name="title", label="Title")
displayname = widgets.TextField(name="displayname", label="Display Name")
initials = widgets.TextField(name="initials", label="Initials")
--
cgit
From 23ffab533fde03171b023d50ba72b53d0ab8f1d7 Mon Sep 17 00:00:00 2001
From: Rob Crittenden
Date: Tue, 11 Dec 2007 17:34:15 -0500
Subject: Make the old entry option in update_*, check for empty parameters and
fix some problems reported by pychecker.
---
ipa-python/ipaclient.py | 4 -
ipa-python/ipaerror.py | 6 +-
ipa-python/rpcclient.py | 35 ++---
ipa-server/xmlrpc-server/funcs.py | 280 ++++++++++++++++++++++++++++++--------
4 files changed, 233 insertions(+), 92 deletions(-)
diff --git a/ipa-python/ipaclient.py b/ipa-python/ipaclient.py
index d815afa7b..bd1fb235a 100644
--- a/ipa-python/ipaclient.py
+++ b/ipa-python/ipaclient.py
@@ -19,14 +19,10 @@
#!/usr/bin/python
-import sys
-
import ipa.rpcclient as rpcclient
import entity
import user
import group
-import ipa
-import config
import radius_util
class IPAClient:
diff --git a/ipa-python/ipaerror.py b/ipa-python/ipaerror.py
index e34963365..59e262018 100644
--- a/ipa-python/ipaerror.py
+++ b/ipa-python/ipaerror.py
@@ -129,14 +129,14 @@ LDAP_NO_CONFIG = gen_error_code(
"IPA configuration not found")
#
-# Input errors (sample - replace me)
+# Function input errors
#
INPUT_CATEGORY = 0x0002
-INPUT_INVALID_ERROR = gen_error_code(
+INPUT_INVALID_PARAMETER = gen_error_code(
INPUT_CATEGORY,
0x0001,
- "Illegal input")
+ "Invalid parameter(s)")
#
# Connection errors
diff --git a/ipa-python/rpcclient.py b/ipa-python/rpcclient.py
index 5656b99d9..c993ac991 100644
--- a/ipa-python/rpcclient.py
+++ b/ipa-python/rpcclient.py
@@ -24,11 +24,8 @@ import socket
import config
from krbtransport import KerbTransport
from kerberos import GSSError
-import os
-import base64
-import user
-import ipa
from ipa import ipaerror, ipautil
+from ipa import config
# Some errors to catch
# http://cvs.fedora.redhat.com/viewcvs/ldapserver/ldap/servers/plugins/pam_passthru/README?root=dirsec&rev=1.6&view=auto
@@ -36,7 +33,7 @@ from ipa import ipaerror, ipautil
class RPCClient:
def __init__(self):
- ipa.config.init_config()
+ config.init_config()
def server_url(self):
"""Build the XML-RPC server URL from our configuration"""
@@ -47,25 +44,6 @@ class RPCClient:
authentication"""
return xmlrpclib.ServerProxy(self.server_url(), KerbTransport())
- def convert_entry(self,ent):
- # Convert into a dict. We need to handle multi-valued attributes as well
- # so we'll convert those into lists.
- obj={}
- for (k) in ent:
- k = k.lower()
- if obj.get(k) is not None:
- if isinstance(obj[k],list):
- obj[k].append(ent[k].strip())
- else:
- first = obj[k]
- obj[k] = ()
- obj[k].append(first)
- obj[k].append(ent[k].strip())
- else:
- obj[k] = ent[k]
-
- return obj
-
# Higher-level API
def get_aci_entry(self, sattrs=None):
@@ -168,7 +146,8 @@ class RPCClient:
def get_user_by_email(self,email,sattrs=None):
"""Get a specific user's entry. Return as a dict of values.
- Multi-valued fields are represented as lists.
+ Multi-valued fields are represented as lists. The result is a
+ dict.
"""
server = self.setup_server()
if sattrs is None:
@@ -245,7 +224,7 @@ class RPCClient:
return ipautil.unwrap_binary_data(result)
def get_all_users (self):
- """Return a list containing a User object for each existing user."""
+ """Return a list containing a dict for each existing user."""
server = self.setup_server()
try:
@@ -258,7 +237,7 @@ class RPCClient:
return ipautil.unwrap_binary_data(result)
def find_users (self, criteria, sattrs=None, searchlimit=0, timelimit=-1):
- """Return a list: counter followed by a User object for each user that
+ """Return a list: counter followed by a dict for each user that
matches the criteria. If the results are truncated, counter will
be set to -1"""
@@ -381,6 +360,8 @@ class RPCClient:
except socket.error, (value, msg):
raise xmlrpclib.Fault(value, msg)
+ return ipautil.unwrap_binary_data(result)
+
def find_groups (self, criteria, sattrs=None, searchlimit=0, timelimit=-1):
"""Return a list containing a Group object for each group that matches
the criteria."""
diff --git a/ipa-server/xmlrpc-server/funcs.py b/ipa-server/xmlrpc-server/funcs.py
index 528b138ba..2d2bddbb4 100644
--- a/ipa-server/xmlrpc-server/funcs.py
+++ b/ipa-server/xmlrpc-server/funcs.py
@@ -25,17 +25,15 @@ import ldap
import ldap.dn
import ipaserver.dsinstance
import ipaserver.ipaldap
-import ipa.ipautil
-import xmlrpclib
import copy
import attrs
from ipa import ipaerror
+from ipa import ipautil
from urllib import quote,unquote
from ipa import radius_util
import string
from types import *
-import os
import re
import logging
import subprocess
@@ -84,7 +82,7 @@ class IPAConnPool:
# This will bind the connection
try:
conn.set_krbccache(krbccache, cprinc.name)
- except ldap.UNWILLING_TO_PERFORM, e:
+ except ldap.UNWILLING_TO_PERFORM:
raise ipaerror.gen_exception(ipaerror.CONNECTION_UNWILLING)
return conn
@@ -111,7 +109,7 @@ class IPAServer:
if _LDAPPool is None:
_LDAPPool = IPAConnPool(128)
- self.basedn = ipa.ipautil.realm_to_suffix(self.realm)
+ self.basedn = ipautil.realm_to_suffix(self.realm)
self.scope = ldap.SCOPE_SUBTREE
self.princ = None
self.krbccache = None
@@ -127,11 +125,11 @@ class IPAServer:
global _LDAPPool
princ = self.__safe_filter(princ)
- filter = "(krbPrincipalName=" + princ + ")"
+ searchfilter = "(krbPrincipalName=" + princ + ")"
# The only anonymous search we should have
conn = _LDAPPool.getConn(self.host,self.sslport,self.bindca,self.bindcert,self.bindkey,None,None,debug)
try:
- ent = conn.getEntry(self.basedn, self.scope, filter, ['dn'])
+ ent = conn.getEntry(self.basedn, self.scope, searchfilter, ['dn'])
finally:
_LDAPPool.releaseConn(conn)
@@ -220,7 +218,7 @@ class IPAServer:
# they currently restrict the data coming back without
# restricting scope. For now adding a __get_base/sub_entry()
# calls, but the API isn't great.
- def __get_entry (self, base, scope, filter, sattrs=None, opts=None):
+ def __get_entry (self, base, scope, searchfilter, sattrs=None, opts=None):
"""Get a specific entry (with a parametized scope).
Return as a dict of values.
Multi-valued fields are represented as lists.
@@ -229,28 +227,28 @@ class IPAServer:
conn = self.getConnection(opts)
try:
- ent = conn.getEntry(base, scope, filter, sattrs)
+ ent = conn.getEntry(base, scope, searchfilter, sattrs)
finally:
self.releaseConnection(conn)
return self.convert_entry(ent)
- def __get_base_entry (self, base, filter, sattrs=None, opts=None):
+ def __get_base_entry (self, base, searchfilter, sattrs=None, opts=None):
"""Get a specific entry (with a scope of BASE).
Return as a dict of values.
Multi-valued fields are represented as lists.
"""
- return self.__get_entry(base, ldap.SCOPE_BASE, filter, sattrs, opts)
+ return self.__get_entry(base, ldap.SCOPE_BASE, searchfilter, sattrs, opts)
- def __get_sub_entry (self, base, filter, sattrs=None, opts=None):
+ def __get_sub_entry (self, base, searchfilter, sattrs=None, opts=None):
"""Get a specific entry (with a scope of SUB).
Return as a dict of values.
Multi-valued fields are represented as lists.
"""
- return self.__get_entry(base, ldap.SCOPE_SUBTREE, filter, sattrs, opts)
+ return self.__get_entry(base, ldap.SCOPE_SUBTREE, searchfilter, sattrs, opts)
- def __get_list (self, base, filter, sattrs=None, opts=None):
+ def __get_list (self, base, searchfilter, sattrs=None, opts=None):
"""Gets a list of entries. Each is converted to a dict of values.
Multi-valued fields are represented as lists.
"""
@@ -258,7 +256,7 @@ class IPAServer:
conn = self.getConnection(opts)
try:
- entries = conn.getList(base, self.scope, filter, sattrs)
+ entries = conn.getList(base, self.scope, searchfilter, sattrs)
finally:
self.releaseConnection(conn)
@@ -278,7 +276,7 @@ class IPAServer:
# original
try:
moddn = oldentry['dn']
- except KeyError, e:
+ except KeyError:
raise ipaerror.gen_exception(ipaerror.LDAP_MISSING_DN)
conn = self.getConnection(opts)
@@ -366,43 +364,66 @@ class IPAServer:
Multi-valued fields are represented as lists.
"""
- filter = "(objectClass=*)"
- return self.__get_base_entry(dn, filter, sattrs, opts)
+ if not dn:
+ raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
+ searchfilter = "(objectClass=*)"
+ return self.__get_base_entry(dn, searchfilter, sattrs, opts)
def get_entry_by_cn (self, cn, sattrs, opts=None):
"""Get a specific entry by cn. Return as a dict of values.
Multi-valued fields are represented as lists.
"""
+ if not cn:
+ raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
cn = self.__safe_filter(cn)
- filter = "(cn=" + cn + ")"
- return self.__get_sub_entry(self.basedn, filter, sattrs, opts)
+ searchfilter = "(cn=" + cn + ")"
+ return self.__get_sub_entry(self.basedn, searchfilter, sattrs, opts)
def update_entry (self, oldentry, newentry, opts=None):
- """Update an entry in LDAP"""
+ """Update an entry in LDAP
+
+ oldentry and newentry are XML-RPC structs.
+
+ If oldentry is not empty then it is used when determine what
+ has changed.
+
+ If oldentry is empty then the value of newentry is compared
+ to the current value of oldentry.
+ """
+ if not newentry:
+ raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
+
+ if not oldentry:
+ oldentry = self.get_entry_by_dn(newentry.get('dn'), None, opts)
+ if oldentry is None:
+ raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND)
+
return self.__update_entry(oldentry, newentry, opts)
# User support
def __is_user_unique(self, uid, opts):
- """Return 1 if the uid is unique in the tree, 0 otherwise."""
+ """Return True if the uid is unique in the tree, False otherwise."""
uid = self.__safe_filter(uid)
- filter = "(&(uid=%s)(objectclass=posixAccount))" % uid
+ searchfilter = "(&(uid=%s)(objectclass=posixAccount))" % uid
try:
- entry = self.__get_sub_entry(self.basedn, filter, ['dn','uid'], opts)
- return 0
+ entry = self.__get_sub_entry(self.basedn, searchfilter, ['dn','uid'], opts)
+ return False
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
- return 1
+ return True
def get_user_by_uid (self, uid, sattrs, opts=None):
"""Get a specific user's entry. Return as a dict of values.
Multi-valued fields are represented as lists.
"""
+ if not uid:
+ raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
uid = self.__safe_filter(uid)
- filter = "(uid=" + uid + ")"
- return self.__get_sub_entry(self.basedn, filter, sattrs, opts)
+ searchfilter = "(uid=" + uid + ")"
+ return self.__get_sub_entry(self.basedn, searchfilter, sattrs, opts)
def get_user_by_principal(self, principal, sattrs, opts=None):
"""Get a user entry searching by Kerberos Principal Name.
@@ -410,27 +431,33 @@ class IPAServer:
represented as lists.
"""
- filter = "(krbPrincipalName="+self.__safe_filter(principal)+")"
- return self.__get_sub_entry(self.basedn, filter, sattrs, opts)
+ if not principal:
+ raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
+ searchfilter = "(krbPrincipalName="+self.__safe_filter(principal)+")"
+ return self.__get_sub_entry(self.basedn, searchfilter, sattrs, opts)
def get_user_by_email (self, email, sattrs, opts=None):
"""Get a specific user's entry. Return as a dict of values.
Multi-valued fields are represented as lists.
"""
+ if not email:
+ raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
email = self.__safe_filter(email)
- filter = "(mail=" + email + ")"
- return self.__get_sub_entry(self.basedn, filter, sattrs, opts)
+ searchfilter = "(mail=" + email + ")"
+ return self.__get_sub_entry(self.basedn, searchfilter, sattrs, opts)
def get_users_by_manager (self, manager_dn, sattrs, opts=None):
"""Gets the users that report to a particular manager.
"""
+ if not manager_dn:
+ raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
manager_dn = self.__safe_filter(manager_dn)
- filter = "(&(objectClass=person)(manager=%s))" % manager_dn
+ searchfilter = "(&(objectClass=person)(manager=%s))" % manager_dn
try:
- return self.__get_list(self.basedn, filter, sattrs, opts)
+ return self.__get_list(self.basedn, searchfilter, sattrs, opts)
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
return []
@@ -438,11 +465,16 @@ class IPAServer:
"""Add a user in LDAP. Takes as input a dict where the key is the
attribute name and the value is either a string or in the case
of a multi-valued field a list of values. user_container sets
- where in the tree the user is placed."""
+ where in the tree the user is placed.
+ """
+
+ if not user:
+ raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
+
if not user_container:
user_container = DefaultUserContainer
- if self.__is_user_unique(user['uid'], opts) == 0:
+ if not self.__is_user_unique(user['uid'], opts):
raise ipaerror.gen_exception(ipaerror.LDAP_DUPLICATE)
# dn is set here, not by the user
@@ -758,6 +790,8 @@ class IPAServer:
It is displayed to the user in the order of the list.
"""
+ if not schema:
+ raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
config = self.get_ipa_config(opts)
# The schema is stored as:
@@ -783,11 +817,11 @@ class IPAServer:
"""Return a list containing a User object for each
existing user.
"""
- filter = "(objectclass=posixAccount)"
+ searchfilter = "(objectclass=posixAccount)"
conn = self.getConnection(opts)
try:
- all_users = conn.getList(self.basedn, self.scope, filter, None)
+ all_users = conn.getList(self.basedn, self.scope, searchfilter, None)
finally:
self.releaseConnection(conn)
@@ -803,6 +837,8 @@ class IPAServer:
If the results are truncated, counter will be set to -1."""
logging.debug("IPA: find users %s" % criteria)
+ if not criteria:
+ raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
config = self.get_ipa_config(opts)
if timelimit < 0:
timelimit = float(config.get('ipasearchtimelimit'))
@@ -875,6 +911,8 @@ class IPAServer:
def convert_scalar_values(self, orig_dict):
"""LDAP update dicts expect all values to be a list (except for dn).
This method converts single entries to a list."""
+ if not orig_dict or not isinstance(orig_dict, dict):
+ raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
new_dict={}
for (k,v) in orig_dict.iteritems():
if not isinstance(v, list) and k != 'dn':
@@ -886,9 +924,23 @@ class IPAServer:
def update_user (self, oldentry, newentry, opts=None):
"""Wrapper around update_entry with user-specific handling.
+ oldentry and newentry are XML-RPC structs.
+
+ If oldentry is not empty then it is used when determine what
+ has changed.
+
+ If oldentry is empty then the value of newentry is compared
+ to the current value of oldentry.
+
If you want to change the RDN of a user you must use
this function. update_entry will fail.
"""
+ if not newentry:
+ raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
+ if not oldentry:
+ oldentry = self.get_entry_by_dn(newentry.get('dn'), None, opts)
+ if oldentry is None:
+ raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND)
newrdn = 0
@@ -938,6 +990,9 @@ class IPAServer:
logging.debug("IPA: activating entry %s" % dn)
+ if not dn:
+ raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
+
res = ""
# First, check the entry status
entry = self.get_entry_by_dn(dn, ['dn', 'nsAccountlock'], opts)
@@ -970,6 +1025,9 @@ class IPAServer:
logging.debug("IPA: inactivating entry %s" % dn)
+ if not dn:
+ raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
+
entry = self.get_entry_by_dn(dn, ['dn', 'nsAccountlock', 'memberOf'], opts)
if entry.get('nsaccountlock', 'false') == "true":
@@ -990,12 +1048,16 @@ class IPAServer:
def mark_user_active(self, uid, opts=None):
"""Mark a user as active"""
+ if not uid:
+ raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
user = self.get_user_by_uid(uid, ['dn', 'uid'], opts)
return self.mark_entry_active(user.get('dn'))
def mark_user_inactive(self, uid, opts=None):
"""Mark a user as inactive"""
+ if not uid:
+ raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
user = self.get_user_by_uid(uid, ['dn', 'uid'], opts)
return self.mark_entry_inactive(user.get('dn'))
@@ -1008,6 +1070,8 @@ class IPAServer:
The memberOf plugin handles removing the user from any other
groups.
"""
+ if not uid:
+ raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
user = self.get_user_by_uid(uid, ['dn', 'uid', 'objectclass'], opts)
if user is None:
raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND)
@@ -1026,6 +1090,8 @@ class IPAServer:
oldpass is the old password (if available)
newpass is the new password
"""
+ if not principal or not newpass:
+ raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
user = self.get_user_by_principal(principal, ['krbprincipalname'], opts)
if user is None or user['krbprincipalname'] != principal:
raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND)
@@ -1040,26 +1106,28 @@ class IPAServer:
# Group support
def __is_group_unique(self, cn, opts):
- """Return 1 if the cn is unique in the tree, 0 otherwise."""
+ """Return True if the cn is unique in the tree, False otherwise."""
cn = self.__safe_filter(cn)
- filter = "(&(cn=%s)(objectclass=posixGroup))" % cn
+ searchfilter = "(&(cn=%s)(objectclass=posixGroup))" % cn
try:
- entry = self.__get_sub_entry(self.basedn, filter, ['dn','cn'], opts)
- return 0
+ entry = self.__get_sub_entry(self.basedn, searchfilter, ['dn','cn'], opts)
+ return False
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
- return 1
+ return True
def get_groups_by_member (self, member_dn, sattrs, opts=None):
"""Get a specific group's entry. Return as a dict of values.
Multi-valued fields are represented as lists.
"""
+ if not member_dn:
+ raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
member_dn = self.__safe_filter(member_dn)
- filter = "(&(objectClass=posixGroup)(member=%s))" % member_dn
+ searchfilter = "(&(objectClass=posixGroup)(member=%s))" % member_dn
try:
- return self.__get_list(self.basedn, filter, sattrs, opts)
+ return self.__get_list(self.basedn, searchfilter, sattrs, opts)
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
return []
@@ -1068,10 +1136,13 @@ class IPAServer:
attribute name and the value is either a string or in the case
of a multi-valued field a list of values. group_container sets
where in the tree the group is placed."""
+ if not group:
+ raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
+
if not group_container:
group_container = DefaultGroupContainer
- if self.__is_group_unique(group['cn'], opts) == 0:
+ if not self.__is_group_unique(group['cn'], opts):
raise ipaerror.gen_exception(ipaerror.LDAP_DUPLICATE)
# Get our configuration
@@ -1102,6 +1173,8 @@ class IPAServer:
"""Return a list containing a User object for each
existing group that matches the criteria.
"""
+ if not criteria:
+ raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
config = self.get_ipa_config(opts)
if timelimit < 0:
@@ -1178,6 +1251,8 @@ class IPAServer:
def add_member_to_group(self, member_dn, group_dn, opts=None):
"""Add a member to an existing group.
"""
+ if not member_dn or not group_dn:
+ raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
old_group = self.get_entry_by_dn(group_dn, None, opts)
if old_group is None:
@@ -1186,6 +1261,8 @@ class IPAServer:
# check to make sure member_dn exists
member_entry = self.__get_base_entry(member_dn, "(objectClass=*)", ['dn','uid'], opts)
+ if not member_entry:
+ raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND)
if new_group.get('member') is not None:
if ((isinstance(new_group.get('member'), str)) or (isinstance(new_group.get('member'), unicode))):
@@ -1205,6 +1282,9 @@ class IPAServer:
Returns a list of the member_dns that were not added to the group.
"""
+ if not member_dns or not group_dn:
+ raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
+
failed = []
if (isinstance(member_dns, str)):
@@ -1225,6 +1305,8 @@ class IPAServer:
def remove_member_from_group(self, member_dn, group_dn, opts=None):
"""Remove a member_dn from an existing group.
"""
+ if not member_dn or not group_dn:
+ raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
old_group = self.get_entry_by_dn(group_dn, None, opts)
if old_group is None:
@@ -1255,6 +1337,8 @@ class IPAServer:
"""Given a list of member dn's remove them from the group.
Returns a list of the members not removed from the group.
"""
+ if not member_dns or not group_dn:
+ raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
failed = []
@@ -1277,6 +1361,8 @@ class IPAServer:
"""Add a user to an existing group.
"""
+ if not user_uid or not group_dn:
+ raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
user = self.get_user_by_uid(user_uid, ['dn', 'uid', 'objectclass'], opts)
if user is None:
raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND)
@@ -1287,6 +1373,8 @@ class IPAServer:
"""Given a list of user uid's add them to the group cn denoted by group
Returns a list of the users were not added to the group.
"""
+ if not user_uids or not group_dn:
+ raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
failed = []
@@ -1309,6 +1397,9 @@ class IPAServer:
"""Remove a user from an existing group.
"""
+ if not user_uid or not group_dn:
+ raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
+
user = self.get_user_by_uid(user_uid, ['dn', 'uid', 'objectclass'], opts)
if user is None:
raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND)
@@ -1319,6 +1410,8 @@ class IPAServer:
"""Given a list of user uid's remove them from the group
Returns a list of the user uids not removed from the group.
"""
+ if not user_uids or not group_dn:
+ raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
failed = []
@@ -1342,6 +1435,8 @@ class IPAServer:
Returns a list of the group dns that were not added.
"""
+ if not group_dns or not user_dn:
+ raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
failed = []
@@ -1365,6 +1460,8 @@ class IPAServer:
Returns a list of the group dns that were not removed.
"""
+ if not group_dns or not user_dn:
+ raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
failed = []
@@ -1386,9 +1483,23 @@ class IPAServer:
def update_group (self, oldentry, newentry, opts=None):
"""Wrapper around update_entry with group-specific handling.
+ oldentry and newentry are XML-RPC structs.
+
+ If oldentry is not empty then it is used when determine what
+ has changed.
+
+ If oldentry is empty then the value of newentry is compared
+ to the current value of oldentry.
+
If you want to change the RDN of a group you must use
this function. update_entry will fail.
"""
+ if not newentry:
+ raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
+ if not oldentry:
+ oldentry = self.get_entry_by_dn(newentry.get('dn'), None, opts)
+ if oldentry is None:
+ raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND)
newrdn = 0
@@ -1451,6 +1562,8 @@ class IPAServer:
The memberOf plugin handles removing the group from any other
groups.
"""
+ if not group_dn:
+ raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
group = self.get_entry_by_dn(group_dn, ['dn', 'cn'], opts)
if group is None:
raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND)
@@ -1478,6 +1591,8 @@ class IPAServer:
tgroup is the DN of the target group to be added to
"""
+ if not group or not tgroup:
+ raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
old_group = self.get_entry_by_dn(tgroup, None, opts)
if old_group is None:
raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND)
@@ -1514,19 +1629,21 @@ class IPAServer:
"""Do a memberOf search of groupdn and return the attributes in
attr_list (an empty list returns everything)."""
+ if not groupdn:
+ raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
config = self.get_ipa_config(opts)
timelimit = float(config.get('ipasearchtimelimit'))
searchlimit = float(config.get('ipasearchrecordslimit'))
groupdn = self.__safe_filter(groupdn)
- filter = "(memberOf=%s)" % groupdn
+ searchfilter = "(memberOf=%s)" % groupdn
conn = self.getConnection(opts)
try:
try:
results = conn.getListAsync(self.basedn, self.scope,
- filter, attr_list, 0, None, None, timelimit,
+ searchfilter, attr_list, 0, None, None, timelimit,
searchlimit)
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
results = [0]
@@ -1545,33 +1662,42 @@ class IPAServer:
def mark_group_active(self, cn, opts=None):
"""Mark a group as active"""
+ if not cn:
+ raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
group = self.get_entry_by_cn(cn, ['dn', 'cn'], opts)
return self.mark_entry_active(group.get('dn'))
def mark_group_inactive(self, cn, opts=None):
"""Mark a group as inactive"""
+ if not cn:
+ raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
group = self.get_entry_by_cn(cn, ['dn', 'uid'], opts)
return self.mark_entry_inactive(group.get('dn'))
def __is_service_unique(self, name, opts):
- """Return 1 if the uid is unique in the tree, 0 otherwise."""
+ """Return True if the uid is unique in the tree, False otherwise."""
name = self.__safe_filter(name)
- filter = "(&(krbprincipalname=%s)(objectclass=krbPrincipal))" % name
+ searchfilter = "(&(krbprincipalname=%s)(objectclass=krbPrincipal))" % name
try:
- entry = self.__get_sub_entry(self.basedn, filter, ['dn','krbprincipalname'], opts)
- return 0
+ entry = self.__get_sub_entry(self.basedn, searchfilter, ['dn','krbprincipalname'], opts)
+ return False
except ipaerror.exception_for(ipaerror.LDAP_NOT_FOUND):
- return 1
+ return True
def add_service_principal(self, name, opts=None):
+ """Given a name of the form: service/FQDN create a service
+ principal for it in the default realm."""
+ if not name:
+ raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
+
service_container = DefaultServiceContainer
princ_name = name + "@" + self.realm
conn = self.getConnection(opts)
- if self.__is_service_unique(name, opts) == 0:
+ if not self.__is_service_unique(name, opts):
raise ipaerror.gen_exception(ipaerror.LDAP_DUPLICATE)
dn = "krbprincipalname=%s,%s,%s" % (ldap.dn.escape_dn_chars(princ_name),
@@ -1591,6 +1717,8 @@ class IPAServer:
timelimit=-1, opts=None):
"""Returns a list: counter followed by the results.
If the results are truncated, counter will be set to -1."""
+ if not criteria:
+ raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
config = self.get_ipa_config(opts)
if timelimit < 0:
@@ -1658,7 +1786,10 @@ class IPAServer:
return entries
def get_keytab(self, name, opts=None):
- """get a keytab"""
+ """Return a keytab for an existing service principal. Note that
+ this increments the secret thus invalidating any older keys."""
+ if not name:
+ raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
princ_name = name + "@" + self.realm
@@ -1699,8 +1830,24 @@ class IPAServer:
return config
def update_ipa_config(self, oldconfig, newconfig, opts=None):
- """Update the IPA configuration"""
-
+ """Update the IPA configuration.
+
+ oldconfig and newconfig are XML-RPC structs.
+
+ If oldconfig is not empty then it is used when determine what
+ has changed.
+
+ If oldconfig is empty then the value of newconfig is compared
+ to the current value of oldconfig.
+
+ """
+ if not newconfig:
+ raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
+ if not oldconfig:
+ oldconfig = self.get_entry_by_dn(newconfig.get('dn'), None, opts)
+ if oldconfig is None:
+ raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND)
+
# The LDAP routines want strings, not ints, so convert a few
# things. Otherwise it sees a string -> int conversion as a change.
try:
@@ -1749,7 +1896,24 @@ class IPAServer:
return policy
def update_password_policy(self, oldpolicy, newpolicy, opts=None):
- """Update the IPA configuration"""
+ """Update the IPA configuration
+
+ oldpolicy and newpolicy are XML-RPC structs.
+
+ If oldpolicy is not empty then it is used when determine what
+ has changed.
+
+ If oldpolicy is empty then the value of newpolicy is compared
+ to the current value of oldpolicy.
+
+ """
+ if not newpolicy:
+ raise ipaerror.gen_exception(ipaerror.INPUT_INVALID_PARAMETER)
+ if not oldpolicy:
+ oldpolicy = self.get_entry_by_dn(newpolicy.get('dn'), None, opts)
+ if oldpolicy is None:
+ raise ipaerror.gen_exception(ipaerror.LDAP_NOT_FOUND)
+
# The LDAP routines want strings, not ints, so convert a few
# things. Otherwise it sees a string -> int conversion as a change.
--
cgit
From 148a55811d0e9814bc2943f3600acf17be1b844e Mon Sep 17 00:00:00 2001
From: Karl MacMillan
Date: Wed, 12 Dec 2007 10:10:06 -0500
Subject: Return a proper error code from ipa-webgui so that the init script
can indicate when the service fails to start.
---
ipa-server/ipa-gui/ipa-webgui | 69 ++++++++++++++++++++++++-------------------
1 file changed, 38 insertions(+), 31 deletions(-)
diff --git a/ipa-server/ipa-gui/ipa-webgui b/ipa-server/ipa-gui/ipa-webgui
index c8e3d0589..a18c2db81 100644
--- a/ipa-server/ipa-gui/ipa-webgui
+++ b/ipa-server/ipa-gui/ipa-webgui
@@ -63,41 +63,48 @@ def daemonize():
# stderr
os.open("/dev/null", os.O_RDWR)
-# To make development easier, we detect if we are in the development
-# environment to load a different configuration and avoid becoming
-# a daemon
-devel = False
-if os.path.exists(os.path.join(os.path.dirname(__file__), "Makefile.am")):
- devel = True
-
-if not devel:
- try:
- daemonize()
- except Exception, e:
- sys.stderr.write("error becoming daemon: " + str(e))
- sys.exit(1)
+def main():
+ # To make development easier, we detect if we are in the development
+ # environment to load a different configuration and avoid becoming
+ # a daemon
+ devel = False
+ if os.path.exists(os.path.join(os.path.dirname(__file__), "Makefile.am")):
+ devel = True
+
+ if not devel:
+ try:
+ daemonize()
+ except Exception, e:
+ sys.stderr.write("error becoming daemon: " + str(e))
+ sys.exit(1)
+
+ sys.path.append("/usr/share/ipa/")
-sys.path.append("/usr/share/ipa/")
+ # this must be after sys.path is changed to work correctly
+ import pkg_resources
+ pkg_resources.require("TurboGears")
+ pkg_resources.require("ipa_gui")
-# this must be after sys.path is changed to work correctly
-import pkg_resources
-pkg_resources.require("TurboGears")
-pkg_resources.require("ipa_gui")
+ from turbogears import update_config, start_server
+ import cherrypy
+ cherrypy.lowercase_api = True
-from turbogears import update_config, start_server
-import cherrypy
-cherrypy.lowercase_api = True
+ # Load the config - look for a local file first for development
+ # and then the system config file
+ if devel:
+ update_config(configfile="dev.cfg",
+ modulename="ipagui.config")
+ else:
+ update_config(configfile="/usr/share/ipa/ipa-webgui.cfg",
+ modulename="ipagui.config.app")
-# Load the config - look for a local file first for development
-# and then the system config file
-if devel:
- update_config(configfile="dev.cfg",
- modulename="ipagui.config")
-else:
- update_config(configfile="/usr/share/ipa/ipa-webgui.cfg",
- modulename="ipagui.config.app")
+ from ipagui.controllers import Root
-from ipagui.controllers import Root
+ start_server(Root())
-start_server(Root())
+try:
+ main()
+except Exception, e:
+ print "failed to start web gui: %s" % str(e)
+ sys.exit(1)
--
cgit
From 380756ace9f1f6f4ce1e203d53f64ac503bb6652 Mon Sep 17 00:00:00 2001
From: Karl MacMillan
Date: Wed, 12 Dec 2007 10:16:22 -0500
Subject: Confirm before configuring the client.
---
ipa-client/ipa-install/ipa-client-install | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/ipa-client/ipa-install/ipa-client-install b/ipa-client/ipa-install/ipa-client-install
index bc8e7c9c3..dfaabca3e 100644
--- a/ipa-client/ipa-install/ipa-client-install
+++ b/ipa-client/ipa-install/ipa-client-install
@@ -124,6 +124,12 @@ def main():
print "IPA Server: "+ds.getServerName()
print "BaseDN: "+ds.getBaseDN()
+ print "\n"
+ yesno = raw_input("Continue to configure the system with these values? [y/N] ")
+ if yesno.lower() != "y":
+ return 1
+ print "\n"
+
# Configure ipa.conf
ipaconf = ipaclient.ipachangeconf.IPAChangeConf("IPA Installer")
ipaconf.setOptionAssignment(" = ")
@@ -225,4 +231,4 @@ def main():
return 0
-main()
+sys.exit(main())
--
cgit
From 7561d7c42d8d62d42cb6a50dac368c786cf75b38 Mon Sep 17 00:00:00 2001
From: Karl MacMillan
Date: Wed, 12 Dec 2007 10:36:46 -0500
Subject: Remove radius from main install script and add ipa-radius-install.
---
ipa-server/ipa-install/Makefile.am | 1 +
ipa-server/ipa-install/ipa-radius-install | 72 +++++++++++++++++++++++++++++++
ipa-server/ipa-install/ipa-server-install | 5 ---
ipa-server/ipaserver/radiusinstance.py | 2 +-
4 files changed, 74 insertions(+), 6 deletions(-)
create mode 100644 ipa-server/ipa-install/ipa-radius-install
diff --git a/ipa-server/ipa-install/Makefile.am b/ipa-server/ipa-install/Makefile.am
index 1b46d354b..b9ce69156 100644
--- a/ipa-server/ipa-install/Makefile.am
+++ b/ipa-server/ipa-install/Makefile.am
@@ -8,6 +8,7 @@ sbin_SCRIPTS = \
ipa-server-install \
ipa-replica-install \
ipa-replica-prepare \
+ ipa-radius-install \
$(NULL)
EXTRA_DIST = \
diff --git a/ipa-server/ipa-install/ipa-radius-install b/ipa-server/ipa-install/ipa-radius-install
new file mode 100644
index 000000000..84e10e426
--- /dev/null
+++ b/ipa-server/ipa-install/ipa-radius-install
@@ -0,0 +1,72 @@
+#! /usr/bin/python -E
+# Authors: Karl MacMillan
+#
+# 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
+#
+
+import sys
+sys.path.append("/usr/share/ipa")
+
+import traceback, logging, krbV
+
+from ipaserver import radiusinstance, installutils
+
+from ipa import ipautil
+
+def get_host_name():
+ hostname = installutils.get_fqdn()
+ try:
+ installutils.verify_fqdn(hostname)
+ except RuntimeError, e:
+ logging.error(str(e))
+ sys.exit(1)
+
+ return hostname
+
+def get_realm_name():
+ c = krbV.default_context()
+ return c.default_realm
+
+def main():
+ if not ipautil.file_exists("/etc/ipa/ipa.conf"):
+ print "This system does not appear to have IPA configured."
+ print "Has ipa-server-install been run?"
+ yesno = raw_input("Continue with radius install [y/N]? ")
+ if yesno.lower() != "y":
+ sys.exit(1)
+
+ installutils.standard_logging_setup("iparadius-install.log", False)
+
+ host_name = get_host_name()
+
+ realm_name = get_realm_name()
+
+ # Create a radius instance
+ radius = radiusinstance.RadiusInstance()
+ # FIXME: ldap_server should be derived, not hardcoded to localhost, also should it be a URL?
+ radius.create_instance(realm_name, host_name, 'localhost')
+
+
+try:
+ main()
+except Exception, e:
+ message = "Unexpected error - see iparadius-install.log for details:\n %s" % str(e)
+ print message
+ message = str(e)
+ for str in traceback.format_tb(sys.exc_info()[2]):
+ message = message + "\n" + str
+ logging.debug(message)
diff --git a/ipa-server/ipa-install/ipa-server-install b/ipa-server/ipa-install/ipa-server-install
index 646512d51..05bece9e2 100644
--- a/ipa-server/ipa-install/ipa-server-install
+++ b/ipa-server/ipa-install/ipa-server-install
@@ -400,11 +400,6 @@ def main():
webgui = ipaserver.webguiinstance.WebGuiInstance()
webgui.create_instance()
- # Create a radius instance
- radius = ipaserver.radiusinstance.RadiusInstance()
- # FIXME: ldap_server should be derived, not hardcoded to localhost, also should it be a URL?
- radius.create_instance(realm_name, host_name, 'localhost')
-
bind.setup(host_name, ip_address, realm_name)
if options.setup_bind:
skipbind = False
diff --git a/ipa-server/ipaserver/radiusinstance.py b/ipa-server/ipaserver/radiusinstance.py
index 3b89018f0..4250b4bd1 100644
--- a/ipa-server/ipaserver/radiusinstance.py
+++ b/ipa-server/ipaserver/radiusinstance.py
@@ -125,7 +125,7 @@ class RadiusInstance(service.Service):
logging.error("could not create %s: %s", radius_util.RADIUSD_CONF_FILEPATH, e)
def __create_radius_keytab(self):
- self.step("creating a keytab for httpd")
+ self.step("creating a keytab for radiusd")
try:
if file_exists(radius_util.RADIUS_IPA_KEYTAB_FILEPATH):
os.remove(radius_util.RADIUS_IPA_KEYTAB_FILEPATH)
--
cgit
From 2892c28f56b855bc07a1c2cdb765bd0e5d2e5f8b Mon Sep 17 00:00:00 2001
From: Karl MacMillan
Date: Wed, 12 Dec 2007 11:05:47 -0500
Subject: Improve confirmation.
---
ipa-client/ipa-install/ipa-client-install | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/ipa-client/ipa-install/ipa-client-install b/ipa-client/ipa-install/ipa-client-install
index dfaabca3e..9cc6f6f6b 100644
--- a/ipa-client/ipa-install/ipa-client-install
+++ b/ipa-client/ipa-install/ipa-client-install
@@ -51,6 +51,13 @@ def parse_options():
return options
+def ask_for_confirmation(message):
+ yesno = raw_input(message + " [y/N]: ")
+ if not yesno or yesno.lower()[0] != "y":
+ return False
+ print "\n"
+ return True
+
def logging_setup(options):
# Always log everything (i.e., DEBUG) to the log
# file.
@@ -114,10 +121,8 @@ def main():
print "\nThe failure to use DNS to find your IPA server indicates that your resolv.conf file is not properly configured\n."
print "Autodiscovery of servers for failover cannot work with this configuration.\n"
print "If you proceed with the installation, services will be configured to always access the discovered server for all operation and will not fail over to other servers in case of failure\n"
- yesno = raw_input("Do you want to proceed and configure the system with fixed values with no DNS discovery? [y/N] ")
- if yesno.lower() != "y":
+ if not ask_for_confirmation("Do you want to proceed and configure the system with fixed values with no DNS discovery?"):
return ret
- print "\n"
print "Realm: "+ds.getRealmName()
print "DNS Domain: "+ds.getDomainName()
@@ -125,10 +130,8 @@ def main():
print "BaseDN: "+ds.getBaseDN()
print "\n"
- yesno = raw_input("Continue to configure the system with these values? [y/N] ")
- if yesno.lower() != "y":
+ if not ask_for_confirmation("Continue to configure the system with these values?"):
return 1
- print "\n"
# Configure ipa.conf
ipaconf = ipaclient.ipachangeconf.IPAChangeConf("IPA Installer")
--
cgit
From 8792559f7440cac54072395a7677f31e9b029dc7 Mon Sep 17 00:00:00 2001
From: Karl MacMillan
Date: Wed, 12 Dec 2007 11:16:20 -0500
Subject: Remove radiusinstance from ipa-server-install.
---
ipa-server/ipa-install/ipa-server-install | 1 -
1 file changed, 1 deletion(-)
diff --git a/ipa-server/ipa-install/ipa-server-install b/ipa-server/ipa-install/ipa-server-install
index 05bece9e2..ee5e929d1 100644
--- a/ipa-server/ipa-install/ipa-server-install
+++ b/ipa-server/ipa-install/ipa-server-install
@@ -46,7 +46,6 @@ import ipaserver.krbinstance
import ipaserver.bindinstance
import ipaserver.httpinstance
import ipaserver.ntpinstance
-import ipaserver.radiusinstance
import ipaserver.webguiinstance
from ipaserver import service
--
cgit
From 1c3849eb576dc9d4cd3d4a39aff9da78be0ddcba Mon Sep 17 00:00:00 2001
From: Karl MacMillan
Date: Wed, 12 Dec 2007 11:19:42 -0500
Subject: User provided certs.
---
ipa-server/ipa-install/Makefile.am | 1 +
ipa-server/ipa-install/ipa-server-certinstall | 156 ++++++++++++++++++++++++++
ipa-server/ipaserver/certs.py | 49 +++++++-
ipa-server/ipaserver/dsinstance.py | 2 +-
ipa-server/ipaserver/httpinstance.py | 18 +--
ipa-server/ipaserver/installutils.py | 19 ++++
6 files changed, 224 insertions(+), 21 deletions(-)
create mode 100644 ipa-server/ipa-install/ipa-server-certinstall
diff --git a/ipa-server/ipa-install/Makefile.am b/ipa-server/ipa-install/Makefile.am
index b9ce69156..37dd325e8 100644
--- a/ipa-server/ipa-install/Makefile.am
+++ b/ipa-server/ipa-install/Makefile.am
@@ -8,6 +8,7 @@ sbin_SCRIPTS = \
ipa-server-install \
ipa-replica-install \
ipa-replica-prepare \
+ ipa-server-certinstall \
ipa-radius-install \
$(NULL)
diff --git a/ipa-server/ipa-install/ipa-server-certinstall b/ipa-server/ipa-install/ipa-server-certinstall
new file mode 100644
index 000000000..932a6be18
--- /dev/null
+++ b/ipa-server/ipa-install/ipa-server-certinstall
@@ -0,0 +1,156 @@
+#! /usr/bin/python -E
+# Authors: Karl MacMillan
+#
+# 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 sys
+sys.path.append("/usr/share/ipa")
+
+import traceback
+
+import krbV, ldap, getpass
+
+from ipaserver import certs, dsinstance, httpinstance, ipaldap, installutils
+
+def get_realm_name():
+ c = krbV.default_context()
+ return c.default_realm
+
+def parse_options():
+ from optparse import OptionParser
+ parser = OptionParser()
+
+ parser.add_option("-d", "--dirsrv", dest="dirsrv", action="store_true",
+ default=False, help="install certificate for the directory server")
+ parser.add_option("-w", "--http", dest="http", action="store_true",
+ default=False, help="install certificate for the http server")
+
+
+ options, args = parser.parse_args()
+
+ if not options.dirsrv and not options.http:
+ parser.error("you must specify dirsrv and/or http")
+
+ if len(args) != 1:
+ parser.error("you must provide a pkcs12 filename")
+
+ return options, args[0]
+
+def set_ds_cert_name(cert_name, dm_password):
+ conn = ipaldap.IPAdmin("127.0.0.1")
+ conn.simple_bind_s("cn=directory manager", dm_password)
+
+ mod = [(ldap.MOD_REPLACE, "nsSSLPersonalitySSL", cert_name)]
+
+ conn.modify_s("cn=RSA,cn=encryption,cn=config", mod)
+
+ conn.unbind()
+
+def set_http_cert_name(cert_name):
+ # find the existing cert name
+ fd = open(httpinstance.NSS_CONF)
+ nick_name = None
+ file = []
+ for line in fd:
+ if "NSSNickname" in line:
+ file.append('NSSNickname "%s"\n' % cert_name)
+ else:
+ file.append(line)
+ fd.close()
+
+ fd = open(httpinstance.NSS_CONF, "w")
+ fd.write("".join(file))
+ fd.close()
+
+
+def choose_server_cert(server_certs):
+ print "Please select the certificate to use:"
+ num = 1
+ for cert in server_certs:
+ print "%d. %s" % (num, cert[0])
+ num += 1
+
+ cert_num = 0
+ while 1:
+ cert_input = raw_input("Certificate number [1]: ")
+ print ""
+ if cert_input == "":
+ break
+ else:
+ try:
+ num = int(cert_input)
+ except ValueError:
+ print "invalid number"
+ continue
+ if num > len(server_certs):
+ print "number out of range"
+ continue
+ cert_num = num - 1
+ break
+ return server_certs[cert_num]
+
+
+def import_cert(dirname, pkcs12_fname):
+ cdb = certs.CertDB(dirname)
+ cdb.create_passwd_file(False)
+ cdb.create_certdbs()
+ try:
+ cdb.import_pkcs12(pkcs12_fname)
+ except RuntimeError, e:
+ print str(e)
+ sys.exit(1)
+
+ server_certs = cdb.find_server_certs()
+ if len(server_certs) == 0:
+ print "could not find a suitable server cert in import"
+ sys.exit(1)
+ elif len(server_certs) == 1:
+ server_cert = server_certs[0]
+ else:
+ server_cert = choose_server_cert(server_certs)
+
+ cdb.trust_root_cert(server_cert[0])
+
+ return server_cert
+
+def main():
+ options, pkcs12_fname = parse_options()
+
+ try:
+ if options.dirsrv:
+ dm_password = getpass.getpass("Directory Manager password: ")
+ realm = get_realm_name()
+ dirname = dsinstance.config_dirname(realm)
+ server_cert = import_cert(dirname, pkcs12_fname)
+ set_ds_cert_name(server_cert[0], dm_password)
+
+ if options.http:
+ dirname = httpinstance.NSS_DIR
+ server_cert = import_cert(dirname, pkcs12_fname)
+ print server_cert
+ set_http_cert_name(server_cert[0])
+
+ except Exception, e:
+ print "an unexpected error occurred: %s" % str(e)
+ traceback.print_exc()
+ return 1
+
+ return 0
+
+
+sys.exit(main())
diff --git a/ipa-server/ipaserver/certs.py b/ipa-server/ipaserver/certs.py
index fb6b01d0e..77052c13e 100644
--- a/ipa-server/ipaserver/certs.py
+++ b/ipa-server/ipaserver/certs.py
@@ -17,7 +17,7 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
-import os, stat, subprocess
+import os, stat, subprocess, re
import sha
from ipa import ipautil
@@ -196,6 +196,50 @@ class CertDB(object):
f.close()
self.set_perms(self.pin_fname)
+ def trust_root_cert(self, nickname):
+ p = subprocess.Popen(["/usr/bin/certutil", "-d", self.secdir,
+ "-O", "-n", nickname], stdout=subprocess.PIPE)
+
+ chain = p.stdout.read()
+ chain = chain.split("\n")
+
+ root_nickname = re.match('\ *"(.*)".*', chain[0]).groups()[0]
+
+ self.run_certutil(["-M", "-n", root_nickname,
+ "-t", "CT,CT,"])
+
+ def find_server_certs(self):
+ p = subprocess.Popen(["/usr/bin/certutil", "-d", self.secdir,
+ "-L"], stdout=subprocess.PIPE)
+
+ certs = p.stdout.read()
+
+ certs = certs.split("\n")
+
+ server_certs = []
+
+ for cert in certs:
+ fields = cert.split()
+ if not len(fields):
+ continue
+ flags = fields[-1]
+ if 'u' in flags:
+ name = " ".join(fields[0:-1])
+ server_certs.append((name, flags))
+
+ return server_certs
+
+
+ def import_pkcs12(self, pkcs12_fname):
+ try:
+ ipautil.run(["/usr/bin/pk12util", "-d", self.secdir,
+ "-i", pkcs12_fname])
+ except ipautil.CalledProcessError, e:
+ if e.returncode == 17:
+ raise RuntimeError("incorrect password")
+ else:
+ raise RuntimeError("unknown error import pkcs#12 file")
+
def create_self_signed(self, passwd=True):
self.create_noise_file()
self.create_passwd_file(passwd)
@@ -208,6 +252,3 @@ class CertDB(object):
self.create_passwd_file(passwd)
self.create_certdbs()
self.load_cacert(cacert_fname)
-
-
-
diff --git a/ipa-server/ipaserver/dsinstance.py b/ipa-server/ipaserver/dsinstance.py
index 5edc3879e..818710fb6 100644
--- a/ipa-server/ipaserver/dsinstance.py
+++ b/ipa-server/ipaserver/dsinstance.py
@@ -322,7 +322,7 @@ class DsInstance(service.Service):
conn.addEntry(entry)
conn.unbind()
-
+
def __add_default_layout(self):
self.step("adding default layout")
txt = ipautil.template_file(ipautil.SHARE_DIR + "bootstrap-template.ldif", self.sub_dict)
diff --git a/ipa-server/ipaserver/httpinstance.py b/ipa-server/ipaserver/httpinstance.py
index 1799cca07..de0b3af82 100644
--- a/ipa-server/ipaserver/httpinstance.py
+++ b/ipa-server/ipaserver/httpinstance.py
@@ -29,6 +29,7 @@ import time
import service
import certs
import dsinstance
+import installutils
from ipa.ipautil import *
HTTPD_DIR = "/etc/httpd"
@@ -43,21 +44,6 @@ successfully change with the command:
Try updating the policycoreutils and selinux-policy packages.
"""
-def update_file(filename, orig, subst):
- if os.path.exists(filename):
- pattern = "%s" % re.escape(orig)
- p = re.compile(pattern)
- for line in fileinput.input(filename, inplace=1):
- if not p.search(line):
- sys.stdout.write(line)
- else:
- sys.stdout.write(p.sub(subst, line))
- fileinput.close()
- return 0
- else:
- print "File %s doesn't exist." % filename
- return 1
-
class HTTPInstance(service.Service):
def __init__(self):
service.Service.__init__(self, "httpd")
@@ -145,7 +131,7 @@ class HTTPInstance(service.Service):
def __set_mod_nss_port(self):
self.step("Setting mod_nss port to 443")
- if update_file(NSS_CONF, '8443', '443') != 0:
+ if installutils.update_file(NSS_CONF, '8443', '443') != 0:
print "Updating %s failed." % NSS_CONF
def __setup_ssl(self):
diff --git a/ipa-server/ipaserver/installutils.py b/ipa-server/ipaserver/installutils.py
index a403e8154..25cd1555c 100644
--- a/ipa-server/ipaserver/installutils.py
+++ b/ipa-server/ipaserver/installutils.py
@@ -21,6 +21,10 @@ import logging
import socket
import errno
import getpass
+import os
+import re
+import fileinput
+import sys
def get_fqdn():
fqdn = ""
@@ -105,4 +109,19 @@ def read_password(user):
print ""
return pwd
+def update_file(filename, orig, subst):
+ if os.path.exists(filename):
+ pattern = "%s" % re.escape(orig)
+ p = re.compile(pattern)
+ for line in fileinput.input(filename, inplace=1):
+ if not p.search(line):
+ sys.stdout.write(line)
+ else:
+ sys.stdout.write(p.sub(subst, line))
+ fileinput.close()
+ return 0
+ else:
+ print "File %s doesn't exist." % filename
+ return 1
+
--
cgit
From 6390db3502eaee385cb990eef723bc4f27a633c0 Mon Sep 17 00:00:00 2001
From: Rob Crittenden
Date: Wed, 12 Dec 2007 09:36:32 -0500
Subject: Add automatic browser configuration for kerberos SSO using
javascript. This uses the UniversalPreferencesWrite function to set the
browser preferences to allow negotiation and ticket forwarding in the IPA
domain. A self-signed certificate is generated to sign the javascript.
---
ipa-server/ipa-install/share/Makefile.am | 1 +
.../ipa-install/share/preferences.html.template | 33 +++++++++++++
ipa-server/ipaserver/certs.py | 56 +++++++++++++++++++---
ipa-server/ipaserver/httpinstance.py | 34 ++++++++++++-
ipa-server/xmlrpc-server/ipa.conf | 5 +-
ipa-server/xmlrpc-server/unauthorized.html | 14 ++++++
6 files changed, 134 insertions(+), 9 deletions(-)
create mode 100644 ipa-server/ipa-install/share/preferences.html.template
diff --git a/ipa-server/ipa-install/share/Makefile.am b/ipa-server/ipa-install/share/Makefile.am
index 36bb54e83..36837356d 100644
--- a/ipa-server/ipa-install/share/Makefile.am
+++ b/ipa-server/ipa-install/share/Makefile.am
@@ -19,6 +19,7 @@ app_DATA = \
krb.con.template \
krbrealm.con.template \
ntp.conf.server.template \
+ preferences.html.template \
radius.radiusd.conf.template \
referint-conf.ldif \
dna-posix.ldif \
diff --git a/ipa-server/ipa-install/share/preferences.html.template b/ipa-server/ipa-install/share/preferences.html.template
new file mode 100644
index 000000000..2d3684dcd
--- /dev/null
+++ b/ipa-server/ipa-install/share/preferences.html.template
@@ -0,0 +1,33 @@
+
+
+
+ Automatically set browser preferences
+
+
+
+
+
+
+
+
diff --git a/ipa-server/ipaserver/certs.py b/ipa-server/ipaserver/certs.py
index 77052c13e..eecfdf21c 100644
--- a/ipa-server/ipaserver/certs.py
+++ b/ipa-server/ipaserver/certs.py
@@ -77,6 +77,11 @@ class CertDB(object):
new_args = new_args + args
ipautil.run(new_args, stdin)
+ def run_signtool(self, args, stdin=None):
+ new_args = ["/usr/bin/signtool", "-d", self.secdir]
+ new_args = new_args + args
+ ipautil.run(new_args, stdin)
+
def create_noise_file(self):
ipautil.backup_file(self.noise_fname)
f = open(self.noise_fname, "w")
@@ -108,7 +113,7 @@ class CertDB(object):
self.run_certutil(["-S", "-n", self.cacert_name,
"-s", "cn=CAcert",
"-x",
- "-t", "CT,,",
+ "-t", "CT,,C",
"-m", self.next_serial(),
"-v", self.valid_months,
"-z", self.noise_fname,
@@ -130,7 +135,7 @@ class CertDB(object):
def load_cacert(self, cacert_fname):
self.run_certutil(["-A", "-n", self.cacert_name,
- "-t", "CT,CT,",
+ "-t", "CT,,C",
"-a",
"-i", cacert_fname])
@@ -139,7 +144,17 @@ class CertDB(object):
if not cdb:
cdb = self
self.request_cert(name)
- cdb.issue_cert(self.certreq_fname, self.certder_fname)
+ cdb.issue_server_cert(self.certreq_fname, self.certder_fname)
+ self.add_cert(self.certder_fname, nickname)
+ os.unlink(self.certreq_fname)
+ os.unlink(self.certder_fname)
+
+ def create_signing_cert(self, nickname, name, other_certdb=None):
+ cdb = other_certdb
+ if not cdb:
+ cdb = self
+ self.request_cert(name)
+ cdb.issue_signing_cert(self.certreq_fname, self.certder_fname)
self.add_cert(self.certder_fname, nickname)
os.unlink(self.certreq_fname)
os.unlink(self.certder_fname)
@@ -151,7 +166,7 @@ class CertDB(object):
"-z", self.noise_fname,
"-f", self.passwd_fname])
- def issue_cert(self, certreq_fname, cert_fname):
+ def issue_server_cert(self, certreq_fname, cert_fname):
p = subprocess.Popen(["/usr/bin/certutil",
"-d", self.secdir,
"-C", "-c", self.cacert_name,
@@ -179,8 +194,37 @@ class CertDB(object):
# n - not critical
p.stdin.write("2\n9\nn\n1\n9\nn\n")
p.wait()
-
-
+
+ def issue_signing_cert(self, certreq_fname, cert_fname):
+ p = subprocess.Popen(["/usr/bin/certutil",
+ "-d", self.secdir,
+ "-C", "-c", self.cacert_name,
+ "-i", certreq_fname,
+ "-o", cert_fname,
+ "-m", self.next_serial(),
+ "-v", self.valid_months,
+ "-f", self.passwd_fname,
+ "-1", "-5"],
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE)
+
+ # Bah - this sucks, but I guess it isn't possible to fully
+ # control this with command line arguments.
+ #
+ # What this is requesting is:
+ # -1 (Create key usage extension)
+ # 0 - Digital Signature
+ # 5 - Cert signing key
+ # 9 - done
+ # n - not critical
+ #
+ # -5 (Create netscape cert type extension)
+ # 3 - Object Signing
+ # 9 - done
+ # n - not critical
+ p.stdin.write("0\n5\n9\nn\n3\n9\nn\n")
+ p.wait()
+
def add_cert(self, cert_fname, nickname):
self.run_certutil(["-A", "-n", nickname,
"-t", "u,u,u",
diff --git a/ipa-server/ipaserver/httpinstance.py b/ipa-server/ipaserver/httpinstance.py
index de0b3af82..a131faedc 100644
--- a/ipa-server/ipaserver/httpinstance.py
+++ b/ipa-server/ipaserver/httpinstance.py
@@ -25,6 +25,7 @@ import pwd
import fileinput
import sys
import time
+import shutil
import service
import certs
@@ -49,9 +50,10 @@ class HTTPInstance(service.Service):
service.Service.__init__(self, "httpd")
def create_instance(self, realm, fqdn):
- self.sub_dict = { "REALM" : realm, "FQDN": fqdn }
self.fqdn = fqdn
self.realm = realm
+ self.domain = fqdn[fqdn.find(".")+1:]
+ self.sub_dict = { "REALM" : realm, "FQDN": fqdn, "DOMAIN" : self.domain }
self.start_creation(7, "Configuring the web interface")
@@ -60,6 +62,7 @@ class HTTPInstance(service.Service):
self.__configure_http()
self.__create_http_keytab()
self.__setup_ssl()
+ self.__setup_autoconfig()
self.step("restarting httpd")
self.restart()
@@ -141,4 +144,31 @@ class HTTPInstance(service.Service):
ds_ca.cur_serial = 2000
ca.create_from_cacert(ds_ca.cacert_fname)
ca.create_server_cert("Server-Cert", "cn=%s,ou=Apache Web Server" % self.fqdn, ds_ca)
-
+ ca.create_signing_cert("Signing-Cert", "cn=%s,ou=Signing Certificate,o=Identity Policy Audit" % self.fqdn, ds_ca)
+
+ def __setup_autoconfig(self):
+ prefs_txt = template_file(SHARE_DIR + "preferences.html.template", self.sub_dict)
+ prefs_fd = open("/usr/share/ipa/html/preferences.html", "w")
+ prefs_fd.write(prefs_txt)
+ prefs_fd.close()
+
+ # The signing cert is generated in __setup_ssl
+ ds_ca = certs.CertDB(dsinstance.config_dirname(self.realm))
+ ca = certs.CertDB(NSS_DIR)
+
+ # Publish the CA certificate
+ shutil.copy(ds_ca.cacert_fname, "/usr/share/ipa/html/ca.crt")
+ os.chmod("/usr/share/ipa/html/ca.crt", 0444)
+
+ try:
+ shutil.rmtree("/tmp/ipa")
+ except:
+ pass
+ os.mkdir("/tmp/ipa")
+ shutil.copy("/usr/share/ipa/html/preferences.html", "/tmp/ipa")
+
+ ca.run_signtool(["-k", "Signing-Cert",
+ "-Z", "/usr/share/ipa/html/configure.jar",
+ "-e", ".html",
+ "/tmp/ipa"])
+ shutil.rmtree("/tmp/ipa")
diff --git a/ipa-server/xmlrpc-server/ipa.conf b/ipa-server/xmlrpc-server/ipa.conf
index fbf26b67c..4e8bf528f 100644
--- a/ipa-server/xmlrpc-server/ipa.conf
+++ b/ipa-server/xmlrpc-server/ipa.conf
@@ -12,9 +12,12 @@ RewriteRule ^/(.*) http://$FQDN/$$1 [L,R=301]
# Redirect to the secure port if not displaying an error or retrieving
# configuration.
RewriteCond %{SERVER_PORT} !^443$$
-RewriteCond %{REQUEST_URI} !^/(errors|config)/
+RewriteCond %{REQUEST_URI} !^/(errors|config|favicon.ico)
RewriteRule ^/(.*) https://$FQDN/$$1 [L,R=301,NC]
+# This is required so the auto-configuration works with Firefox 2+
+AddType application/java-archive jar
+
AuthType Kerberos
AuthName "Kerberos Login"
diff --git a/ipa-server/xmlrpc-server/unauthorized.html b/ipa-server/xmlrpc-server/unauthorized.html
index 23a8d5c7d..e46ca538f 100644
--- a/ipa-server/xmlrpc-server/unauthorized.html
+++ b/ipa-server/xmlrpc-server/unauthorized.html
@@ -9,6 +9,20 @@ have configured your
browser correctly. If you are still unable to access
the IPA Web interface, please contact the helpdesk on for additional assistance.
+
+Import the IPA Certificate Authority.
+
+
+
+