netpgp.c revision 1.43
1/*-
2 * Copyright (c) 2009 The NetBSD Foundation, Inc.
3 * All rights reserved.
4 *
5 * This code is derived from software contributed to The NetBSD Foundation
6 * by Alistair Crooks (agc@NetBSD.org)
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 */
29#include "config.h"
30
31#ifdef HAVE_SYS_CDEFS_H
32#include <sys/cdefs.h>
33#endif
34
35#if defined(__NetBSD__)
36__COPYRIGHT("@(#) Copyright (c) 2009 The NetBSD Foundation, Inc. All rights reserved.");
37__RCSID("$NetBSD: netpgp.c,v 1.43 2010/03/13 23:30:41 agc Exp $");
38#endif
39
40#include <sys/types.h>
41#include <sys/stat.h>
42#include <sys/param.h>
43#include <sys/mman.h>
44
45#ifdef HAVE_SYS_RESOURCE_H
46#include <sys/resource.h>
47#endif
48
49#ifdef HAVE_FCNTL_H
50#include <fcntl.h>
51#endif
52
53#include <errno.h>
54#include <regex.h>
55#include <stdarg.h>
56#include <stdlib.h>
57#include <string.h>
58#include <time.h>
59
60#ifdef HAVE_UNISTD_H
61#include <unistd.h>
62#endif
63
64#include <errno.h>
65
66#ifdef HAVE_LIMITS_H
67#include <limits.h>
68#endif
69
70#include <netpgp.h>
71
72#include "packet.h"
73#include "packet-parse.h"
74#include "keyring.h"
75#include "errors.h"
76#include "packet-show.h"
77#include "create.h"
78#include "netpgpsdk.h"
79#include "memory.h"
80#include "validate.h"
81#include "readerwriter.h"
82#include "netpgpdefs.h"
83#include "crypto.h"
84#include "ops-ssh.h"
85#include "defs.h"
86
87/* read any gpg config file */
88static int
89conffile(netpgp_t *netpgp, char *homedir, char *userid, size_t length)
90{
91	regmatch_t	 matchv[10];
92	regex_t		 keyre;
93	char		 buf[BUFSIZ];
94	FILE		*fp;
95
96	__OPS_USED(netpgp);
97	(void) snprintf(buf, sizeof(buf), "%s/gpg.conf", homedir);
98	if ((fp = fopen(buf, "r")) == NULL) {
99		return 0;
100	}
101	(void) memset(&keyre, 0x0, sizeof(keyre));
102	(void) regcomp(&keyre, "^[ \t]*default-key[ \t]+([0-9a-zA-F]+)",
103		REG_EXTENDED);
104	while (fgets(buf, sizeof(buf), fp) != NULL) {
105		if (regexec(&keyre, buf, 10, matchv, 0) == 0) {
106			(void) memcpy(userid, &buf[(int)matchv[1].rm_so],
107				MIN((unsigned)(matchv[1].rm_eo -
108						matchv[1].rm_so), length));
109			if (netpgp->passfp == NULL) {
110				(void) fprintf(stderr,
111				"netpgp: default key set to \"%.*s\"\n",
112				(int)(matchv[1].rm_eo - matchv[1].rm_so),
113				&buf[(int)matchv[1].rm_so]);
114			}
115		}
116	}
117	(void) fclose(fp);
118	return 1;
119}
120
121/* small function to pretty print an 8-character raw userid */
122static char    *
123userid_to_id(const uint8_t *userid, char *id)
124{
125	static const char *hexes = "0123456789abcdef";
126	int		   i;
127
128	for (i = 0; i < 8 ; i++) {
129		id[i * 2] = hexes[(unsigned)(userid[i] & 0xf0) >> 4];
130		id[(i * 2) + 1] = hexes[userid[i] & 0xf];
131	}
132	id[8 * 2] = 0x0;
133	return id;
134}
135
136/* print out the successful signature information */
137static void
138resultp(__ops_io_t *io,
139	const char *f,
140	__ops_validation_t *res,
141	__ops_keyring_t *ring)
142{
143	const __ops_key_t	*pubkey;
144	unsigned		 from;
145	unsigned		 i;
146	time_t			 t;
147	char			 id[MAX_ID_LENGTH + 1];
148
149	for (i = 0; i < res->validc; i++) {
150		(void) fprintf(io->res,
151			"Good signature for %s made %s",
152			(f) ? f : "<stdin>",
153			ctime(&res->valid_sigs[i].birthtime));
154		if (res->duration > 0) {
155			t = res->birthtime + res->duration;
156			(void) fprintf(io->res, "Valid until %s", ctime(&t));
157		}
158		(void) fprintf(io->res,
159			"using %s key %s\n",
160			__ops_show_pka(res->valid_sigs[i].key_alg),
161			userid_to_id(res->valid_sigs[i].signer_id, id));
162		from = 0;
163		pubkey = __ops_getkeybyid(io, ring,
164			(const uint8_t *) res->valid_sigs[i].signer_id,
165			&from);
166		__ops_print_keydata(io, ring, pubkey, "pub", &pubkey->key.pubkey, 0);
167	}
168}
169
170/* check there's enough space in the arrays */
171static int
172size_arrays(netpgp_t *netpgp, unsigned needed)
173{
174	char	**temp;
175
176	if (netpgp->size == 0) {
177		/* only get here first time around */
178		netpgp->size = needed;
179		if ((netpgp->name = calloc(sizeof(char *), needed)) == NULL) {
180			(void) fprintf(stderr, "size_arrays: bad alloc\n");
181			return 0;
182		}
183		if ((netpgp->value = calloc(sizeof(char *), needed)) == NULL) {
184			free(netpgp->name);
185			(void) fprintf(stderr, "size_arrays: bad alloc\n");
186			return 0;
187		}
188	} else if (netpgp->c == netpgp->size) {
189		/* only uses 'needed' when filled array */
190		netpgp->size += needed;
191		temp = realloc(netpgp->name, sizeof(char *) * needed);
192		if (temp == NULL) {
193			(void) fprintf(stderr, "size_arrays: bad alloc\n");
194			return 0;
195		}
196		netpgp->name = temp;
197		temp = realloc(netpgp->value, sizeof(char *) * needed);
198		if (temp == NULL) {
199			(void) fprintf(stderr, "size_arrays: bad alloc\n");
200			return 0;
201		}
202		netpgp->value = temp;
203	}
204	return 1;
205}
206
207/* find the name in the array */
208static int
209findvar(netpgp_t *netpgp, const char *name)
210{
211	unsigned	i;
212
213	for (i = 0 ; i < netpgp->c && strcmp(netpgp->name[i], name) != 0; i++) {
214	}
215	return (i == netpgp->c) ? -1 : (int)i;
216}
217
218/* read a keyring and return it */
219static void *
220readkeyring(netpgp_t *netpgp, const char *name)
221{
222	__ops_keyring_t	*keyring;
223	const unsigned	 noarmor = 0;
224	char		 f[MAXPATHLEN];
225	char		*filename;
226	char		*homedir;
227
228	homedir = netpgp_getvar(netpgp, "homedir");
229	if ((filename = netpgp_getvar(netpgp, name)) == NULL) {
230		(void) snprintf(f, sizeof(f), "%s/%s.gpg", homedir, name);
231		filename = f;
232	}
233	if ((keyring = calloc(1, sizeof(*keyring))) == NULL) {
234		(void) fprintf(stderr, "readkeyring: bad alloc\n");
235		return NULL;
236	}
237	if (!__ops_keyring_fileread(keyring, noarmor, filename)) {
238		free(keyring);
239		(void) fprintf(stderr, "Can't read %s %s\n", name, filename);
240		return NULL;
241	}
242	netpgp_setvar(netpgp, name, filename);
243	return keyring;
244}
245
246/* read keys from ssh key files */
247static int
248readsshkeys(netpgp_t *netpgp, char *homedir)
249{
250	__ops_keyring_t	*pubring;
251	__ops_keyring_t	*secring;
252	char		 f[MAXPATHLEN];
253	char		*filename;
254
255	if ((filename = netpgp_getvar(netpgp, "sshkeyfile")) == NULL) {
256		(void) snprintf(f, sizeof(f), "%s/.ssh/is_rsa.pub", homedir);
257		filename = f;
258	}
259	if ((pubring = calloc(1, sizeof(*pubring))) == NULL) {
260		(void) fprintf(stderr, "readsshkeys: bad alloc\n");
261		return 0;
262	}
263	if (!__ops_ssh2_readkeys(netpgp->io, pubring, NULL, filename, NULL)) {
264		free(pubring);
265		(void) fprintf(stderr, "readsshkeys: can't read %s\n",
266				filename);
267		return 0;
268	}
269	if (netpgp->pubring == NULL) {
270		netpgp->pubring = pubring;
271	} else {
272		__ops_append_keyring(netpgp->pubring, pubring);
273	}
274	netpgp_setvar(netpgp, "sshpubfile", filename);
275	/* try to take the ".pub" off the end */
276	if (filename == f) {
277		f[strlen(f) - 4] = 0x0;
278	} else {
279		(void) snprintf(f, sizeof(f), "%.*s",
280				(int)strlen(filename) - 4, filename);
281		filename = f;
282	}
283	if ((secring = calloc(1, sizeof(*secring))) == NULL) {
284		(void) fprintf(stderr, "readsshkeys: bad alloc\n");
285		return 0;
286	}
287	if (__ops_ssh2_readkeys(netpgp->io, pubring, secring, NULL, filename)) {
288		netpgp->secring = secring;
289		netpgp_setvar(netpgp, "sshsecfile", filename);
290	} else {
291		(void) fprintf(stderr, "readsshkeys: can't read sec %s (%d)\n",
292				filename, errno);
293	}
294	return 1;
295}
296
297/* set ssh uid to first one in ring */
298static void
299set_ssh_userid(__ops_keyring_t *pubring, char *id, size_t len, int last)
300{
301	uint8_t	*src;
302	int	 i;
303	int	 n;
304
305	(void) memset(id, 0x0, len);
306	src = pubring->keys[(last) ? pubring->keyc - 1 : 0].key_id;
307	for (i = 0, n = 0 ; i < OPS_KEY_ID_SIZE ; i += 2) {
308		n += snprintf(&id[n], len - n, "%02x%02x", src[i], src[i + 1]);
309	}
310	id[n] = 0x0;
311}
312
313/* find the time - in a specific %Y-%m-%d format - using a regexp */
314static int
315grabdate(char *s, int64_t *t)
316{
317	static regex_t	r;
318	static int	compiled;
319	regmatch_t	matches[10];
320	struct tm	tm;
321
322	if (!compiled) {
323		compiled = 1;
324		(void) regcomp(&r, "([0-9][0-9][0-9][0-9])[-/]([0-9][0-9])[-/]([0-9][0-9])", REG_EXTENDED);
325	}
326	if (regexec(&r, s, 10, matches, 0) == 0) {
327		(void) memset(&tm, 0x0, sizeof(tm));
328		tm.tm_year = (int)strtol(&s[(int)matches[1].rm_so], NULL, 10);
329		tm.tm_mon = (int)strtol(&s[(int)matches[2].rm_so], NULL, 10) - 1;
330		tm.tm_mday = (int)strtol(&s[(int)matches[3].rm_so], NULL, 10);
331		*t = mktime(&tm);
332		return 1;
333	}
334	return 0;
335}
336
337/* get expiration in seconds */
338static uint64_t
339get_duration(char *s)
340{
341	uint64_t	 now;
342	int64_t	 	 t;
343	char		*mult;
344
345	if (s == NULL) {
346		return 0;
347	}
348	now = strtoull(s, NULL, 10);
349	if ((mult = strchr("hdwmy", s[strlen(s) - 1])) != NULL) {
350		switch(*mult) {
351		case 'h':
352			return now * 60 * 60;
353		case 'd':
354			return now * 60 * 60 * 24;
355		case 'w':
356			return now * 60 * 60 * 24 * 7;
357		case 'm':
358			return now * 60 * 60 * 24 * 31;
359		case 'y':
360			return now * 60 * 60 * 24 * 365;
361		}
362	}
363	if (grabdate(s, &t)) {
364		return t;
365	}
366	return (uint64_t)strtoll(s, NULL, 10);
367}
368
369/* get birthtime in seconds */
370static int64_t
371get_birthtime(char *s)
372{
373	int64_t	t;
374
375	if (s == NULL) {
376		return time(NULL);
377	}
378	if (grabdate(s, &t)) {
379		return t;
380	}
381	return (uint64_t)strtoll(s, NULL, 10);
382}
383
384/***************************************************************************/
385/* exported functions start here */
386/***************************************************************************/
387
388/* initialise a netpgp_t structure */
389int
390netpgp_init(netpgp_t *netpgp)
391{
392	__ops_io_t	*io;
393	char		 id[MAX_ID_LENGTH];
394	char		*homedir;
395	char		*userid;
396	char		*stream;
397	char		*passfd;
398	char		*results;
399	int		 coredumps;
400	int		 last;
401
402#ifdef HAVE_SYS_RESOURCE_H
403	struct rlimit	limit;
404
405	coredumps = netpgp_getvar(netpgp, "coredumps") != NULL;
406	if (!coredumps) {
407		(void) memset(&limit, 0x0, sizeof(limit));
408		if (setrlimit(RLIMIT_CORE, &limit) != 0) {
409			(void) fprintf(stderr,
410			"netpgp: warning - can't turn off core dumps\n");
411			coredumps = 1;
412		}
413	}
414#else
415	coredumps = 1;
416#endif
417	if ((io = calloc(1, sizeof(*io))) == NULL) {
418		(void) fprintf(stderr, "netpgp_init: bad alloc\n");
419		return 0;
420	}
421	io->outs = stdout;
422	if ((stream = netpgp_getvar(netpgp, "stdout")) != NULL &&
423	    strcmp(stream, "stderr") == 0) {
424		io->outs = stderr;
425	}
426	io->errs = stderr;
427	if ((stream = netpgp_getvar(netpgp, "stderr")) != NULL &&
428	    strcmp(stream, "stdout") == 0) {
429		io->errs = stdout;
430	}
431	if ((results = netpgp_getvar(netpgp, "results")) == NULL) {
432		io->res = io->errs;
433	} else if ((io->res = fopen(results, "w")) == NULL) {
434		(void) fprintf(io->errs, "Can't open results %s for writing\n",
435			results);
436		free(io);
437		return 0;
438	}
439	netpgp->io = io;
440	if ((passfd = netpgp_getvar(netpgp, "pass-fd")) != NULL &&
441	    (netpgp->passfp = fdopen(atoi(passfd), "r")) == NULL) {
442		(void) fprintf(io->errs, "Can't open fd %s for reading\n",
443			passfd);
444		return 0;
445	}
446	if (coredumps) {
447		(void) fprintf(io->errs,
448			"netpgp: warning: core dumps enabled\n");
449	}
450	if ((homedir = netpgp_getvar(netpgp, "homedir")) == NULL) {
451		(void) fprintf(io->errs, "netpgp: bad homedir\n");
452		return 0;
453	}
454	/* read from either gpg files or ssh keys */
455	if (netpgp_getvar(netpgp, "ssh keys") == NULL) {
456		if ((userid = netpgp_getvar(netpgp, "userid")) == NULL) {
457			(void) memset(id, 0x0, sizeof(id));
458			(void) conffile(netpgp, homedir, id, sizeof(id));
459			if (id[0] != 0x0) {
460				netpgp_setvar(netpgp, "userid", userid = id);
461			}
462		}
463		if (userid == NULL) {
464			if (netpgp_getvar(netpgp, "need userid") != NULL) {
465				(void) fprintf(io->errs,
466						"Cannot find user id\n");
467				return 0;
468			}
469		} else {
470			(void) netpgp_setvar(netpgp, "userid", userid);
471		}
472		netpgp->pubring = readkeyring(netpgp, "pubring");
473		if (netpgp->pubring == NULL) {
474			(void) fprintf(io->errs, "Can't read pub keyring\n");
475			return 0;
476		}
477		netpgp->secring = readkeyring(netpgp, "secring");
478		if (netpgp->secring == NULL) {
479			(void) fprintf(io->errs, "Can't read sec keyring\n");
480			return 0;
481		}
482	} else {
483		last = (netpgp->pubring != NULL);
484		if (!readsshkeys(netpgp, homedir)) {
485			(void) fprintf(io->errs, "Can't read ssh pub key\n");
486			return 0;
487		}
488		if ((userid = netpgp_getvar(netpgp, "userid")) == NULL) {
489			set_ssh_userid(netpgp->pubring, id, sizeof(id), last);
490			netpgp_setvar(netpgp, "userid", userid = id);
491		}
492		if (userid == NULL) {
493			if (netpgp_getvar(netpgp, "need userid") != NULL) {
494				(void) fprintf(io->errs,
495						"Cannot find user id\n");
496				return 0;
497			}
498		} else {
499			(void) netpgp_setvar(netpgp, "userid", userid);
500		}
501	}
502	return 1;
503}
504
505/* finish off with the netpgp_t struct */
506int
507netpgp_end(netpgp_t *netpgp)
508{
509	unsigned	i;
510
511	for (i = 0 ; i < netpgp->c ; i++) {
512		if (netpgp->name[i] != NULL) {
513			free(netpgp->name[i]);
514		}
515		if (netpgp->value[i] != NULL) {
516			free(netpgp->value[i]);
517		}
518	}
519	if (netpgp->name != NULL) {
520		free(netpgp->name);
521	}
522	if (netpgp->value != NULL) {
523		free(netpgp->value);
524	}
525	if (netpgp->pubring != NULL) {
526		__ops_keyring_free(netpgp->pubring);
527	}
528	if (netpgp->secring != NULL) {
529		__ops_keyring_free(netpgp->secring);
530	}
531	free(netpgp->io);
532	return 1;
533}
534
535/* list the keys in a keyring */
536int
537netpgp_list_keys(netpgp_t *netpgp, const int psigs)
538{
539	return __ops_keyring_list(netpgp->io, netpgp->pubring, psigs);
540}
541
542DEFINE_ARRAY(strings_t, char *);
543
544#ifndef HKP_VERSION
545#define HKP_VERSION	1
546#endif
547
548/* find and list some keys in a keyring */
549int
550netpgp_match_keys(netpgp_t *netpgp, char *name, const char *fmt, void *vp, const int psigs)
551{
552	const __ops_key_t	*key;
553	unsigned		 k;
554	strings_t		 pubs;
555	FILE			*fp = (FILE *)vp;
556
557	if (name[0] == '0' && name[1] == 'x') {
558		name += 2;
559	}
560	(void) memset(&pubs, 0x0, sizeof(pubs));
561	k = 0;
562	do {
563		key = __ops_getnextkeybyname(netpgp->io, netpgp->pubring,
564						name, &k);
565		if (key != NULL) {
566			ALLOC(char *, pubs.v, pubs.size, pubs.c, 10, 10,
567					"netpgp_match_keys", return 0);
568			if (strcmp(fmt, "mr") == 0) {
569				__ops_hkp_sprint_keydata(
570						key, &pubs.v[pubs.c],
571						&key->key.pubkey);
572			} else {
573				__ops_sprint_keydata(netpgp->io, netpgp->pubring,
574						key, &pubs.v[pubs.c],
575						"pub",
576						&key->key.pubkey, psigs);
577			}
578			pubs.c += 1;
579			k += 1;
580		}
581	} while (key != NULL);
582	if (strcmp(fmt, "mr") == 0) {
583		(void) fprintf(fp, "info:%d:%d\n", HKP_VERSION, pubs.c);
584	} else {
585		(void) fprintf(fp, "%d key%s found\n", pubs.c,
586			(pubs.c == 1) ? "" : "s");
587	}
588	for (k = 0 ; k < pubs.c ; k++) {
589		(void) fprintf(fp, "%s", pubs.v[k]);
590		free(pubs.v[k]);
591	}
592	free(pubs.v);
593	return pubs.c;
594}
595
596/* find and list some public keys in a keyring */
597int
598netpgp_match_pubkeys(netpgp_t *netpgp, char *name, void *vp)
599{
600	const __ops_key_t	*key;
601	unsigned		 k;
602	strings_t		 pubs;
603	FILE			*fp = (FILE *)vp;
604
605	(void) memset(&pubs, 0x0, sizeof(pubs));
606	do {
607		key = __ops_getnextkeybyname(netpgp->io, netpgp->pubring,
608						name, &k);
609		if (key != NULL) {
610			char	out[1024 * 64];
611
612			ALLOC(char *, pubs.v, pubs.size, pubs.c, 10, 10,
613					"netpgp_match_pubkeys", return 0);
614			(void) __ops_sprint_pubkey(key, out, sizeof(out));
615			pubs.v[pubs.c++] = netpgp_strdup(out);
616			k += 1;
617		}
618	} while (key != NULL);
619	(void) fprintf(fp, "info:%d:%d\n", HKP_VERSION, pubs.c);
620	for (k = 0 ; k < pubs.c ; k++) {
621		(void) fprintf(fp, "%s", pubs.v[k]);
622		free(pubs.v[k]);
623	}
624	free(pubs.v);
625	return pubs.c;
626}
627
628/* find a key in a keyring */
629int
630netpgp_find_key(netpgp_t *netpgp, char *id)
631{
632	__ops_io_t	*io;
633
634	io = netpgp->io;
635	if (id == NULL) {
636		(void) fprintf(io->errs, "NULL id to search for\n");
637		return 0;
638	}
639	return __ops_getkeybyname(netpgp->io, netpgp->pubring, id) != NULL;
640}
641
642/* get a key in a keyring */
643char *
644netpgp_get_key(netpgp_t *netpgp, const char *name, const char *fmt)
645{
646	const __ops_key_t	*key;
647	__ops_io_t		*io;
648	char			*newkey;
649
650	io = netpgp->io;
651	if (name == NULL) {
652		name = netpgp_getvar(netpgp, "userid");
653	} else if (name[0] == '0' && name[1] == 'x') {
654		name += 2;
655	}
656	key = __ops_getkeybyname(netpgp->io, netpgp->pubring, name);
657	if (key == NULL) {
658		(void) fprintf(io->errs, "Can't find key '%s'\n", name);
659		return NULL;
660	}
661	if (strcmp(fmt, "mr") == 0) {
662		return (__ops_hkp_sprint_keydata(key, &newkey,
663				&key->key.pubkey) > 0) ? newkey : NULL;
664	}
665	return (__ops_sprint_keydata(netpgp->io, netpgp->pubring,
666				key, &newkey, "pub",
667				&key->key.pubkey, 0) > 0) ? newkey : NULL;
668}
669
670/* export a given key */
671char *
672netpgp_export_key(netpgp_t *netpgp, char *name)
673{
674	const __ops_key_t	*key;
675	__ops_io_t		*io;
676
677	io = netpgp->io;
678	if (name == NULL) {
679		name = netpgp_getvar(netpgp, "userid");
680	} else if (name[0] == '0' && name[1] == 'x') {
681		name += 2;
682	}
683	key = __ops_getkeybyname(io, netpgp->pubring, name);
684	if (key == NULL) {
685		(void) fprintf(io->errs,
686			"Cannot find own key \"%s\" in keyring\n", name);
687		return 0;
688	}
689	return __ops_export_key(io, key, NULL);
690}
691
692/* import a key into our keyring */
693int
694netpgp_import_key(netpgp_t *netpgp, char *f)
695{
696	const unsigned	noarmor = 0;
697	const unsigned	armor = 1;
698	__ops_io_t	*io;
699	int		done;
700
701	io = netpgp->io;
702	if ((done = __ops_keyring_fileread(netpgp->pubring, noarmor, f)) == 0) {
703		done = __ops_keyring_fileread(netpgp->pubring, armor, f);
704	}
705	if (!done) {
706		(void) fprintf(io->errs, "Cannot import key from file %s\n",
707				f);
708		return 0;
709	}
710	return __ops_keyring_list(io, netpgp->pubring, 0);
711}
712
713/* generate a new key */
714int
715netpgp_generate_key(netpgp_t *netpgp, char *id, int numbits)
716{
717	__ops_key_t		*keypair;
718	__ops_userid_t		 uid;
719	__ops_output_t		*create;
720	const unsigned		 noarmor = 0;
721	__ops_io_t		*io;
722	char			*ringfile;
723	int             	 fd;
724
725	(void) memset(&uid, 0x0, sizeof(uid));
726	io = netpgp->io;
727	/* generate a new key for 'id' */
728	uid.userid = (uint8_t *) id;
729	keypair = __ops_rsa_new_selfsign_key(numbits, 65537UL, &uid);
730	if (keypair == NULL) {
731		(void) fprintf(io->errs, "Cannot generate key\n");
732		return 0;
733	}
734	/* write public key, and try to re-read it */
735	ringfile = netpgp_getvar(netpgp, "pubring");
736	fd = __ops_setup_file_append(&create, ringfile);
737	if (!__ops_write_xfer_pubkey(create, keypair, noarmor)) {
738		(void) fprintf(io->errs, "Cannot write pubkey\n");
739		return 0;
740	}
741	__ops_teardown_file_write(create, fd);
742	__ops_keyring_free(netpgp->pubring);
743	if (!__ops_keyring_fileread(netpgp->pubring, noarmor, ringfile)) {
744		(void) fprintf(io->errs, "Cannot read pubring %s\n", ringfile);
745		return 0;
746	}
747	/* write secret key, and try to re-read it */
748	ringfile = netpgp_getvar(netpgp, "sec ring file");
749	fd = __ops_setup_file_append(&create, ringfile);
750	if (!__ops_write_xfer_seckey(create, keypair, NULL, 0, noarmor)) {
751		(void) fprintf(io->errs, "Cannot write seckey\n");
752		return 0;
753	}
754	__ops_teardown_file_write(create, fd);
755	__ops_keyring_free(netpgp->secring);
756	if (!__ops_keyring_fileread(netpgp->secring, noarmor, ringfile)) {
757		(void) fprintf(io->errs, "Can't read secring %s\n", ringfile);
758		return 0;
759	}
760	__ops_keydata_free(keypair);
761	return 1;
762}
763
764/* encrypt a file */
765int
766netpgp_encrypt_file(netpgp_t *netpgp,
767			const char *userid,
768			const char *f,
769			char *out,
770			int armored)
771{
772	const __ops_key_t	*keypair;
773	const unsigned		 overwrite = 1;
774	const char		*suffix;
775	__ops_io_t		*io;
776	char			 outname[MAXPATHLEN];
777
778	io = netpgp->io;
779	if (f == NULL) {
780		(void) fprintf(io->errs,
781			"netpgp_encrypt_file: no filename specified\n");
782		return 0;
783	}
784	if (userid == NULL) {
785		userid = netpgp_getvar(netpgp, "userid");
786	}
787	suffix = (armored) ? ".asc" : ".gpg";
788	keypair = __ops_getkeybyname(io, netpgp->pubring, userid);
789	if (keypair == NULL) {
790		(void) fprintf(io->errs, "Userid '%s' not found in keyring\n",
791					userid);
792		return 0;
793	}
794	if (out == NULL) {
795		(void) snprintf(outname, sizeof(outname), "%s%s", f, suffix);
796		out = outname;
797	}
798	return (int)__ops_encrypt_file(io, f, out, keypair, (unsigned)armored,
799					overwrite);
800}
801
802#define ARMOR_HEAD	"-----BEGIN PGP MESSAGE-----\r\n"
803
804/* decrypt a file */
805int
806netpgp_decrypt_file(netpgp_t *netpgp, const char *f, char *out, int armored)
807{
808	const unsigned	 overwrite = 1;
809	__ops_io_t	*io;
810	unsigned	 realarmour;
811	FILE		*fp;
812	char		 buf[BUFSIZ];
813
814	io = netpgp->io;
815	if (f == NULL) {
816		(void) fprintf(io->errs,
817			"netpgp_decrypt_file: no filename specified\n");
818		return 0;
819	}
820	realarmour = (unsigned)armored;
821	if ((fp = fopen(f, "r")) == NULL) {
822		(void) fprintf(io->errs,
823			"netpgp_decrypt_file: can't open '%s'\n", f);
824		return 0;
825	}
826	if (fgets(buf, sizeof(buf), fp) == NULL) {
827		realarmour = 0;
828	} else {
829		realarmour = (strcmp(buf, ARMOR_HEAD) == 0);
830	}
831	(void) fclose(fp);
832	return __ops_decrypt_file(netpgp->io, f, out, netpgp->secring,
833				netpgp->pubring,
834				(unsigned)realarmour, overwrite,
835				netpgp->passfp, get_passphrase_cb);
836}
837
838/* sign a file */
839int
840netpgp_sign_file(netpgp_t *netpgp,
841		const char *userid,
842		const char *f,
843		char *out,
844		int armored,
845		int cleartext,
846		int detached)
847{
848	const __ops_key_t	*keypair;
849	const __ops_key_t	*pubkey;
850	__ops_seckey_t		*seckey;
851	const unsigned		 overwrite = 1;
852	__ops_io_t		*io;
853	char			*hashalg;
854	int			 ret;
855
856	io = netpgp->io;
857	if (f == NULL) {
858		(void) fprintf(io->errs,
859			"netpgp_sign_file: no filename specified\n");
860		return 0;
861	}
862	if (userid == NULL) {
863		userid = netpgp_getvar(netpgp, "userid");
864	}
865	/* get key with which to sign */
866	keypair = __ops_getkeybyname(io, netpgp->secring, userid);
867	if (keypair == NULL) {
868		(void) fprintf(io->errs, "Userid '%s' not found in secring\n",
869				userid);
870		return 0;
871	}
872	ret = 1;
873	do {
874		if (netpgp->passfp == NULL) {
875			/* print out the user id */
876			pubkey = __ops_getkeybyname(io, netpgp->pubring, userid);
877			if (pubkey == NULL) {
878				(void) fprintf(io->errs,
879					"netpgp: warning - using pubkey from secring\n");
880				__ops_print_keydata(io, netpgp->pubring, keypair, "pub",
881					&keypair->key.seckey.pubkey, 0);
882			} else {
883				__ops_print_keydata(io, netpgp->pubring, pubkey, "pub", &pubkey->key.pubkey, 0);
884			}
885		}
886		if (netpgp_getvar(netpgp, "ssh keys") == NULL) {
887			/* now decrypt key */
888			seckey = __ops_decrypt_seckey(keypair, netpgp->passfp);
889			if (seckey == NULL) {
890				(void) fprintf(io->errs, "Bad passphrase\n");
891			}
892		} else {
893			__ops_keyring_t	*secring;
894
895			secring = netpgp->secring;
896			seckey = &secring->keys[0].key.seckey;
897		}
898	} while (seckey == NULL);
899	/* sign file */
900	hashalg = netpgp_getvar(netpgp, "hash");
901	if (detached) {
902		ret = __ops_sign_detached(io, f, out, seckey, hashalg,
903				get_birthtime(netpgp_getvar(netpgp, "birthtime")),
904				get_duration(netpgp_getvar(netpgp, "duration")));
905	} else {
906		ret = __ops_sign_file(io, f, out, seckey, hashalg,
907				get_birthtime(netpgp_getvar(netpgp, "birthtime")),
908				get_duration(netpgp_getvar(netpgp, "duration")),
909				(unsigned)armored, (unsigned)cleartext,
910				overwrite);
911	}
912	__ops_forget(seckey, sizeof(*seckey));
913	return ret;
914}
915
916/* verify a file */
917int
918netpgp_verify_file(netpgp_t *netpgp, const char *in, const char *out, int armored)
919{
920	__ops_validation_t	 result;
921	__ops_io_t		*io;
922
923	(void) memset(&result, 0x0, sizeof(result));
924	io = netpgp->io;
925	if (in == NULL) {
926		(void) fprintf(io->errs,
927			"netpgp_verify_file: no filename specified\n");
928		return 0;
929	}
930	if (__ops_validate_file(io, &result, in, out, armored,
931						netpgp->pubring)) {
932		resultp(io, in, &result, netpgp->pubring);
933		return 1;
934	}
935	if (result.validc + result.invalidc + result.unknownc == 0) {
936		(void) fprintf(io->errs,
937		"\"%s\": No signatures found - is this a signed file?\n",
938			in);
939	} else if (result.invalidc == 0 && result.unknownc == 0) {
940		(void) fprintf(io->errs,
941			"\"%s\": file verification failure: invalid signature time\n", in);
942	} else {
943		(void) fprintf(io->errs,
944"\"%s\": verification failure: %u invalid signatures, %u unknown signatures\n",
945			in, result.invalidc, result.unknownc);
946	}
947	return 0;
948}
949
950/* sign some memory */
951int
952netpgp_sign_memory(netpgp_t *netpgp,
953		const char *userid,
954		char *mem,
955		size_t size,
956		char *out,
957		size_t outsize,
958		const unsigned armored,
959		const unsigned cleartext)
960{
961	const __ops_key_t	*keypair;
962	const __ops_key_t	*pubkey;
963	__ops_seckey_t		*seckey;
964	__ops_memory_t		*signedmem;
965	__ops_io_t		*io;
966	char			*hashalg;
967	int			 ret;
968
969	io = netpgp->io;
970	if (mem == NULL) {
971		(void) fprintf(io->errs,
972			"netpgp_sign_memory: no memory to sign\n");
973		return 0;
974	}
975	if (userid == NULL) {
976		userid = netpgp_getvar(netpgp, "userid");
977	}
978	/* get key with which to sign */
979	keypair = __ops_getkeybyname(io, netpgp->secring, userid);
980	if (keypair == NULL) {
981		(void) fprintf(io->errs, "Userid '%s' not found in keyring\n",
982				userid);
983		return 0;
984	}
985	ret = 1;
986	do {
987		if (netpgp->passfp == NULL) {
988			/* print out the user id */
989			pubkey = __ops_getkeybyname(io, netpgp->pubring, userid);
990			if (pubkey == NULL) {
991				(void) fprintf(io->errs,
992					"netpgp: warning - using pubkey from secring\n");
993				__ops_print_keydata(io, netpgp->pubring, keypair, "pub",
994					&keypair->key.seckey.pubkey, 0);
995			} else {
996				__ops_print_keydata(io, netpgp->pubring, pubkey, "pub", &pubkey->key.pubkey, 0);
997			}
998		}
999		/* now decrypt key */
1000		seckey = __ops_decrypt_seckey(keypair, netpgp->passfp);
1001		if (seckey == NULL) {
1002			(void) fprintf(io->errs, "Bad passphrase\n");
1003		}
1004	} while (seckey == NULL);
1005	/* sign file */
1006	(void) memset(out, 0x0, outsize);
1007	hashalg = netpgp_getvar(netpgp, "hash");
1008	signedmem = __ops_sign_buf(io, mem, size, seckey,
1009				get_birthtime(netpgp_getvar(netpgp, "birthtime")),
1010				get_duration(netpgp_getvar(netpgp, "duration")),
1011				hashalg, armored, cleartext);
1012	if (signedmem) {
1013		size_t	m;
1014
1015		m = MIN(__ops_mem_len(signedmem), outsize);
1016		(void) memcpy(out, __ops_mem_data(signedmem), m);
1017		__ops_memory_free(signedmem);
1018		ret = (int)m;
1019	} else {
1020		ret = 0;
1021	}
1022	__ops_forget(seckey, sizeof(*seckey));
1023	return ret;
1024}
1025
1026/* verify memory */
1027int
1028netpgp_verify_memory(netpgp_t *netpgp, const void *in, const size_t size,
1029			void *out, size_t outsize, const int armored)
1030{
1031	__ops_validation_t	 result;
1032	__ops_memory_t		*signedmem;
1033	__ops_memory_t		*cat;
1034	__ops_io_t		*io;
1035	size_t			 m;
1036	int			 ret;
1037
1038	(void) memset(&result, 0x0, sizeof(result));
1039	io = netpgp->io;
1040	if (in == NULL) {
1041		(void) fprintf(io->errs,
1042			"netpgp_verify_memory: no memory to verify\n");
1043		return 0;
1044	}
1045	signedmem = __ops_memory_new();
1046	__ops_memory_add(signedmem, in, size);
1047	if (out) {
1048		cat = __ops_memory_new();
1049	}
1050	ret = __ops_validate_mem(io, &result, signedmem,
1051				(out) ? &cat : NULL,
1052				armored, netpgp->pubring);
1053	__ops_memory_free(signedmem);
1054	if (ret) {
1055		resultp(io, "<stdin>", &result, netpgp->pubring);
1056		if (out) {
1057			m = MIN(__ops_mem_len(cat), outsize);
1058			(void) memcpy(out, __ops_mem_data(cat), m);
1059			__ops_memory_free(cat);
1060		} else {
1061			m = 1;
1062		}
1063		return (int)m;
1064	}
1065	if (result.validc + result.invalidc + result.unknownc == 0) {
1066		(void) fprintf(io->errs,
1067		"No signatures found - is this memory signed?\n");
1068	} else if (result.invalidc == 0 && result.unknownc == 0) {
1069		(void) fprintf(io->errs,
1070			"memory verification failure: invalid signature time\n");
1071	} else {
1072		(void) fprintf(io->errs,
1073"memory verification failure: %u invalid signatures, %u unknown signatures\n",
1074			result.invalidc, result.unknownc);
1075	}
1076	return 0;
1077}
1078
1079/* encrypt some memory */
1080int
1081netpgp_encrypt_memory(netpgp_t *netpgp,
1082			const char *userid,
1083			void *in,
1084			const size_t insize,
1085			char *out,
1086			size_t outsize,
1087			int armored)
1088{
1089	const __ops_key_t	*keypair;
1090	__ops_memory_t		*enc;
1091	__ops_io_t		*io;
1092	size_t			 m;
1093
1094	io = netpgp->io;
1095	if (in == NULL) {
1096		(void) fprintf(io->errs,
1097			"netpgp_encrypt_buf: no memory to encrypt\n");
1098		return 0;
1099	}
1100	if (userid == NULL) {
1101		userid = netpgp_getvar(netpgp, "userid");
1102	}
1103	keypair = __ops_getkeybyname(io, netpgp->pubring, userid);
1104	if (keypair == NULL) {
1105		(void) fprintf(io->errs, "Userid '%s' not found in keyring\n",
1106					userid);
1107		return 0;
1108	}
1109	if (in == out) {
1110		(void) fprintf(io->errs,
1111			"netpgp_encrypt_buf: input and output bufs need to be different\n");
1112		return 0;
1113	}
1114	if (outsize < insize) {
1115		(void) fprintf(io->errs,
1116			"netpgp_encrypt_buf: input size is larger than output size\n");
1117		return 0;
1118	}
1119	enc = __ops_encrypt_buf(io, in, insize, keypair, (unsigned)armored);
1120	m = MIN(__ops_mem_len(enc), outsize);
1121	(void) memcpy(out, __ops_mem_data(enc), m);
1122	__ops_memory_free(enc);
1123	return (int)m;
1124}
1125
1126/* decrypt a chunk of memory */
1127int
1128netpgp_decrypt_memory(netpgp_t *netpgp, const void *input, const size_t insize,
1129			char *out, size_t outsize, const int armored)
1130{
1131	__ops_memory_t	*mem;
1132	__ops_io_t	*io;
1133	unsigned	 realarmour;
1134	size_t		 m;
1135
1136	io = netpgp->io;
1137	realarmour = (unsigned) armored;
1138	if (input == NULL) {
1139		(void) fprintf(io->errs,
1140			"netpgp_decrypt_memory: no memory\n");
1141		return 0;
1142	}
1143	realarmour = (strncmp(input, ARMOR_HEAD, sizeof(ARMOR_HEAD) - 1) == 0);
1144	mem = __ops_decrypt_buf(netpgp->io, input, insize, netpgp->secring,
1145				netpgp->pubring,
1146				realarmour, netpgp->passfp,
1147				get_passphrase_cb);
1148	m = MIN(__ops_mem_len(mem), outsize);
1149	(void) memcpy(out, __ops_mem_data(mem), m);
1150	__ops_memory_free(mem);
1151	return (int)m;
1152}
1153
1154/* wrappers for the ops_debug_level functions we added to openpgpsdk */
1155
1156/* set the debugging level per filename */
1157int
1158netpgp_set_debug(const char *f)
1159{
1160	return __ops_set_debug_level(f);
1161}
1162
1163/* get the debugging level per filename */
1164int
1165netpgp_get_debug(const char *f)
1166{
1167	return __ops_get_debug_level(f);
1168}
1169
1170/* return the version for the library */
1171const char *
1172netpgp_get_info(const char *type)
1173{
1174	return __ops_get_info(type);
1175}
1176
1177/* list all the packets in a file */
1178int
1179netpgp_list_packets(netpgp_t *netpgp, char *f, int armour, char *pubringname)
1180{
1181	__ops_keyring_t	*keyring;
1182	const unsigned	 noarmor = 0;
1183	struct stat	 st;
1184	__ops_io_t	*io;
1185	char		 ringname[MAXPATHLEN];
1186	char		*homedir;
1187	int		 ret;
1188
1189	io = netpgp->io;
1190	if (f == NULL) {
1191		(void) fprintf(io->errs, "No file containing packets\n");
1192		return 0;
1193	}
1194	if (stat(f, &st) < 0) {
1195		(void) fprintf(io->errs, "No such file '%s'\n", f);
1196		return 0;
1197	}
1198	homedir = netpgp_getvar(netpgp, "homedir");
1199	if (pubringname == NULL) {
1200		(void) snprintf(ringname, sizeof(ringname),
1201				"%s/pubring.gpg", homedir);
1202		pubringname = ringname;
1203	}
1204	if ((keyring = calloc(1, sizeof(*keyring))) == NULL) {
1205		(void) fprintf(io->errs, "netpgp_list_packets: bad alloc\n");
1206		return 0;
1207	}
1208	if (!__ops_keyring_fileread(keyring, noarmor, pubringname)) {
1209		free(keyring);
1210		(void) fprintf(io->errs, "Cannot read pub keyring %s\n",
1211			pubringname);
1212		return 0;
1213	}
1214	netpgp->pubring = keyring;
1215	netpgp_setvar(netpgp, "pubring", pubringname);
1216	ret = __ops_list_packets(io, f, (unsigned)armour,
1217					netpgp->secring,
1218					netpgp->pubring,
1219					netpgp->passfp,
1220					get_passphrase_cb);
1221	free(keyring);
1222	return ret;
1223}
1224
1225/* set a variable */
1226int
1227netpgp_setvar(netpgp_t *netpgp, const char *name, const char *value)
1228{
1229	int	i;
1230
1231	if ((i = findvar(netpgp, name)) < 0) {
1232		/* add the element to the array */
1233		if (size_arrays(netpgp, netpgp->size + 15)) {
1234			netpgp->name[i = netpgp->c++] = netpgp_strdup(name);
1235		}
1236	} else {
1237		/* replace the element in the array */
1238		if (netpgp->value[i]) {
1239			free(netpgp->value[i]);
1240			netpgp->value[i] = NULL;
1241		}
1242	}
1243	/* sanity checks for range of values */
1244	if (strcmp(name, "hash") == 0 || strcmp(name, "algorithm") == 0) {
1245		if (__ops_str_to_hash_alg(value) == OPS_HASH_UNKNOWN) {
1246			return 0;
1247		}
1248	}
1249	netpgp->value[i] = netpgp_strdup(value);
1250	return 1;
1251}
1252
1253/* get a variable's value (NULL if not set) */
1254char *
1255netpgp_getvar(netpgp_t *netpgp, const char *name)
1256{
1257	int	i;
1258
1259	return ((i = findvar(netpgp, name)) < 0) ? NULL : netpgp->value[i];
1260}
1261
1262/* increment a value */
1263int
1264netpgp_incvar(netpgp_t *netpgp, const char *name, const int delta)
1265{
1266	char	*cp;
1267	char	 num[16];
1268	int	 val;
1269
1270	val = 0;
1271	if ((cp = netpgp_getvar(netpgp, name)) != NULL) {
1272		val = atoi(cp);
1273	}
1274	(void) snprintf(num, sizeof(num), "%d", val + delta);
1275	netpgp_setvar(netpgp, name, num);
1276	return 1;
1277}
1278
1279/* set the home directory value to "home/subdir" */
1280int
1281netpgp_set_homedir(netpgp_t *netpgp, char *home, const char *subdir, const int quiet)
1282{
1283	struct stat	st;
1284	char		d[MAXPATHLEN];
1285
1286	if (home == NULL) {
1287		if (!quiet) {
1288			(void) fprintf(stderr, "NULL HOME directory\n");
1289		}
1290		return 0;
1291	}
1292	(void) snprintf(d, sizeof(d), "%s%s", home, (subdir) ? subdir : "");
1293	if (stat(d, &st) == 0) {
1294		if ((st.st_mode & S_IFMT) == S_IFDIR) {
1295			netpgp_setvar(netpgp, "homedir", d);
1296			return 1;
1297		}
1298		(void) fprintf(stderr, "netpgp: homedir \"%s\" is not a dir\n",
1299					d);
1300		return 0;
1301	}
1302	if (!quiet) {
1303		(void) fprintf(stderr,
1304			"netpgp: warning homedir \"%s\" not found\n", d);
1305	}
1306	return 1;
1307}
1308
1309/* validate all sigs in the pub keyring */
1310int
1311netpgp_validate_sigs(netpgp_t *netpgp)
1312{
1313	__ops_validation_t	result;
1314
1315	return (int)__ops_validate_all_sigs(&result, netpgp->pubring, NULL);
1316}
1317
1318#if 0
1319#include "sshkey.h"
1320
1321int
1322netpgp_pgpkey_to_sshkey(netpgp_t *netpgp, char *name, SSHKey *sshkey)
1323{
1324	const __ops_key_t	*pgpkey;
1325	unsigned		 k;
1326
1327	k = 0;
1328	pgpkey = __ops_getnextkeybyname(netpgp->io, netpgp->pubring, name, &k);
1329	if (pgpkey == NULL) {
1330		pgpkey = __ops_getkeybyname(io, netpgp->pubring, userid);
1331	}
1332	if (pgpkey == NULL) {
1333		(void) fprintf(stderr, "No key matching '%s'\n", name);
1334		return 0;
1335	}
1336	switch(pgpkey->key.pubkey.alg) {
1337	case OPS_PKA_RSA:
1338		sshkey->type = KEY_RSA;
1339		sshkey->rsa = calloc(1, sizeof(*sshkey->rsa);
1340		if (sshkey->rsa == NULL) {
1341			(void) fprintf(stderr, "RSA memory problems\n");
1342			return 0;
1343		}
1344		sshkey->rsa->n = pgpkey->key.pubkey.key.rsa.n;
1345		sshkey->rsa->e = pgpkey->key.pubkey.key.rsa.e;
1346		sshkey->rsa->d = pgpkey->key.seckey.key.rsa.d;
1347		sshkey->rsa->p = pgpkey->key.seckey.key.rsa.p;
1348		sshkey->rsa->q = pgpkey->key.seckey.key.rsa.q;
1349		sshkey->rsa->iqmp = pgpkey->key.seckey.key.rsa.u;
1350		break;
1351	case OPS_PKA_DSA:
1352		sshkey->type = KEY_DSA;
1353		sshkey->dsa = calloc(1, sizeof(*sshkey->dsa);
1354		if (sshkey->dsa == NULL) {
1355			(void) fprintf(stderr, "DSA memory problems\n");
1356			return 0;
1357		}
1358		sshkey->rsa->n = pgpkey->key.pubkey.key.rsa.n;
1359		key->dsa->p = pgpkey->key.pubkey.key.dsa.p;
1360		key->dsa->q = pgpkey->key.pubkey.key.dsa.q;
1361		key->dsa->g = pgpkey->key.pubkey.key.dsa.g;
1362		key->dsa->pub_key = pgpkey->key.pubkey.key.dsa.y;
1363		key->dsa->priv_key = pgpkey->key.seckey.key.dsa.x;
1364		break;
1365	default:
1366		(void) fprintf(stderr, "weird type\n");
1367		return 0;
1368	}
1369	return 1;
1370}
1371#endif
1372