7927c555e388e199ed3876b57a1f017c524acbd7
[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():
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 try:
91 ip = socket.gethostbyname(hostname)
92 except:
93 return hostname
94 if ip != "127.0.0.1":
95 return hostname
96
97
98 config_file = '/etc/certmaster/minion.conf'
99 config = read_config(config_file, MinionConfig)
100
101 server = config.certmaster
102 port = 51235
103
104 try:
105 s = socket.socket()
106 s.settimeout(5)
107 s.connect((server, port))
108 (intf, port) = s.getsockname()
109 hostname = socket.gethostbyaddr(intf)[0]
110 s.close()
111 except:
112 s.close()
113 raise
114
115 return hostname
116
117
118
119 def create_minion_keys():
120 # FIXME: paths should not be hard coded here, move to settings universally
121 config_file = '/etc/certmaster/minion.conf'
122 config = read_config(config_file, MinionConfig)
123 cert_dir = config.cert_dir
124 master_uri = 'http://%s:51235/' % config.certmaster
125 print "DEBUG: acquiring hostname"
126 hn = get_hostname()
127 print "DEBUG: hostname = %s\n" % hn
128
129 if hn is None:
130 raise codes.CMException("Could not determine a hostname other than localhost")
131
132 key_file = '%s/%s.pem' % (cert_dir, hn)
133 csr_file = '%s/%s.csr' % (cert_dir, hn)
134 cert_file = '%s/%s.cert' % (cert_dir, hn)
135 ca_cert_file = '%s/ca.cert' % cert_dir
136
137
138 if os.path.exists(cert_file) and os.path.exists(ca_cert_file):
139 print "DEBUG: err, no cert_file"
140 return
141
142 keypair = None
143 try:
144 if not os.path.exists(cert_dir):
145 os.makedirs(cert_dir)
146 if not os.path.exists(key_file):
147 keypair = certs.make_keypair(dest=key_file)
148 if not os.path.exists(csr_file):
149 if not keypair:
150 keypair = certs.retrieve_key_from_file(key_file)
151 csr = certs.make_csr(keypair, dest=csr_file)
152 except Exception, e:
153 traceback.print_exc()
154 raise codes.FuncException, "Could not create local keypair or csr for session"
155
156 result = False
157 log = logger.Logger().logger
158 while not result:
159 try:
160 print "DEBUG: submitting CSR to certmaster: %s" % master_uri
161 log.debug("submitting CSR to certmaster %s" % master_uri)
162 result, cert_string, ca_cert_string = submit_csr_to_master(csr_file, master_uri)
163 except socket.gaierror, e:
164 raise codes.FuncException, "Could not locate certmaster at %s" % master_uri
165
166 # logging here would be nice
167 if not result:
168 print "DEBUG: no response from certmaster, sleeping 10 seconds"
169 log.warning("no response from certmaster %s, sleeping 10 seconds" % master_uri)
170 time.sleep(10)
171
172
173 if result:
174 print "DEBUG: recieved certificate from certmaster"
175 log.debug("received certificate from certmaster %s, storing" % master_uri)
176 cert_fd = os.open(cert_file, os.O_RDWR|os.O_CREAT, 0644)
177 os.write(cert_fd, cert_string)
178 os.close(cert_fd)
179
180 ca_cert_fd = os.open(ca_cert_file, os.O_RDWR|os.O_CREAT, 0644)
181 os.write(ca_cert_fd, ca_cert_string)
182 os.close(ca_cert_fd)
183
184 def submit_csr_to_master(csr_file, master_uri):
185 """"
186 gets us our cert back from the certmaster.wait_for_cert() method
187 takes csr_file as path location and master_uri
188 returns Bool, str(cert), str(ca_cert)
189 """
190
191 fo = open(csr_file)
192 csr = fo.read()
193 s = xmlrpclib.ServerProxy(master_uri)
194
195 print "DEBUG: waiting for cert"
196 return s.wait_for_cert(csr)
197