1 #include "../burp.h"
2 #include "../asfd.h"
3 #include "../async.h"
4 #include "../cmd.h"
5 #include "../conf.h"
6 #include "../conffile.h"
7 #include "../fsops.h"
8 #include "../handy.h"
9 #include "../iobuf.h"
10 #include "../log.h"
11 #include "../run_script.h"
12 #include "cvss.h"
13 #include "ca.h"
14 
15 #ifdef HAVE_WIN32
16 // Want to avoid giving standard users access to the conf files, otherwise
17 // they can get access to all the backed up files, which may include files
18 // they shouldn't be able to get.
hack_windows_perms(const char * path)19 static void hack_windows_perms(const char *path)
20 {
21 	char cmd[512]="";
22 	snprintf(cmd, sizeof(cmd), "icacls.exe \"%s\" /inheritance:r /grant:r Administrators:F SYSTEM:F", path);
23 	system(cmd);
24 }
25 #endif
26 
generate_key_and_csr(struct asfd * asfd,struct conf ** confs,const char * csr_path)27 static int generate_key_and_csr(struct asfd *asfd,
28 	struct conf **confs, const char *csr_path)
29 {
30 	int a=0;
31 	const char *args[12];
32 	const char *ca_burp_ca=get_string(confs[OPT_CA_BURP_CA]);
33 	const char *cname=get_string(confs[OPT_CNAME]);
34 	const char *ssl_key=get_string(confs[OPT_SSL_KEY]);
35 
36 	logp("Generating SSL key and certificate signing request\n");
37 	logp("Running '%s --key --keypath %s --request --requestpath %s --name %s'\n", ca_burp_ca, ssl_key, csr_path, cname);
38 #ifdef HAVE_WIN32
39 	win32_enable_backup_privileges();
40 #else
41 	// FIX THIS
42 	signal(SIGPIPE, SIG_IGN);
43 #endif
44 	args[a++]=ca_burp_ca;
45 	args[a++]="--key";
46 	args[a++]="--keypath";
47 	args[a++]=ssl_key;
48 	args[a++]="--request";
49 	args[a++]="--requestpath";
50 	args[a++]=csr_path;
51 	args[a++]="--name";
52 	args[a++]=cname;
53 	args[a++]=NULL;
54 	if(run_script(asfd, args, NULL, confs, 1 /* wait */,
55 		0, 0 /* do not use logp - stupid openssl prints lots of dots
56 		        one at a time with no way to turn it off */))
57 	{
58 		logp("error when running '%s --key --keypath %s --request --requestpath %s --name %s'\n",
59 			ca_burp_ca, ssl_key, csr_path, cname);
60 		return -1;
61 	}
62 
63 #ifdef HAVE_WIN32
64 	hack_windows_perms(ssl_key);
65 	hack_windows_perms(csr_path);
66 #endif
67 
68 	return 0;
69 }
70 
71 /* Rewrite the conf file with the ssl_peer_cn value changed to what the
72    server told us it should be. */
rewrite_client_conf(struct conf ** confs)73 static int rewrite_client_conf(struct conf **confs)
74 {
75 	int ret=-1;
76 	char p[32]="";
77 	struct fzp *dp=NULL;
78 	struct fzp *sp=NULL;
79 	char *tmp=NULL;
80 	char buf[4096]="";
81 	const char *conffile=get_string(confs[OPT_CONFFILE]);
82 	const char *ssl_peer_cn=get_string(confs[OPT_SSL_PEER_CN]);
83 
84 	snprintf(p, sizeof(p), ".%d", getpid());
85 	if(!(tmp=prepend(conffile, p)))
86 		goto end;
87 	if(!(sp=fzp_open(conffile, "rb"))
88 	  || !(dp=fzp_open(tmp, "wb")))
89 		goto end;
90 
91 	while(fzp_gets(sp, buf, sizeof(buf)))
92 	{
93 		char *copy=NULL;
94 		char *field=NULL;
95 		char *value=NULL;
96 		int r=0;
97 
98 		if(!(copy=strdup_w(buf, __func__)))
99 			goto end;
100 		if(conf_get_pair(buf, &field, &value, &r)
101 		  || !field || !value
102 		  || strcmp(field, "ssl_peer_cn"))
103 		{
104 			fzp_printf(dp, "%s", copy);
105 			free_w(&copy);
106 			continue;
107 		}
108 		free_w(&copy);
109 #ifdef HAVE_WIN32
110 		fzp_printf(dp, "ssl_peer_cn = %s\r\n", ssl_peer_cn);
111 #else
112 		fzp_printf(dp, "ssl_peer_cn = %s\n", ssl_peer_cn);
113 #endif
114 	}
115 	fzp_close(&sp);
116 	if(fzp_close(&dp))
117 	{
118 		logp("error closing %s in %s\n", tmp, __func__);
119 		goto end;
120 	}
121 
122 	if(files_equal(conffile, tmp, 0/*compressed*/))
123 	{
124 		// No need to overwrite if there were no differences.
125 		ret=0;
126 		unlink(tmp);
127 		goto end;
128 	}
129 
130 	logp("Rewriting conf file: %s\n", conffile);
131 
132 	// Nasty race conditions going on here. However, the new config
133 	// file will get left behind, so at worse you will have to move
134 	// the new file into the correct place by hand. Or delete everything
135 	// and start again.
136 #ifdef HAVE_WIN32
137 	// Need to delete the destination, or Windows gets upset.
138 	unlink(conffile);
139 #endif
140 	if(do_rename(tmp, conffile)) goto end;
141 
142 	ret=0;
143 end:
144 	fzp_close(&sp);
145 	fzp_close(&dp);
146 	if(ret)
147 	{
148 		logp("%s with %s failed\n", __func__, conffile);
149 		unlink(tmp);
150 	}
151 	free_w(&tmp);
152 	return ret;
153 }
154 
csr_client_func(struct asfd * asfd,struct conf ** confs,void * param)155 static enum asl_ret csr_client_func(struct asfd *asfd,
156         struct conf **confs, __attribute__((unused)) void *param)
157 {
158 	if(strncmp_w(asfd->rbuf->buf, "csr ok:"))
159 	{
160 		iobuf_log_unexpected(asfd->rbuf, __func__);
161 		return ASL_END_ERROR;
162 	}
163 	// The server appends its name after 'csr ok:'
164 	if(set_string(confs[OPT_SSL_PEER_CN],
165 		asfd->rbuf->buf+strlen("csr ok:")))
166 			return ASL_END_ERROR;
167 	return ASL_END_OK;
168 }
169 
170 /* Return 1 for everything OK, signed and returned, -1 for error, 0 for
171    nothing done. */
ca_client_setup(struct asfd * asfd,struct conf ** confs)172 int ca_client_setup(struct asfd *asfd, struct conf **confs)
173 {
174 	int ret=-1;
175 	struct stat statp;
176 	char csr_path[256]="";
177 	char ssl_cert_tmp[512]="";
178 	char ssl_cert_ca_tmp[512]="";
179 	const char *ca_burp_ca=get_string(confs[OPT_CA_BURP_CA]);
180 	const char *ca_csr_dir=get_string(confs[OPT_CA_CSR_DIR]);
181 	const char *cname=get_string(confs[OPT_CNAME]);
182 	const char *ssl_key=get_string(confs[OPT_SSL_KEY]);
183 	const char *ssl_cert=get_string(confs[OPT_SSL_CERT]);
184 	const char *ssl_cert_ca=get_string(confs[OPT_SSL_CERT_CA]);
185 	struct cntr *cntr=get_cntr(confs);
186 
187 	/* Store setting, compared later to decide whether to rewrite the config */
188 	char *ssl_peer_cn_old=strdup_w(get_string(confs[OPT_SSL_PEER_CN]), __func__);
189 	if(!ssl_peer_cn_old) goto end;
190 
191 	// Do not continue if we have one of the following things not set.
192 	if(  !ca_burp_ca
193 	  || !ca_csr_dir
194 	  || !ssl_cert_ca
195 	  || !ssl_cert
196 	  || !ssl_key
197 	// Do not try to get a new certificate if we already have a key.
198 	  || !lstat(ssl_key, &statp))
199 	{
200 		if(asfd->write_str(asfd, CMD_GEN, "nocsr")
201 		  || asfd_read_expect(asfd, CMD_GEN, "nocsr ok"))
202 		{
203 			logp("problem reading from server nocsr\n");
204 			goto end;
205 		}
206 		logp("nocsr ok\n");
207 		ret=0;
208 		goto end;
209 	}
210 
211 	// Tell the server we want to do a signing request and store the servers name in ssl_peer_cn.
212 	if(asfd->write_str(asfd, CMD_GEN, "csr")
213 	  || asfd->simple_loop(asfd, confs, NULL, __func__, csr_client_func))
214 		goto end;
215 
216 	logp("Server will sign a certificate request\n");
217 
218 	// First need to generate a client key and a certificate signing
219 	// request.
220 	snprintf(csr_path, sizeof(csr_path), "%s/%s.csr", ca_csr_dir, cname);
221 	if(generate_key_and_csr(asfd, confs, csr_path)) goto end_cleanup;
222 
223 	// Then copy the csr to the server.
224 	if(send_a_file(asfd, csr_path, cntr)) goto end_cleanup;
225 
226 	snprintf(ssl_cert_tmp, sizeof(ssl_cert_tmp), "%s.%d",
227 		ssl_cert, getpid());
228 	snprintf(ssl_cert_ca_tmp, sizeof(ssl_cert_ca_tmp), "%s.%d",
229 		ssl_cert_ca, getpid());
230 
231 	// The server will then sign it, and give it back.
232 	if(receive_a_file(asfd, ssl_cert_tmp, cntr)) goto end_cleanup;
233 	// The server will also send the CA certificate.
234 	if(receive_a_file(asfd, ssl_cert_ca_tmp, cntr)) goto end_cleanup;
235 
236 	// Possible race condition - the rename can delete the destination
237 	// and then fail. Worse case, the user has to rename them by hand.
238 	if(do_rename(ssl_cert_tmp, ssl_cert)
239 	  || do_rename(ssl_cert_ca_tmp, ssl_cert_ca))
240 		goto end_cleanup;
241 
242 #ifdef HAVE_WIN32
243 	hack_windows_perms(ssl_cert);
244 	hack_windows_perms(ssl_cert_ca);
245 #endif
246 
247 	// Need to rewrite our configuration file to contain the server
248 	// name (ssl_peer_cn) if the name differs from the config file.
249 	if(strncmp_w(ssl_peer_cn_old, get_string(confs[OPT_SSL_PEER_CN])))
250 	{
251 	    if(rewrite_client_conf(confs)) goto end_cleanup;
252 	}
253 
254 	// My goodness, everything seems to have gone OK. Stand back!
255 	ret=1;
256 end_cleanup:
257 	if(ret<0)
258 	{
259 		// On error, remove any possibly newly created files, so that
260 		// this function might run again on another go.
261 		unlink(csr_path);
262 		unlink(ssl_key);
263 		unlink(ssl_cert);
264 		unlink(ssl_cert_ca);
265 		unlink(ssl_cert_tmp);
266 		unlink(ssl_cert_ca_tmp);
267 	}
268 end:
269 	if(ssl_peer_cn_old) free_w(&ssl_peer_cn_old);
270 	return ret;
271 }
272