Make hostname checking smarter.
[certmaster.git] / certmaster / utils.py
1 """
2 Copyright 2007-2008, Red Hat, Inc
3 see AUTHORS
4
5 This software may be freely redistributed under the terms of the GNU
6 general public license.
7
8 You should have received a copy of the GNU General Public License
9 along with this program; if not, write to the Free Software
10 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
11 """
12
13 import os
14 import string
15 import sys
16 import traceback
17 import xmlrpclib
18 import socket
19 import time
20 import glob
21
22 import codes
23 import certs
24 from config import read_config
25 from commonconfig import MinionConfig
26 import logger
27
28 # FIXME: module needs better pydoc
29
30
31 # FIXME: can remove this constant?
32 REMOTE_ERROR = "REMOTE_ERROR"
33
34
35 def trace_me():
36 x = traceback.extract_stack()
37 bar = string.join(traceback.format_list(x))
38 return bar
39
40 def daemonize(pidfile=None):
41 """
42 Daemonize this process with the UNIX double-fork trick.
43 Writes the new PID to the provided file name if not None.
44 """
45
46 print pidfile
47 pid = os.fork()
48 if pid > 0:
49 sys.exit(0)
50 os.setsid()
51 os.umask(0)
52 pid = os.fork()
53
54 if pid > 0:
55 if pidfile is not None:
56 open(pidfile, "w").write(str(pid))
57 sys.exit(0)
58
59 def nice_exception(etype, evalue, etb):
60 # FIXME: I believe we can remove this function
61 etype = str(etype)
62 lefti = etype.index("'") + 1
63 righti = etype.rindex("'")
64 nicetype = etype[lefti:righti]
65 nicestack = string.join(traceback.format_list(traceback.extract_tb(etb)))
66 return [ REMOTE_ERROR, nicetype, str(evalue), nicestack ]
67
68 def is_error(result):
69 # FIXME: I believe we can remove this function
70 if type(result) != list:
71 return False
72 if len(result) == 0:
73 return False
74 if result[0] == REMOTE_ERROR:
75 return True
76 return False
77
78 def get_hostname(talk_to_certmaster=True):
79 """
80 "localhost" is a lame hostname to use for a key, so try to get
81 a more meaningful hostname. We do this by connecting to the certmaster
82 and seeing what interface/ip it uses to make that connection, and looking
83 up the hostname for that.
84 """
85 # FIXME: this code ignores http proxies (which granted, we don't
86 # support elsewhere either. It also hardcodes the port number
87 # for the certmaster for now
88 hostname = None
89 hostname = socket.gethostname()
90 # print "DEBUG: HOSTNAME TRY1: %s" % hostname
91 try:
92 ip = socket.gethostbyname(hostname)
93 # print "DEBUG: IP TRY2: %s" % ip
94 except:
95 # print "DEBUG: ERROR: returning"
96 return hostname
97 if ip != "127.0.0.1":
98 # print "DEBUG: ERROR: returning 2"
99 return hostname
100
101 if talk_to_certmaster:
102 config_file = '/etc/certmaster/minion.conf'
103 config = read_config(config_file, MinionConfig)
104
105 server = config.certmaster
106 port = 51235
107
108 try:
109 s = socket.socket()
110 s.settimeout(5)
111 s.connect((server, port))
112 (intf, port) = s.getsockname()
113 remote_hostname = socket.gethostbyaddr(intf)[0]
114 if remote_hostname != "localhost":
115 hostname = remote_hostname
116 # print "DEBUG: HOSTNAME FROM CERTMASTER == %s" % hostname
117 s.close()
118 except:
119 s.close()
120 raise
121
122 # print "DEBUG: final hostname=%s" % hostname
123 return hostname
124
125
126 # FIXME: move to requestor module and also create a verbose mode
127 # prints to the screen for usage by /usr/bin/certmaster-request
128
129 def create_minion_keys():
130 # FIXME: paths should not be hard coded here, move to settings universally
131 config_file = '/etc/certmaster/minion.conf'
132 config = read_config(config_file, MinionConfig)
133 cert_dir = config.cert_dir
134 master_uri = 'http://%s:51235/' % config.certmaster
135 # print "DEBUG: acquiring hostname"
136 hn = get_hostname()
137 # print "DEBUG: hostname = %s\n" % hn
138
139 if hn is None:
140 raise codes.CMException("Could not determine a hostname other than localhost")
141
142 key_file = '%s/%s.pem' % (cert_dir, hn)
143 csr_file = '%s/%s.csr' % (cert_dir, hn)
144 cert_file = '%s/%s.cert' % (cert_dir, hn)
145 ca_cert_file = '%s/ca.cert' % cert_dir
146
147
148 if os.path.exists(cert_file) and os.path.exists(ca_cert_file):
149 # print "DEBUG: err, no cert_file"
150 return
151
152 keypair = None
153 try:
154 if not os.path.exists(cert_dir):
155 os.makedirs(cert_dir)
156 if not os.path.exists(key_file):
157 keypair = certs.make_keypair(dest=key_file)
158 if not os.path.exists(csr_file):
159 if not keypair:
160 keypair = certs.retrieve_key_from_file(key_file)
161 csr = certs.make_csr(keypair, dest=csr_file)
162 except Exception, e:
163 traceback.print_exc()
164 raise codes.CMException, "Could not create local keypair or csr for session"
165
166 result = False
167 log = logger.Logger().logger
168 while not result:
169 try:
170 # print "DEBUG: submitting CSR to certmaster: %s" % master_uri
171 log.debug("submitting CSR to certmaster %s" % master_uri)
172 result, cert_string, ca_cert_string = submit_csr_to_master(csr_file, master_uri)
173 except socket.gaierror, e:
174 raise codes.CMException, "Could not locate certmaster at %s" % master_uri
175
176 # logging here would be nice
177 if not result:
178 # print "DEBUG: no response from certmaster, sleeping 10 seconds"
179 log.warning("no response from certmaster %s, sleeping 10 seconds" % master_uri)
180 time.sleep(10)
181
182
183 if result:
184 # print "DEBUG: recieved certificate from certmaster"
185 log.debug("received certificate from certmaster %s, storing" % master_uri)
186 cert_fd = os.open(cert_file, os.O_RDWR|os.O_CREAT, 0644)
187 os.write(cert_fd, cert_string)
188 os.close(cert_fd)
189
190 ca_cert_fd = os.open(ca_cert_file, os.O_RDWR|os.O_CREAT, 0644)
191 os.write(ca_cert_fd, ca_cert_string)
192 os.close(ca_cert_fd)
193
194 def submit_csr_to_master(csr_file, master_uri):
195 """"
196 gets us our cert back from the certmaster.wait_for_cert() method
197 takes csr_file as path location and master_uri
198 returns Bool, str(cert), str(ca_cert)
199 """
200
201 fo = open(csr_file)
202 csr = fo.read()
203 s = xmlrpclib.ServerProxy(master_uri)
204
205 # print "DEBUG: waiting for cert"
206 return s.wait_for_cert(csr)
207