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
2011-03-14 11:56:17 -05:00
import time
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
2011-05-27 10:05:45 -05:00
class HostnameLocalhost ( Exception ) :
pass
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 :
2011-01-25 11:46:26 -06:00
raise RuntimeError ( " Cannot find Reverse Address for %s ( %s ) " % ( host_name , dns_addr . format ( ) ) )
2010-12-01 10:22:56 -06:00
rev = None
for rsn in rs :
if rsn . dns_type == dnsclient . DNS_T_PTR :
rev = rsn
break
if rev == None :
2011-01-25 11:46:26 -06:00
raise RuntimeError ( " Cannot find Reverse Address for %s ( %s ) " % ( host_name , dns_addr . format ( ) ) )
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
2011-03-17 09:22:33 -05:00
if host_name != host_name . lower ( ) :
raise RuntimeError ( " Invalid hostname ' %s ' , must be lower-case. " % host_name )
2011-05-31 05:51:38 -05:00
if no_host_dns :
print " Warning: skipping DNS resolution of host " , host_name
return
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 ' :
2011-03-28 08:35:36 -05:00
raise RuntimeError ( " The IPA Server hostname must not 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 ) )
# 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 )
2011-01-31 08:30:43 -06:00
if len ( [ rec for rec in rs if rec . dns_type is not dnsclient . DNS_T_SOA ] ) > 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 )
2011-01-31 08:30:43 -06:00
if len ( [ rec for rec in rs if rec . dns_type is not dnsclient . DNS_T_SOA ] ) > 0 :
2010-12-21 07:48:44 -06:00
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
2011-05-27 13:17:22 -05:00
def parse_ip_address ( addr , match_local = True , parse_netmask = True ) :
2011-05-31 05:51:38 -05:00
ip = ipautil . CheckedIPAddress ( addr , match_local = match_local , parse_netmask = parse_netmask )
if match_local and not ip . is_local ( ) :
print " Warning: No network interface matches IP address %s " % addr
return ip
2011-05-27 13:17:22 -05:00
def verify_ip_address ( addr , match_local = True , parse_netmask = True ) :
2011-05-31 05:51:38 -05:00
ip = parse_ip_address ( addr , match_local , parse_netmask )
2009-11-23 02:15:35 -06:00
2011-02-10 14:47:45 -06:00
def record_in_hosts ( ip , host_name , file = " /etc/hosts " ) :
hosts = open ( file , ' r ' ) . readlines ( )
for line in hosts :
2011-02-15 10:51:18 -06:00
line = line . rstrip ( ' \n ' )
fields = line . partition ( ' # ' ) [ 0 ] . split ( )
if len ( fields ) == 0 :
2011-02-10 14:47:45 -06:00
continue
2011-02-15 10:51:18 -06:00
try :
hosts_ip = fields [ 0 ]
names = fields [ 1 : ]
if hosts_ip != ip :
continue
if host_name in names :
return True
except IndexError :
print " Warning: Erroneous line ' %s ' in %s " % ( line , file )
continue
2011-02-10 14:47:45 -06:00
return False
def add_record_to_hosts ( ip , host_name , file = " /etc/hosts " ) :
hosts_fd = open ( file , ' r+ ' )
hosts_fd . seek ( 0 , 2 )
hosts_fd . write ( ip + ' \t ' + host_name + ' ' + host_name . split ( ' . ' ) [ 0 ] + ' \n ' )
hosts_fd . close ( )
2009-11-23 02:15:35 -06:00
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 )
2011-05-31 05:51:38 -05:00
try :
ip_parsed = parse_ip_address ( ip )
except Exception , e :
print " Error: Invalid IP Address %s : %s " % ( ip , e )
continue
else :
2009-11-23 02:15:35 -06:00
break
2011-05-27 13:17:22 -05:00
ip = str ( ip_parsed )
2009-11-23 02:15:35 -06:00
print " Adding [ " + ip + " " + host_name + " ] to your /etc/hosts file "
fstore . backup_file ( " /etc/hosts " )
2011-02-10 14:47:45 -06:00
add_record_to_hosts ( ip , host_name )
2009-11-23 02:15:35 -06:00
2011-05-27 13:17:22 -05:00
return ip_parsed
2009-11-23 02:15:35 -06:00
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
2011-05-31 05:51:38 -05:00
try :
ip_parsed = parse_ip_address ( ip , match_local = False , parse_netmask = False )
except Exception , e :
print " Error: Invalid IP Address %s : %s " % ( ip , e )
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
2011-05-27 13:17:22 -05:00
addrs . append ( str ( ip_parsed ) )
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 ( )
2011-06-08 07:39:50 -05:00
def read_password ( user , confirm = True , validate = True , retry = True ) :
0000-12-31 18:09:24 -05:50
correct = False
pwd = " "
while not correct :
2011-06-08 07:39:50 -05:00
if not retry :
correct = True
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 "
2011-06-08 07:39:50 -05:00
pwd = " "
0000-12-31 18:09:24 -05:50
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 " "
2011-06-08 07:39:50 -05:00
pwd = " "
0000-12-31 18:09:24 -05:50
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 ) :
2011-02-10 21:13:42 -06:00
st = os . stat ( filename )
0000-12-31 18:09:24 -05:50
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 ( )
2011-02-10 21:13:42 -06:00
os . chown ( filename , st . st_uid , st . st_gid ) # reset perms
0000-12-31 18:09:24 -05:50
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.
2011-06-09 12:16:07 -05:00
A value of None means to drop the directive .
2008-07-11 10:34:29 -05:00
This has only been tested with nss . conf
"""
2009-04-17 16:17:31 -05:00
valueset = False
2011-02-10 21:13:42 -06:00
st = os . stat ( filename )
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
2011-06-09 12:16:07 -05:00
if value is not None :
if quotes :
newfile . append ( ' %s %s " %s " \n ' % ( directive , separator , value ) )
else :
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 :
2011-06-09 12:16:07 -05:00
if value is not None :
if quotes :
newfile . append ( ' %s %s " %s " \n ' % ( directive , separator , value ) )
else :
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 ( )
2011-02-10 21:13:42 -06:00
os . chown ( filename , st . st_uid , st . st_gid ) # reset perms
2008-07-11 10:34:29 -05:00
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
2011-03-14 11:56:17 -05:00
def wait_for_open_ports ( host , ports , timeout = 0 ) :
"""
Wait until the specified port ( s ) on the remote host are open . Timeout
in seconds may be specified to limit the wait .
"""
if not isinstance ( ports , ( tuple , list ) ) :
ports = [ ports ]
op_timeout = time . time ( ) + timeout
ipv6_failover = False
2011-06-09 12:16:07 -05:00
2011-03-14 11:56:17 -05:00
for port in ports :
while True :
try :
if ipv6_failover :
s = socket . socket ( socket . AF_INET6 , socket . SOCK_STREAM )
else :
s = socket . socket ( socket . AF_INET , socket . SOCK_STREAM )
s . connect ( ( host , port ) )
s . close ( )
break ;
except socket . error , e :
if e . errno == 111 : # 111: Connection refused
if timeout and time . time ( ) > op_timeout : # timeout exceeded
raise e
time . sleep ( 1 )
elif not ipv6_failover : # fallback to IPv6 connection
ipv6_failover = True
else :
raise e
2011-05-27 10:05:45 -05:00
def resolve_host ( host_name ) :
try :
addrinfos = socket . getaddrinfo ( host_name , None ,
socket . AF_UNSPEC , socket . SOCK_STREAM )
for ai in addrinfos :
ip = ai [ 4 ] [ 0 ]
if ip == " 127.0.0.1 " or ip == " ::1 " :
raise HostnameLocalhost ( " The hostname resolves to the localhost address " )
return addrinfos [ 0 ] [ 4 ] [ 0 ]
except :
return None