xref: /minix3/crypto/external/bsd/netpgp/dist/src/libpaa/libpaa.c (revision ebfedea0ce5bbe81e252ddf32d732e40fb633fae)
1*ebfedea0SLionel Sambuc /*-
2*ebfedea0SLionel Sambuc  * Copyright (c) 2010 Alistair Crooks <agc@NetBSD.org>
3*ebfedea0SLionel Sambuc  * All rights reserved.
4*ebfedea0SLionel Sambuc  *
5*ebfedea0SLionel Sambuc  * Redistribution and use in source and binary forms, with or without
6*ebfedea0SLionel Sambuc  * modification, are permitted provided that the following conditions
7*ebfedea0SLionel Sambuc  * are met:
8*ebfedea0SLionel Sambuc  * 1. Redistributions of source code must retain the above copyright
9*ebfedea0SLionel Sambuc  *    notice, this list of conditions and the following disclaimer.
10*ebfedea0SLionel Sambuc  * 2. Redistributions in binary form must reproduce the above copyright
11*ebfedea0SLionel Sambuc  *    notice, this list of conditions and the following disclaimer in the
12*ebfedea0SLionel Sambuc  *    documentation and/or other materials provided with the distribution.
13*ebfedea0SLionel Sambuc  *
14*ebfedea0SLionel Sambuc  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15*ebfedea0SLionel Sambuc  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16*ebfedea0SLionel Sambuc  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17*ebfedea0SLionel Sambuc  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18*ebfedea0SLionel Sambuc  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19*ebfedea0SLionel Sambuc  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20*ebfedea0SLionel Sambuc  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21*ebfedea0SLionel Sambuc  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22*ebfedea0SLionel Sambuc  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23*ebfedea0SLionel Sambuc  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24*ebfedea0SLionel Sambuc  */
25*ebfedea0SLionel Sambuc #include <sys/types.h>
26*ebfedea0SLionel Sambuc #include <sys/socket.h>
27*ebfedea0SLionel Sambuc 
28*ebfedea0SLionel Sambuc #include <arpa/inet.h>
29*ebfedea0SLionel Sambuc 
30*ebfedea0SLionel Sambuc #include <netinet6/in6.h>
31*ebfedea0SLionel Sambuc 
32*ebfedea0SLionel Sambuc #include <netdb.h>
33*ebfedea0SLionel Sambuc 
34*ebfedea0SLionel Sambuc #include <ifaddrs.h>
35*ebfedea0SLionel Sambuc #include <netpgp.h>
36*ebfedea0SLionel Sambuc #include <regex.h>
37*ebfedea0SLionel Sambuc #include <sha1.h>
38*ebfedea0SLionel Sambuc #include <stdio.h>
39*ebfedea0SLionel Sambuc #include <stdlib.h>
40*ebfedea0SLionel Sambuc #include <string.h>
41*ebfedea0SLionel Sambuc #include <time.h>
42*ebfedea0SLionel Sambuc #include <unistd.h>
43*ebfedea0SLionel Sambuc 
44*ebfedea0SLionel Sambuc #include "libpaa.h"
45*ebfedea0SLionel Sambuc #include "b64.h"
46*ebfedea0SLionel Sambuc 
47*ebfedea0SLionel Sambuc enum {
48*ebfedea0SLionel Sambuc 	MAX_DIGEST_SIZE	= 128
49*ebfedea0SLionel Sambuc };
50*ebfedea0SLionel Sambuc 
51*ebfedea0SLionel Sambuc /* create an area of random memory */
52*ebfedea0SLionel Sambuc static int
randomise(char * s,size_t size)53*ebfedea0SLionel Sambuc randomise(char *s, size_t size)
54*ebfedea0SLionel Sambuc {
55*ebfedea0SLionel Sambuc 	uint32_t	r;
56*ebfedea0SLionel Sambuc 	size_t		i;
57*ebfedea0SLionel Sambuc 
58*ebfedea0SLionel Sambuc 	for (i = 0 ; i < size ; i += sizeof(r)) {
59*ebfedea0SLionel Sambuc 		r = random();
60*ebfedea0SLionel Sambuc 		(void) memcpy(&s[i], &r, sizeof(r));
61*ebfedea0SLionel Sambuc 	}
62*ebfedea0SLionel Sambuc 	return i;
63*ebfedea0SLionel Sambuc }
64*ebfedea0SLionel Sambuc 
65*ebfedea0SLionel Sambuc /* generate a challenge */
66*ebfedea0SLionel Sambuc static int
genchallenge(paa_challenge_t * challenge,paa_server_info_t * server)67*ebfedea0SLionel Sambuc genchallenge(paa_challenge_t *challenge, paa_server_info_t *server)
68*ebfedea0SLionel Sambuc {
69*ebfedea0SLionel Sambuc 	time_t		 t;
70*ebfedea0SLionel Sambuc 	char		 digest[MAX_DIGEST_SIZE];
71*ebfedea0SLionel Sambuc 	char		 raw[PAA_CHALLENGE_SIZE * 2];
72*ebfedea0SLionel Sambuc 	int		 cc;
73*ebfedea0SLionel Sambuc 
74*ebfedea0SLionel Sambuc 	t = time(NULL);
75*ebfedea0SLionel Sambuc 	cc = snprintf(raw, sizeof(raw), "%s;%s;%lld;", challenge->realm, server->hostaddress, (int64_t)t);
76*ebfedea0SLionel Sambuc 	cc += randomise(&raw[cc], 64);	/* 64 is arbitrary */
77*ebfedea0SLionel Sambuc 	/* raw now has the raw-challenge in it */
78*ebfedea0SLionel Sambuc 	challenge->encc = b64encode(raw, (const unsigned)cc, challenge->encoded_challenge,
79*ebfedea0SLionel Sambuc 		sizeof(challenge->encoded_challenge), 0);
80*ebfedea0SLionel Sambuc 	cc += snprintf(&raw[cc], sizeof(raw) - cc, ";%.*s", server->secretc, server->secret);
81*ebfedea0SLionel Sambuc 	(void) SHA1Data((uint8_t *)raw, (unsigned)cc, digest);
82*ebfedea0SLionel Sambuc 	server->server_signaturec = b64encode(digest, (const unsigned)strlen(digest),
83*ebfedea0SLionel Sambuc 		server->server_signature, sizeof(server->server_signature), (int)0);
84*ebfedea0SLionel Sambuc 	/* raw has raw-challenge ; server-secret-value, i.e. raw-server-signature */
85*ebfedea0SLionel Sambuc 	challenge->challengec = snprintf(challenge->challenge, sizeof(challenge->challenge),
86*ebfedea0SLionel Sambuc 		"%.*s;%.*s", server->server_signaturec, server->server_signature,
87*ebfedea0SLionel Sambuc 		challenge->encc, challenge->encoded_challenge);
88*ebfedea0SLionel Sambuc 	return challenge->challengec;
89*ebfedea0SLionel Sambuc }
90*ebfedea0SLionel Sambuc 
91*ebfedea0SLionel Sambuc /* fill in the identity information in the response */
92*ebfedea0SLionel Sambuc static int
fill_identity(paa_identity_t * id,char * response,char * raw_challenge)93*ebfedea0SLionel Sambuc fill_identity(paa_identity_t *id, char *response, char *raw_challenge)
94*ebfedea0SLionel Sambuc {
95*ebfedea0SLionel Sambuc 	regmatch_t	matches[10];
96*ebfedea0SLionel Sambuc 	regex_t		response_re;
97*ebfedea0SLionel Sambuc 	regex_t		id_re;
98*ebfedea0SLionel Sambuc 	char		t[32];
99*ebfedea0SLionel Sambuc 
100*ebfedea0SLionel Sambuc 	/* id="userid" */
101*ebfedea0SLionel Sambuc 	(void) regcomp(&id_re, "id=\"([^\"]+)\"", REG_EXTENDED);
102*ebfedea0SLionel Sambuc 	if (regexec(&id_re, response, 10, matches, 0) != 0) {
103*ebfedea0SLionel Sambuc 		(void) fprintf(stderr, "No identity information found\n");
104*ebfedea0SLionel Sambuc 		return 0;
105*ebfedea0SLionel Sambuc 	}
106*ebfedea0SLionel Sambuc 	(void) snprintf(id->userid, sizeof(id->userid), "%.*s",
107*ebfedea0SLionel Sambuc 		(int)(matches[1].rm_eo - matches[1].rm_so),
108*ebfedea0SLionel Sambuc 		&response[(int)matches[1].rm_so]);
109*ebfedea0SLionel Sambuc 	/* realm;ip;timestamp;seed */
110*ebfedea0SLionel Sambuc 	(void) regcomp(&response_re, "([^;]+);([^;]+);([^;]+);(.*)", REG_EXTENDED);
111*ebfedea0SLionel Sambuc 	if (regexec(&response_re, raw_challenge, 10, matches, 0) != 0) {
112*ebfedea0SLionel Sambuc 		(void) fprintf(stderr, "No identity information found\n");
113*ebfedea0SLionel Sambuc 		return 0;
114*ebfedea0SLionel Sambuc 	}
115*ebfedea0SLionel Sambuc 	(void) snprintf(id->realm, sizeof(id->realm), "%.*s",
116*ebfedea0SLionel Sambuc 		(int)(matches[1].rm_eo - matches[1].rm_so),
117*ebfedea0SLionel Sambuc 		&raw_challenge[(int)matches[1].rm_so]);
118*ebfedea0SLionel Sambuc 	(void) snprintf(id->client, sizeof(id->client), "%.*s",
119*ebfedea0SLionel Sambuc 		(int)(matches[2].rm_eo - matches[2].rm_so),
120*ebfedea0SLionel Sambuc 		&raw_challenge[(int)matches[2].rm_so]);
121*ebfedea0SLionel Sambuc 	(void) snprintf(t, sizeof(t), "%.*s",
122*ebfedea0SLionel Sambuc 		(int)(matches[3].rm_eo - matches[3].rm_so),
123*ebfedea0SLionel Sambuc 		&raw_challenge[(int)matches[3].rm_so]);
124*ebfedea0SLionel Sambuc 	id->timestamp = strtoll(t, NULL, 10);
125*ebfedea0SLionel Sambuc 	return 1;
126*ebfedea0SLionel Sambuc }
127*ebfedea0SLionel Sambuc 
128*ebfedea0SLionel Sambuc /***************************************************************************/
129*ebfedea0SLionel Sambuc /* exported functions start here */
130*ebfedea0SLionel Sambuc /***************************************************************************/
131*ebfedea0SLionel Sambuc 
132*ebfedea0SLionel Sambuc /* initialise the server info */
133*ebfedea0SLionel Sambuc int
paa_server_init(paa_server_info_t * server,unsigned secretsize)134*ebfedea0SLionel Sambuc paa_server_init(paa_server_info_t *server, unsigned secretsize)
135*ebfedea0SLionel Sambuc {
136*ebfedea0SLionel Sambuc 	struct sockaddr_in6	*sin6;
137*ebfedea0SLionel Sambuc 	struct sockaddr_in	*sin;
138*ebfedea0SLionel Sambuc 	struct ifaddrs		*addrs;
139*ebfedea0SLionel Sambuc 	char			 host[512];
140*ebfedea0SLionel Sambuc 
141*ebfedea0SLionel Sambuc 	if (getifaddrs(&addrs) < 0) {
142*ebfedea0SLionel Sambuc 		(void) fprintf(stderr, "can't getifaddrs\n");
143*ebfedea0SLionel Sambuc 		return 0;
144*ebfedea0SLionel Sambuc 	}
145*ebfedea0SLionel Sambuc 	for ( ; addrs ; addrs = addrs->ifa_next) {
146*ebfedea0SLionel Sambuc 		if (addrs->ifa_addr->sa_family == AF_INET) {
147*ebfedea0SLionel Sambuc 			sin = (struct sockaddr_in *)(void *)addrs->ifa_addr;
148*ebfedea0SLionel Sambuc 			(void) snprintf(server->hostaddress, sizeof(server->hostaddress), "%s",
149*ebfedea0SLionel Sambuc 				inet_ntoa(sin->sin_addr));
150*ebfedea0SLionel Sambuc 			break;
151*ebfedea0SLionel Sambuc 		}
152*ebfedea0SLionel Sambuc 		if (addrs->ifa_addr->sa_family == AF_INET6) {
153*ebfedea0SLionel Sambuc 			sin6 = (struct sockaddr_in6 *)(void *)addrs->ifa_addr;
154*ebfedea0SLionel Sambuc 			(void) getnameinfo((const struct sockaddr *)(void *)sin6,
155*ebfedea0SLionel Sambuc 				(unsigned)sin6->sin6_len,
156*ebfedea0SLionel Sambuc 				server->hostaddress, sizeof(server->hostaddress),
157*ebfedea0SLionel Sambuc 				NULL, 0, NI_NUMERICHOST);
158*ebfedea0SLionel Sambuc 			break;
159*ebfedea0SLionel Sambuc 		}
160*ebfedea0SLionel Sambuc 	}
161*ebfedea0SLionel Sambuc 	if (addrs == NULL) {
162*ebfedea0SLionel Sambuc 		if (gethostname(host, sizeof(host)) < 0) {
163*ebfedea0SLionel Sambuc 			(void) fprintf(stderr, "can't get hostname\n");
164*ebfedea0SLionel Sambuc 			return 0;
165*ebfedea0SLionel Sambuc 		}
166*ebfedea0SLionel Sambuc 		(void) snprintf(server->hostaddress, sizeof(server->hostaddress), "%s", host);
167*ebfedea0SLionel Sambuc 	}
168*ebfedea0SLionel Sambuc 	if ((server->secret = calloc(1, server->secretc = secretsize)) == NULL) {
169*ebfedea0SLionel Sambuc 		(void) fprintf(stderr, "can't allocate server secret\n");
170*ebfedea0SLionel Sambuc 		return 0;
171*ebfedea0SLionel Sambuc 	}
172*ebfedea0SLionel Sambuc 	server->secretc = randomise(server->secret, secretsize);
173*ebfedea0SLionel Sambuc 	return 1;
174*ebfedea0SLionel Sambuc }
175*ebfedea0SLionel Sambuc 
176*ebfedea0SLionel Sambuc /*
177*ebfedea0SLionel Sambuc     challenge         = "PubKey.v1" pubkey-challenge
178*ebfedea0SLionel Sambuc 
179*ebfedea0SLionel Sambuc     pubkey-challenge  = 1#( realm | [domain] | challenge )
180*ebfedea0SLionel Sambuc 
181*ebfedea0SLionel Sambuc     realm             = "realm" "=" quoted-string
182*ebfedea0SLionel Sambuc     domain            = "domain" "=" <"> URI ( 1*SP URI ) <">
183*ebfedea0SLionel Sambuc     URI               = absoluteURI | abs_path
184*ebfedea0SLionel Sambuc     challenge         = "challenge" "=" quoted-string
185*ebfedea0SLionel Sambuc */
186*ebfedea0SLionel Sambuc 
187*ebfedea0SLionel Sambuc /* called from server to send the challenge */
188*ebfedea0SLionel Sambuc int
paa_format_challenge(paa_challenge_t * challenge,paa_server_info_t * server,char * buf,size_t size)189*ebfedea0SLionel Sambuc paa_format_challenge(paa_challenge_t *challenge, paa_server_info_t *server, char *buf, size_t size)
190*ebfedea0SLionel Sambuc {
191*ebfedea0SLionel Sambuc 	int	cc;
192*ebfedea0SLionel Sambuc 
193*ebfedea0SLionel Sambuc 	if (challenge->realm == NULL) {
194*ebfedea0SLionel Sambuc 		(void) fprintf(stderr, "paa_format_challenge: no realm information\n");
195*ebfedea0SLionel Sambuc 		return 0;
196*ebfedea0SLionel Sambuc 	}
197*ebfedea0SLionel Sambuc 	cc = snprintf(buf, size, "401 Unauthorized\r\nWWW-Authenticate: PubKey.v1\r\n");
198*ebfedea0SLionel Sambuc 	(void) genchallenge(challenge, server);
199*ebfedea0SLionel Sambuc 	cc += snprintf(&buf[cc], size - cc, "    challenge=\"%s\"", challenge->challenge);
200*ebfedea0SLionel Sambuc 	if (challenge->realm) {
201*ebfedea0SLionel Sambuc 		cc += snprintf(&buf[cc], size - cc, ",\r\n    realm=\"%s\"", challenge->realm);
202*ebfedea0SLionel Sambuc 	}
203*ebfedea0SLionel Sambuc 	if (challenge->domain) {
204*ebfedea0SLionel Sambuc 		cc += snprintf(&buf[cc], size - cc, ",\r\n    domain=\"%s\"", challenge->domain);
205*ebfedea0SLionel Sambuc 	}
206*ebfedea0SLionel Sambuc 	cc += snprintf(&buf[cc], size - cc, "\r\n");
207*ebfedea0SLionel Sambuc 	return cc;
208*ebfedea0SLionel Sambuc }
209*ebfedea0SLionel Sambuc 
210*ebfedea0SLionel Sambuc /*
211*ebfedea0SLionel Sambuc     credentials          = "PubKey.v1" privkey-credentials
212*ebfedea0SLionel Sambuc 
213*ebfedea0SLionel Sambuc     privkey-credentials  = 1#( identifier | realm | challenge | signature )
214*ebfedea0SLionel Sambuc 
215*ebfedea0SLionel Sambuc     identifier           = "id" "=" identifier-value
216*ebfedea0SLionel Sambuc     identifier-value     = quoted-string
217*ebfedea0SLionel Sambuc     challenge            = "challenge" "=" challenge-value
218*ebfedea0SLionel Sambuc     challenge-value      = quoted-string
219*ebfedea0SLionel Sambuc     signature            = "signature" "=" signature-value
220*ebfedea0SLionel Sambuc     signature-value      = quoted-string
221*ebfedea0SLionel Sambuc */
222*ebfedea0SLionel Sambuc 
223*ebfedea0SLionel Sambuc /* called from client to respond to the challenge */
224*ebfedea0SLionel Sambuc int
paa_format_response(paa_response_t * response,netpgp_t * netpgp,char * in,char * out,size_t outsize)225*ebfedea0SLionel Sambuc paa_format_response(paa_response_t *response, netpgp_t *netpgp, char *in, char *out, size_t outsize)
226*ebfedea0SLionel Sambuc {
227*ebfedea0SLionel Sambuc 	regmatch_t	matches[10];
228*ebfedea0SLionel Sambuc 	regex_t		r;
229*ebfedea0SLionel Sambuc 	char		challenge[2048 * 2];
230*ebfedea0SLionel Sambuc 	char		base64_signature[2048 * 2];
231*ebfedea0SLionel Sambuc 	char		sig[2048];
232*ebfedea0SLionel Sambuc 	int		challengec;
233*ebfedea0SLionel Sambuc 	int		sig64c;
234*ebfedea0SLionel Sambuc 	int		sigc;
235*ebfedea0SLionel Sambuc 	int		outc;
236*ebfedea0SLionel Sambuc 
237*ebfedea0SLionel Sambuc 	if (response->realm == NULL) {
238*ebfedea0SLionel Sambuc 		(void) fprintf(stderr, "paa_format_response: no realm information\n");
239*ebfedea0SLionel Sambuc 		return 0;
240*ebfedea0SLionel Sambuc 	}
241*ebfedea0SLionel Sambuc 	(void) regcomp(&r, "challenge=\"([^\"]+)\"", REG_EXTENDED);
242*ebfedea0SLionel Sambuc 	if (regexec(&r, in, 10, matches, 0) != 0) {
243*ebfedea0SLionel Sambuc 		(void) fprintf(stderr, "no signature found\n");
244*ebfedea0SLionel Sambuc 		return 0;
245*ebfedea0SLionel Sambuc 	}
246*ebfedea0SLionel Sambuc 	challengec = snprintf(challenge, sizeof(challenge), "%.*s",
247*ebfedea0SLionel Sambuc 		(int)(matches[1].rm_eo - matches[1].rm_so), &in[(int)matches[1].rm_so]);
248*ebfedea0SLionel Sambuc 	/* read challenge string */
249*ebfedea0SLionel Sambuc 	outc = snprintf(out, outsize, "Authorization: PubKey.v1\r\n");
250*ebfedea0SLionel Sambuc 	response->userid = netpgp_getvar(netpgp, "userid");
251*ebfedea0SLionel Sambuc 	outc += snprintf(&out[outc], outsize - outc, "    id=\"%s\"", response->userid);
252*ebfedea0SLionel Sambuc 	outc += snprintf(&out[outc], outsize - outc, ",\r\n    challenge=\"%s\"", challenge);
253*ebfedea0SLionel Sambuc 	outc += snprintf(&out[outc], outsize - outc, ",\r\n    realm=\"%s\"", response->realm);
254*ebfedea0SLionel Sambuc 	/* set up response */
255*ebfedea0SLionel Sambuc 	(void) memset(sig, 0x0, sizeof(sig));
256*ebfedea0SLionel Sambuc 	(void) snprintf(sig, sizeof(sig), "%s;%s;%s;", response->userid, response->realm, challenge);
257*ebfedea0SLionel Sambuc 	sigc = netpgp_sign_memory(netpgp, response->userid, challenge,
258*ebfedea0SLionel Sambuc 		(unsigned)challengec, sig, sizeof(sig), 0, 0);
259*ebfedea0SLionel Sambuc 	sig64c = b64encode(sig, (const unsigned)sigc, base64_signature,
260*ebfedea0SLionel Sambuc 		sizeof(base64_signature), (int)0);
261*ebfedea0SLionel Sambuc 	outc += snprintf(&out[outc], outsize - outc, ",\r\n    signature=\"%.*s\"", sig64c, base64_signature);
262*ebfedea0SLionel Sambuc 	return outc;
263*ebfedea0SLionel Sambuc }
264*ebfedea0SLionel Sambuc 
265*ebfedea0SLionel Sambuc /* called from server to check the response to the challenge */
266*ebfedea0SLionel Sambuc int
paa_check_response(paa_challenge_t * challenge,paa_identity_t * id,netpgp_t * netpgp,char * response)267*ebfedea0SLionel Sambuc paa_check_response(paa_challenge_t *challenge, paa_identity_t *id, netpgp_t *netpgp, char *response)
268*ebfedea0SLionel Sambuc {
269*ebfedea0SLionel Sambuc 	regmatch_t	matches[10];
270*ebfedea0SLionel Sambuc 	regex_t		challenge_regex;
271*ebfedea0SLionel Sambuc 	regex_t		signature_regex;
272*ebfedea0SLionel Sambuc 	regex_t		realm_regex;
273*ebfedea0SLionel Sambuc 	time_t		t;
274*ebfedea0SLionel Sambuc 	char		encoded_challenge[512];
275*ebfedea0SLionel Sambuc 	char		raw_challenge[512];
276*ebfedea0SLionel Sambuc 	char		verified[2048];
277*ebfedea0SLionel Sambuc 	char		realm[128];
278*ebfedea0SLionel Sambuc 	char		buf[2048];
279*ebfedea0SLionel Sambuc 	int		bufc;
280*ebfedea0SLionel Sambuc 
281*ebfedea0SLionel Sambuc 	/* grab the signed text from the response */
282*ebfedea0SLionel Sambuc 	(void) regcomp(&signature_regex, "signature=\"([^\"]+)\"", REG_EXTENDED);
283*ebfedea0SLionel Sambuc 	if (regexec(&signature_regex, response, 10, matches, 0) != 0) {
284*ebfedea0SLionel Sambuc 		(void) fprintf(stderr, "paa_check: no signature found\n");
285*ebfedea0SLionel Sambuc 		return 0;
286*ebfedea0SLionel Sambuc 	}
287*ebfedea0SLionel Sambuc 	/* atob the signature itself */
288*ebfedea0SLionel Sambuc 	bufc = b64decode(&response[(int)matches[1].rm_so],
289*ebfedea0SLionel Sambuc 		(size_t)(matches[1].rm_eo - matches[1].rm_so), buf, sizeof(buf));
290*ebfedea0SLionel Sambuc 	/* verify the signature */
291*ebfedea0SLionel Sambuc 	(void) memset(verified, 0x0, sizeof(verified));
292*ebfedea0SLionel Sambuc 	if (netpgp_verify_memory(netpgp, buf, (const unsigned)bufc, verified, sizeof(verified), 0) <= 0) {
293*ebfedea0SLionel Sambuc 		(void) fprintf(stderr, "paa_check: signature cannot be verified\n");
294*ebfedea0SLionel Sambuc 		return 0;
295*ebfedea0SLionel Sambuc 	}
296*ebfedea0SLionel Sambuc 	/* we check the complete signed text against our challenge */
297*ebfedea0SLionel Sambuc 	if (strcmp(challenge->challenge, verified) != 0) {
298*ebfedea0SLionel Sambuc 		(void) fprintf(stderr, "paa_check: signature does not match\n");
299*ebfedea0SLionel Sambuc 		return 0;
300*ebfedea0SLionel Sambuc 	}
301*ebfedea0SLionel Sambuc 	(void) regcomp(&challenge_regex, "^([^;]+);(.+)", REG_EXTENDED);
302*ebfedea0SLionel Sambuc 	if (regexec(&challenge_regex, verified, 10, matches, 0) != 0) {
303*ebfedea0SLionel Sambuc 		(void) fprintf(stderr, "paa_check: no 2 parts to challenge\n");
304*ebfedea0SLionel Sambuc 		return 0;
305*ebfedea0SLionel Sambuc 	}
306*ebfedea0SLionel Sambuc 	/* we know server signature matches from comparison on whole challenge above */
307*ebfedea0SLionel Sambuc 	(void) snprintf(encoded_challenge, sizeof(encoded_challenge), "%.*s",
308*ebfedea0SLionel Sambuc 		(int)(matches[2].rm_eo - matches[2].rm_so), &verified[(int)matches[2].rm_so]);
309*ebfedea0SLionel Sambuc 	(void) b64decode(&verified[(int)matches[2].rm_so],
310*ebfedea0SLionel Sambuc 		(const unsigned)(matches[2].rm_eo - matches[2].rm_so),
311*ebfedea0SLionel Sambuc 		raw_challenge, sizeof(raw_challenge));
312*ebfedea0SLionel Sambuc 	if (!fill_identity(id, response, raw_challenge)) {
313*ebfedea0SLionel Sambuc 		(void) fprintf(stderr, "paa_check: identity problems\n");
314*ebfedea0SLionel Sambuc 		return 0;
315*ebfedea0SLionel Sambuc 
316*ebfedea0SLionel Sambuc 	}
317*ebfedea0SLionel Sambuc 	/* check realm info in authentication header matches signed realm */
318*ebfedea0SLionel Sambuc 	(void) regcomp(&realm_regex, "realm=\"([^\"]+)\"", REG_EXTENDED);
319*ebfedea0SLionel Sambuc 	if (regexec(&realm_regex, response, 10, matches, 0) != 0) {
320*ebfedea0SLionel Sambuc 		(void) fprintf(stderr, "paa_check: no realm found\n");
321*ebfedea0SLionel Sambuc 		return 0;
322*ebfedea0SLionel Sambuc 	}
323*ebfedea0SLionel Sambuc 	(void) snprintf(realm, sizeof(realm), "%.*s",
324*ebfedea0SLionel Sambuc 		(int)(matches[1].rm_eo - matches[1].rm_so),
325*ebfedea0SLionel Sambuc 		&response[(int)matches[1].rm_so]);
326*ebfedea0SLionel Sambuc 	if (strcmp(id->realm, realm) != 0) {
327*ebfedea0SLionel Sambuc 		(void) fprintf(stderr, "paa_check: realm mismatch: signed realm '%s' vs '%s'\n",
328*ebfedea0SLionel Sambuc 			id->realm, realm);
329*ebfedea0SLionel Sambuc 		return 0;
330*ebfedea0SLionel Sambuc 	}
331*ebfedea0SLionel Sambuc 	/* check timestamp is within bounds */
332*ebfedea0SLionel Sambuc 	t = time(NULL);
333*ebfedea0SLionel Sambuc 	if (id->timestamp < t - (3 * 60)) {
334*ebfedea0SLionel Sambuc 		(void) fprintf(stderr, "paa_check: timestamp check: %lld seconds ago\n",
335*ebfedea0SLionel Sambuc 			t - id->timestamp);
336*ebfedea0SLionel Sambuc 		return 0;
337*ebfedea0SLionel Sambuc 	}
338*ebfedea0SLionel Sambuc 	if (id->timestamp > t + (3 * 60)) {
339*ebfedea0SLionel Sambuc 		(void) fprintf(stderr, "paa_check: timestamp check: %lld seconds in future\n",
340*ebfedea0SLionel Sambuc 			id->timestamp - t);
341*ebfedea0SLionel Sambuc 		return 0;
342*ebfedea0SLionel Sambuc 	}
343*ebfedea0SLionel Sambuc 	return 1;
344*ebfedea0SLionel Sambuc }
345*ebfedea0SLionel Sambuc 
346*ebfedea0SLionel Sambuc /* print identity details on a stream */
347*ebfedea0SLionel Sambuc int
paa_print_identity(FILE * fp,paa_identity_t * id)348*ebfedea0SLionel Sambuc paa_print_identity(FILE *fp, paa_identity_t *id)
349*ebfedea0SLionel Sambuc {
350*ebfedea0SLionel Sambuc 	(void) fprintf(fp, "\tuserid\t%s\n\tclient\t%s\n\trealm\t%s\n\ttime\t%.24s\n",
351*ebfedea0SLionel Sambuc 		id->userid,
352*ebfedea0SLionel Sambuc 		id->client,
353*ebfedea0SLionel Sambuc 		id->realm,
354*ebfedea0SLionel Sambuc 		ctime(&id->timestamp));
355*ebfedea0SLionel Sambuc 	return 1;
356*ebfedea0SLionel Sambuc }
357*ebfedea0SLionel Sambuc 
358*ebfedea0SLionel Sambuc /* utility function to write a string to a file */
359*ebfedea0SLionel Sambuc int
paa_write_file(const char * f,char * s,unsigned cc)360*ebfedea0SLionel Sambuc paa_write_file(const char *f, char *s, unsigned cc)
361*ebfedea0SLionel Sambuc {
362*ebfedea0SLionel Sambuc 	FILE	*fp;
363*ebfedea0SLionel Sambuc 
364*ebfedea0SLionel Sambuc 	if ((fp = fopen(f, "w")) == NULL) {
365*ebfedea0SLionel Sambuc 		(void) fprintf(stderr, "can't write file '%s'\n", f);
366*ebfedea0SLionel Sambuc 		return 0;
367*ebfedea0SLionel Sambuc 	}
368*ebfedea0SLionel Sambuc 	write(fileno(fp), s, cc);
369*ebfedea0SLionel Sambuc 	(void) fclose(fp);
370*ebfedea0SLionel Sambuc 	return 1;
371*ebfedea0SLionel Sambuc }
372*ebfedea0SLionel Sambuc 
373*ebfedea0SLionel Sambuc /* utility function to read a string from a file */
374*ebfedea0SLionel Sambuc int
paa_read_file(const char * f,char * s,size_t size)375*ebfedea0SLionel Sambuc paa_read_file(const char *f, char *s, size_t size)
376*ebfedea0SLionel Sambuc {
377*ebfedea0SLionel Sambuc 	FILE	*fp;
378*ebfedea0SLionel Sambuc 	int	 cc;
379*ebfedea0SLionel Sambuc 
380*ebfedea0SLionel Sambuc 	if ((fp = fopen(f, "r")) == NULL) {
381*ebfedea0SLionel Sambuc 		(void) fprintf(stderr, "can't write '%s'\n", f);
382*ebfedea0SLionel Sambuc 		return 0;
383*ebfedea0SLionel Sambuc 	}
384*ebfedea0SLionel Sambuc 	cc = read(fileno(fp), s, size);
385*ebfedea0SLionel Sambuc 	(void) fclose(fp);
386*ebfedea0SLionel Sambuc 	return cc;
387*ebfedea0SLionel Sambuc }
388