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