0000-12-31 18:09:24 -05:50
# Authors: Simo Sorce <ssorce@redhat.com>
#
# Copyright (C) 2007 Red Hat
# see file 'COPYING' for use and warranty information
#
2010-12-09 06:59:11 -06:00
# 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, either version 3 of the License, or
# (at your option) any later version.
0000-12-31 18:09:24 -05:50
#
# 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
2010-12-09 06:59:11 -06:00
# along with this program. If not, see <http://www.gnu.org/licenses/>.
0000-12-31 18:09:24 -05:50
#
import logging
import socket
import errno
import getpass
0000-12-31 18:09:24 -05:50
import os
import re
import fileinput
import sys
2008-03-03 15:10:06 -06:00
import struct
2008-03-27 14:33:06 -05:00
import fcntl
2010-12-01 10:22:56 -06:00
import netaddr
2007-12-18 12:03:34 -06:00
2009-02-05 14:03:08 -06:00
from ipapython import ipautil
from ipapython import dnsclient
0000-12-31 18:09:24 -05:50
def get_fqdn ( ) :
fqdn = " "
try :
fqdn = socket . getfqdn ( )
except :
try :
fqdn = socket . gethostname ( )
except :
fqdn = " "
return fqdn
2008-03-06 12:17:28 -06:00
2010-12-01 10:22:56 -06:00
def verify_dns_records ( host_name , responses , resaddr , family ) :
familykw = { ' ipv4 ' : {
' dns_type ' : dnsclient . DNS_T_A ,
' socket_family ' : socket . AF_INET ,
} ,
' ipv6 ' : {
' dns_type ' : dnsclient . DNS_T_AAAA ,
' socket_family ' : socket . AF_INET6 ,
} ,
}
family = family . lower ( )
if family not in familykw . keys ( ) :
raise RuntimeError ( " Unknown faimily %s \n " % family )
rec = None
for rsn in responses :
if rsn . dns_type == familykw [ family ] [ ' dns_type ' ] :
rec = rsn
break
if rec == None :
raise IOError ( errno . ENOENT ,
" Warning: Hostname ( %s ) not found in DNS " % host_name )
if family == ' ipv4 ' :
familykw [ family ] [ ' address ' ] = socket . inet_ntop ( socket . AF_INET ,
struct . pack ( ' !L ' , rec . rdata . address ) )
else :
familykw [ family ] [ ' address ' ] = socket . inet_ntop ( socket . AF_INET6 ,
struct . pack ( ' !16B ' , * rec . rdata . address ) )
# Check that DNS address is the same is address returned via standard glibc calls
dns_addr = netaddr . IPAddress ( familykw [ family ] [ ' address ' ] )
if dns_addr . format ( ) != resaddr :
raise RuntimeError ( " The network address %s does not match the DNS lookup %s . Check /etc/hosts and ensure that %s is the IP address for %s " % ( dns_addr . format ( ) , resaddr , dns_addr . format ( ) , host_name ) )
rs = dnsclient . query ( dns_addr . reverse_dns , dnsclient . DNS_C_IN , dnsclient . DNS_T_PTR )
if len ( rs ) == 0 :
raise RuntimeError ( " Cannot find Reverse Address for %s ( %s ) " % ( host_name , addr ) )
rev = None
for rsn in rs :
if rsn . dns_type == dnsclient . DNS_T_PTR :
rev = rsn
break
if rev == None :
raise RuntimeError ( " Cannot find Reverse Address for %s ( %s ) " % ( host_name , addr ) )
2008-03-30 19:00:43 -05:00
2010-12-01 10:22:56 -06:00
if rec . dns_name != rev . rdata . ptrdname :
raise RuntimeError ( " The DNS forward record %s does not match the reverse address %s " % ( rec . dns_name , rev . rdata . ptrdname ) )
def verify_fqdn ( host_name , no_host_dns = False ) :
0000-12-31 18:09:24 -05:50
if len ( host_name . split ( " . " ) ) < 2 or host_name == " localhost.localdomain " :
2011-01-10 16:16:25 -06:00
raise RuntimeError ( " Invalid hostname ' %s ' , must be fully-qualified. " % host_name )
0000-12-31 18:09:24 -05:50
2008-03-30 19:00:43 -05:00
try :
hostaddr = socket . getaddrinfo ( host_name , None )
except :
raise RuntimeError ( " Unable to resolve host name, check /etc/hosts or DNS name resolution " )
if len ( hostaddr ) == 0 :
raise RuntimeError ( " Unable to resolve host name, check /etc/hosts or DNS name resolution " )
for a in hostaddr :
if a [ 4 ] [ 0 ] == ' 127.0.0.1 ' or a [ 4 ] [ 0 ] == ' ::1 ' :
2008-05-08 16:13:15 -05:00
raise RuntimeError ( " The IPA Server hostname cannot resolve to localhost ( %s ). A routable IP address must be used. Check /etc/hosts to see if %s is an alias for %s " % ( a [ 4 ] [ 0 ] , host_name , a [ 4 ] [ 0 ] ) )
2008-03-30 19:00:43 -05:00
try :
2010-12-01 10:22:56 -06:00
resaddr = a [ 4 ] [ 0 ]
2008-03-30 19:00:43 -05:00
revname = socket . gethostbyaddr ( a [ 4 ] [ 0 ] ) [ 0 ]
except :
raise RuntimeError ( " Unable to resolve the reverse ip address, check /etc/hosts or DNS name resolution " )
if revname != host_name :
raise RuntimeError ( " The host name %s does not match the reverse lookup %s " % ( host_name , revname ) )
2008-09-16 21:18:11 -05:00
if no_host_dns :
print " Warning: skipping DNS resolution of host " , host_name
return
2008-03-30 19:00:43 -05:00
# Verify this is NOT a CNAME
rs = dnsclient . query ( host_name + " . " , dnsclient . DNS_C_IN , dnsclient . DNS_T_CNAME )
if len ( rs ) != 0 :
for rsn in rs :
if rsn . dns_type == dnsclient . DNS_T_CNAME :
2011-01-10 16:16:25 -06:00
raise RuntimeError ( " The IPA Server Hostname cannot be a CNAME, only A and AAAA names are allowed. " )
2008-03-30 19:00:43 -05:00
2010-12-01 10:22:56 -06:00
# Verify that it is a DNS A or AAAA record
2008-03-03 15:10:06 -06:00
rs = dnsclient . query ( host_name + " . " , dnsclient . DNS_C_IN , dnsclient . DNS_T_A )
2010-12-21 07:48:44 -06:00
if len ( rs ) > 0 :
2010-12-01 10:22:56 -06:00
verify_dns_records ( host_name , rs , resaddr , ' ipv4 ' )
2010-12-21 07:48:44 -06:00
return
2008-03-30 19:00:43 -05:00
2010-12-21 07:48:44 -06:00
rs = dnsclient . query ( host_name + " . " , dnsclient . DNS_C_IN , dnsclient . DNS_T_AAAA )
if len ( rs ) > 0 :
verify_dns_records ( host_name , rs , resaddr , ' ipv6 ' )
return
else :
print " Warning: Hostname ( %s ) not found in DNS " % host_name
2008-03-06 12:17:28 -06:00
2009-11-23 02:15:35 -06:00
def verify_ip_address ( ip ) :
is_ok = True
try :
socket . inet_pton ( socket . AF_INET , ip )
except :
try :
socket . inet_pton ( socket . AF_INET6 , ip )
except :
print " Unable to verify IP address "
is_ok = False
return is_ok
def read_ip_address ( host_name , fstore ) :
while True :
ip = ipautil . user_input ( " Please provide the IP address to be used for this host name " , allow_empty = False )
if ip == " 127.0.0.1 " or ip == " ::1 " :
print " The IPA Server can ' t use localhost as a valid IP "
continue
if verify_ip_address ( ip ) :
break
print " Adding [ " + ip + " " + host_name + " ] to your /etc/hosts file "
fstore . backup_file ( " /etc/hosts " )
hosts_fd = open ( ' /etc/hosts ' , ' r+ ' )
hosts_fd . seek ( 0 , 2 )
hosts_fd . write ( ip + ' \t ' + host_name + ' ' + host_name . split ( ' . ' ) [ 0 ] + ' \n ' )
hosts_fd . close ( )
return ip
def read_dns_forwarders ( ) :
addrs = [ ]
2010-04-06 12:47:21 -05:00
if ipautil . user_input ( " Do you want to configure DNS forwarders? " , True ) :
print " Enter the IP address of DNS forwarder to use, or press Enter to finish. "
2010-02-08 12:31:57 -06:00
while True :
ip = ipautil . user_input ( " Enter IP address for a DNS forwarder " ,
allow_empty = True )
if not ip :
break
if ip == " 127.0.0.1 " or ip == " ::1 " :
print " You cannot use localhost as a DNS forwarder "
continue
if not verify_ip_address ( ip ) :
2010-04-06 12:47:21 -05:00
print " DNS forwarder %s not added " % ip
2010-02-08 12:31:57 -06:00
continue
print " DNS forwarder %s added " % ip
addrs . append ( ip )
2009-11-23 02:15:35 -06:00
if not addrs :
print " No DNS forwarders configured "
return addrs
0000-12-31 18:09:24 -05:50
def port_available ( port ) :
""" Try to bind to a port on the wildcard host
Return 1 if the port is available
Return 0 if the port is in use
"""
rv = 1
try :
s = socket . socket ( socket . AF_INET , socket . SOCK_STREAM )
2010-04-29 16:33:18 -05:00
s . setsockopt ( socket . SOL_SOCKET , socket . SO_REUSEADDR , 1 )
2008-03-27 14:33:06 -05:00
fcntl . fcntl ( s , fcntl . F_SETFD , fcntl . FD_CLOEXEC )
0000-12-31 18:09:24 -05:50
s . bind ( ( ' ' , port ) )
s . close ( )
except socket . error , e :
if e [ 0 ] == errno . EADDRINUSE :
rv = 0
if rv :
try :
s = socket . socket ( socket . AF_INET6 , socket . SOCK_STREAM )
2010-04-29 16:33:18 -05:00
s . setsockopt ( socket . SOL_SOCKET , socket . SO_REUSEADDR , 1 )
2008-03-27 14:33:06 -05:00
fcntl . fcntl ( s , fcntl . F_SETFD , fcntl . FD_CLOEXEC )
0000-12-31 18:09:24 -05:50
s . bind ( ( ' ' , port ) )
s . close ( )
except socket . error , e :
if e [ 0 ] == errno . EADDRINUSE :
rv = 0
return rv
2009-11-23 02:18:25 -06:00
def standard_logging_setup ( log_filename , debug = False , filemode = ' w ' ) :
2008-05-20 03:19:19 -05:00
old_umask = os . umask ( 077 )
0000-12-31 18:09:24 -05:50
# Always log everything (i.e., DEBUG) to the log
# file.
logging . basicConfig ( level = logging . DEBUG ,
format = ' %(asctime)s %(levelname)s %(message)s ' ,
filename = log_filename ,
2009-11-23 02:18:25 -06:00
filemode = filemode )
2008-05-20 03:19:19 -05:00
os . umask ( old_umask )
0000-12-31 18:09:24 -05:50
console = logging . StreamHandler ( )
# If the debug option is set, also log debug messages to the console
if 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 )
2008-09-12 19:34:25 -05:00
def get_password ( prompt ) :
if os . isatty ( sys . stdin . fileno ( ) ) :
return getpass . getpass ( prompt )
else :
return sys . stdin . readline ( ) . rstrip ( )
2008-04-29 13:12:00 -05:00
def read_password ( user , confirm = True , validate = True ) :
0000-12-31 18:09:24 -05:50
correct = False
pwd = " "
while not correct :
2008-09-12 19:34:25 -05:00
pwd = get_password ( user + " password: " )
0000-12-31 18:09:24 -05:50
if not pwd :
continue
2008-04-29 13:12:00 -05:00
if validate and len ( pwd ) < 8 :
0000-12-31 18:09:24 -05:50
print " Password must be at least 8 characters long "
continue
2008-04-29 13:12:00 -05:00
if not confirm :
correct = True
continue
2008-09-12 19:34:25 -05:00
pwd_confirm = get_password ( " Password (confirm): " )
0000-12-31 18:09:24 -05:50
if pwd != pwd_confirm :
print " Password mismatch! "
print " "
else :
correct = True
print " "
return pwd
0000-12-31 18:09:24 -05:50
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
2009-04-17 16:17:31 -05:00
def set_directive ( filename , directive , value , quotes = True , separator = ' ' ) :
2008-07-11 10:34:29 -05:00
""" Set a name/value pair directive in a configuration file.
This has only been tested with nss . conf
"""
2009-04-17 16:17:31 -05:00
valueset = False
2008-07-11 10:34:29 -05:00
fd = open ( filename )
2009-08-11 16:08:09 -05:00
newfile = [ ]
2008-07-11 10:34:29 -05:00
for line in fd :
if directive in line :
2009-04-17 16:17:31 -05:00
valueset = True
if quotes :
2009-08-11 16:08:09 -05:00
newfile . append ( ' %s %s " %s " \n ' % ( directive , separator , value ) )
2009-04-17 16:17:31 -05:00
else :
2009-08-11 16:08:09 -05:00
newfile . append ( ' %s %s %s \n ' % ( directive , separator , value ) )
2008-07-11 10:34:29 -05:00
else :
2009-08-11 16:08:09 -05:00
newfile . append ( line )
2008-07-11 10:34:29 -05:00
fd . close ( )
2009-04-17 16:17:31 -05:00
if not valueset :
if quotes :
2009-08-11 16:08:09 -05:00
newfile . append ( ' %s %s " %s " \n ' % ( directive , separator , value ) )
2009-04-17 16:17:31 -05:00
else :
2009-08-11 16:08:09 -05:00
newfile . append ( ' %s %s %s \n ' % ( directive , separator , value ) )
2008-07-11 10:34:29 -05:00
fd = open ( filename , " w " )
2009-08-11 16:08:09 -05:00
fd . write ( " " . join ( newfile ) )
2008-07-11 10:34:29 -05:00
fd . close ( )
2009-08-11 16:08:09 -05:00
def get_directive ( filename , directive , separator = ' ' ) :
2009-04-17 16:17:31 -05:00
"""
A rather inefficient way to get a configuration directive .
"""
fd = open ( filename , " r " )
for line in fd :
if directive in line :
line = line . strip ( )
result = line . split ( separator , 1 ) [ 1 ]
result = result . strip ( ' " ' )
2010-05-27 10:58:31 -05:00
result = result . strip ( ' ' )
2009-04-17 16:17:31 -05:00
fd . close ( )
return result
fd . close ( )
return None
2007-12-18 12:03:34 -06:00
def kadmin ( command ) :
2010-08-31 15:59:27 -05:00
ipautil . run ( [ " kadmin.local " , " -q " , command ] )
2007-12-18 12:03:34 -06:00
def kadmin_addprinc ( principal ) :
kadmin ( " addprinc -randkey " + principal )
def kadmin_modprinc ( principal , options ) :
kadmin ( " modprinc " + options + " " + principal )
def create_keytab ( path , principal ) :
try :
if ipautil . file_exists ( path ) :
os . remove ( path )
except os . error :
logging . critical ( " Failed to remove %s . " % path )
kadmin ( " ktadd -k " + path + " " + principal )
0000-12-31 18:09:24 -05:50