diff options
Diffstat (limited to 'sqwebmail/gpg.c')
| -rw-r--r-- | sqwebmail/gpg.c | 746 | 
1 files changed, 746 insertions, 0 deletions
| diff --git a/sqwebmail/gpg.c b/sqwebmail/gpg.c new file mode 100644 index 0000000..f07d605 --- /dev/null +++ b/sqwebmail/gpg.c @@ -0,0 +1,746 @@ +/* +** Copyright 2001-2011 Double Precision, Inc.  See COPYING for +** distribution information. +*/ + + +/* +*/ +#include	"sqwebmail.h" +#include	"config.h" +#include	"gpg.h" +#include	"pref.h" +#include	"cgi/cgi.h" +#include	"gpglib/gpglib.h" +#include	"unicode/unicode.h" +#include	"numlib/numlib.h" +#include	"rfc822/rfc822.h" +#include	"htmllibdir.h" +#include	<stdio.h> +#include	<string.h> +#include	<errno.h> +#if HAVE_SYS_WAIT_H +#include	<sys/wait.h> +#endif +#if HAVE_FCNTL_H +#include	<fcntl.h> +#endif + +#ifndef WEXITSTATUS +#define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) +#endif +#ifndef WIFEXITED +#define WIFEXITED(stat_val) (((stat_val) & 255) == 0) +#endif + +extern void output_scriptptrget(); +extern void print_attrencodedlen(const char *, size_t, int, FILE *); +extern void print_safe(const char *); +const char *sqwebmail_content_charset; + + +static char gpgerrbuf[1024]; +static size_t gpgerrcnt=0; + +static void gpginiterr() +{ +	gpgerrcnt=0; +} + +static int gpg_error(const char *p, size_t l, void *dummy) +{ +	while (l && gpgerrcnt < sizeof(gpgerrbuf)-1) +	{ +		gpgerrbuf[gpgerrcnt++]= *p++; +		--l; +	} +	return (0); +} + +static void gpg_error_save(const char *errmsg, void *dummy) +{ +	gpg_error(errmsg, strlen(errmsg), dummy); +} + +int gpgbadarg(const char *p) +{ +	for ( ; *p; p++) +	{ +		int c=(unsigned char)*p; + +		if (c < ' ' || strchr("\",'`;*?()<>", c)) +			return (1); +	} +	return (0); +} + +static void dump_error() +{ +	if (gpgerrcnt >= 0) +	{ +		printf("<span style=\"color: #e00000\"><pre class=\"gpgerroutput\">"); +		print_attrencodedlen (gpgerrbuf, gpgerrcnt, 1, stdout); +		printf("</pre></span>\n"); +	} +} + +struct listinfo { +	int issecret; +	const char *default_key; +} ; + +static int show_key(const char *fingerprint, const char *shortname, +                    const char *key, int invalid, +		    struct gpg_list_info *gli) +{ +	struct listinfo *li=(struct listinfo *)gli->voidarg; + +	printf("<tr valign=\"middle\" class=\"%s\"><td>" +	       "<input type=\"radio\" name=\"%s\" value=\"", +	       li->issecret ? "gpgseckey":"gpgpubkey", +	       li->issecret ? "seckeyname":"pubkeyname"); + +	print_attrencodedlen(fingerprint, strlen(fingerprint), 0, stdout); +	printf("\"%s /></td><td><span class=\"tt\">", +	       li->default_key && strcmp(li->default_key, fingerprint) == 0 +	       ? " checked=\"checked\"":""); +	print_safe(key); +	printf("</span></td></tr>\n"); +	return (0); +} + +static void listpubsec(int flag, +		       int (*callback_func)(const char *, +					    const char *, +					    const char *, +					    int, +					    struct gpg_list_info *), +		       const char *default_key +		       ) +{ +	int rc; +	struct gpg_list_info gli; +	struct listinfo li; + +	li.issecret=flag; + +	li.default_key=default_key; + +	memset(&gli, 0, sizeof(gli)); +	gli.charset=sqwebmail_content_charset; + +	gli.disabled_msg=getarg("DISABLED"); +	gli.revoked_msg=getarg("REVOKED"); +	gli.expired_msg=getarg("EXPIRED"); +	gli.voidarg= &li; + +	gpginiterr(); + +	rc=libmail_gpg_listkeys(GPGDIR, flag, callback_func, gpg_error, &gli); + +	if (rc) +	{ +		dump_error(); +	} +} + +void gpglistpub() +{ +	printf("<table width=\"100%%\" border=\"0\" cellspacing=\"2\" cellpadding=\"0\" class=\"gpgpubkeys\">"); +	listpubsec(0, show_key, NULL); +	printf("</table>"); +} + +void gpglistsec() +{ +	printf("<table width=\"100%%\" border=\"0\" cellspacing=\"2\" cellpadding=\"0\" class=\"gpgseckeys\">"); +	listpubsec(1, show_key, NULL); +	printf("</table>"); +} + +static int select_key(const char *fingerprint, const char *shortname, +		      const char *key, +		      struct gpg_list_info *gli, +		      int is_select) +{ +	printf("<option value=\""); +	print_attrencodedlen(fingerprint, strlen(fingerprint), 0, stdout); +	printf("\"%s>", is_select ? " selected='selected'":""); + +	print_safe(shortname); +	printf("</option>"); +	return (0); +} + +static int select_key_default(const char *fingerprint, const char *shortname, +			      const char *key, +			      int invalid, +			      struct gpg_list_info *gli) +{ +	struct listinfo *li=(struct listinfo *)gli->voidarg; + +	return (select_key(fingerprint, shortname, key, gli, +			   li->default_key && strcmp(li->default_key, +						     fingerprint) +			   == 0)); +} + +void gpgselectkey() +{ +	char *default_key=pref_getdefaultgpgkey(); + +	listpubsec(1, select_key_default, default_key); + +	if (default_key) +		free(default_key); +} + +void gpgselectpubkey() +{ +	listpubsec(0, select_key_default, NULL); +} + +void gpgselectprivkey() +{ +	listpubsec(1, select_key_default, NULL); +} + +/* +** Check if this encryption key address is included in the list of recipients +** of the message. +*/ + +static int knownkey(const char *shortname, const char *known_keys) +{ +	struct rfc822t *t=rfc822t_alloc_new(shortname, NULL, NULL); +	struct rfc822a *a; +	int i; + +	if (!t) +		return (0); + +	a=rfc822a_alloc(t); + +	if (!a) +	{ +		rfc822t_free(t); +		return (0); +	} + +	for (i=0; i<a->naddrs; i++) +	{ +		char *p=rfc822_getaddr(a, i); +		int plen; +		const char *q; + +		if (!p) +			continue; + +		if (!*p) +		{ +			free(p); +			continue; +		} + +		plen=strlen(p); + +		for (q=known_keys; *q; ) +		{ +			if (strncasecmp(q, p, plen) == 0 && q[plen] == '\n') +			{ +				free(p); +				rfc822a_free(a); +				rfc822t_free(t); +				return (1); +			} + +			while (*q) +				if (*q++ == '\n') +					break; +		} +		free(p); +	} +	rfc822a_free(a); +	rfc822t_free(t); +	return (0); +} + +static int encrypt_key_default(const char *fingerprint, const char *shortname, +			       const char *key, +			       int invalid, +			       struct gpg_list_info *gli) +{ +	struct listinfo *li=(struct listinfo *)gli->voidarg; + +	if (invalid) +		return (0); + +	return (select_key(fingerprint, shortname, key, gli, +			   knownkey(shortname, li->default_key))); +} + +void gpgencryptkeys(const char *select_keys) +{ +	listpubsec(0, encrypt_key_default, select_keys); +} + + +/* +** Create a new key +*/ + +static int dump_func(const char *p, size_t l, void *vp) +{ +	int *ip=(int *)vp; + +	while (l) +	{ +		if (*ip >= 80) +		{ +			printf("\n"); +			*ip=0; +		} + +		++*ip; + +		switch (*p) { +		case '<': +			printf("<"); +			break; +		case '>': +			printf(">"); +			break; +		case '\n': +			*ip=0; +			/* FALLTHROUGH */ +		default: +			putchar(*p); +			break; +		} + +		++p; +		--l; +	} +	fflush(stdout); +	return (0); +} + +static int timeout_func(void *vp) +{ +	return (dump_func("*", 1, vp)); +} + +void gpgcreate() +{ +	int linelen; + +	const char *newname=cgi("newname"); +	const char *newaddress=cgi("newaddress"); +	const char *newcomment=cgi("newcomment"); +	unsigned skl=atoi(cgi("skeylength")); +	unsigned ekl=atoi(cgi("ekeylength")); +	unsigned newexpire=atoi(cgi("newexpire")); +	char newexpirewhen=*cgi("newexpirewhen"); +	const char *passphrase, *p; + +	if (*newname == 0 || *newaddress == 0 || strchr(newaddress, '@') == 0 +	    || gpgbadarg(newname) || gpgbadarg(newaddress) +	    || gpgbadarg(newcomment) +	    || ekl < 512 || ekl > 2048 || skl < 512 || skl > 1024) +	{ +		printf("%s\n", getarg("BADARGS")); +		return; +	} +	passphrase=cgi("passphrase"); +	if (strcmp(passphrase, cgi("passphrase2"))) +	{ +		printf("%s\n", getarg("PASSPHRASEFAIL")); +		return; +	} + +	for (p=passphrase; *p; p++) +	{ +		if ((int)(unsigned char)*p < ' ') +		{ +			printf("%s\n", getarg("PASSPHRASEFAIL")); +			return; +		} +	} + +	printf("<pre class=\"gpgcreate\">"); + +	linelen=0; + +	libmail_gpg_genkey(GPGDIR, sqwebmail_content_charset, +			   newname, newaddress, newcomment, +			   skl, ekl, +			   newexpire, newexpirewhen, +			   passphrase, +			   &dump_func, +			   &timeout_func, +			   &linelen); +	printf("</pre>"); +} + +static void delkey(const char *keyname, int flag) +{ +	int rc; + +	if (gpgbadarg(keyname)) +		return; + +	gpginiterr(); + +	rc=libmail_gpg_deletekey(GPGDIR, flag, keyname, gpg_error, NULL); + +	if (rc) +	{ +		printf("<div class=\"indent\">%s\n", getarg("DELETEFAIL")); +		dump_error(); +		printf("</div>\n"); +	} +} + +static FILE *passphrasefp() +{ +	FILE *fp=NULL; +	const char *passphrase; + +	passphrase=cgi("passphrase"); +	if (*passphrase) +	{ +		fp=tmpfile(); +		if (fp) +		{ +			fprintf(fp, "%s", passphrase); +			if (fflush(fp) || ferror(fp) +			    || lseek(fileno(fp), 0L, SEEK_SET) < 0 +			    || fcntl(fileno(fp), F_SETFD, 0) < 0) +			{ +				fclose(fp); +				fp=NULL; +			} +		} +	} +	return (fp); +} + +static void signkey(const char *signthis, const char *signwith, +		    const char *trustlevel) +{ +	int rc; +	FILE *fp=NULL; + +	int n=atoi(trustlevel); + +	if (n < 0 || n > 9) +		n=0; + +	if (gpgbadarg(signthis) || gpgbadarg(signwith)) +		return; + +	gpginiterr(); + + +	fp=passphrasefp(); + +	rc=libmail_gpg_signkey(GPGDIR, signthis, signwith, +			       fp ? fileno(fp):-1, gpg_error, n, NULL); + +	if (fp) +		fclose(fp); + +	if (rc) +	{ +		printf("<div class=\"indent\">%s\n", getarg("SIGNFAIL")); +		dump_error(); +		printf("</div>\n"); +	} +} + +static void setdefault(const char *def) +{ +	if (gpgbadarg(def)) +		return; + +	pref_setdefaultgpgkey(def); +} + +void gpgdo() +{ +	if (*cgi("delpub")) +		delkey(cgi("pubkeyname"), 0); +	else if (*cgi("delsec") && *cgi("really")) +		delkey(cgi("seckeyname"), 1); +	else if (*cgi("sign")) +		signkey(cgi("pubkeyname"), cgi("seckeyname"), +			cgi("signlevel")); +	else if (*cgi("setdefault")) +		setdefault(cgi("seckeyname")); +} + +static char gpgerrbuf[1024]; + +static int read_fd(char *buf, size_t cnt, void *vp) +{ +	FILE *fp=(FILE *)vp; +	size_t i; +	int c; + +	if (cnt == 0) +		return -1; + +	--cnt; + +	for (i=0; i<cnt; i++) +	{ +		if ((c=getc(fp)) == EOF) +		{ +			if (i == 0) +				return -1; +			break; +		} +		buf[i]=c; + +		if (c == '\n') +		{ +			++i; +			break; +		} +	} +	buf[i]=0; +	return 0; +} + +static void write_fd(const char *p, size_t n, void *dummy) +{ +	if (n == 0) +		return; + +	if (fwrite(p, n, 1, (FILE *)dummy) != 1) +		exit(1); +} + +int gpgdomsg(int in_fd, int out_fd, const char *signkey, +	     const char *encryptkeys) +{ +	char *k=strdup(encryptkeys ? encryptkeys:""); +	int n; +	int i; +	char *p; +	char **argvec; +	FILE *passfd=NULL; +	char passfd_buf[NUMBUFSIZE]; +	struct libmail_gpg_info gi; + +	int in_dup, out_dup; +	FILE *in_fp, *out_fp; + +	gpginiterr(); + +	if (!k) +	{ +		enomem(); +		return 1; +	} + +	if ((in_dup=dup(in_fd)) < 0 || (in_fp=fdopen(in_dup, "r")) == NULL) +	{ +		if (in_dup >= 0) +			close(in_dup); +		free(k); +		enomem(); +		return 1; +	} + +	if ((out_dup=dup(out_fd)) < 0 || (out_fp=fdopen(out_dup, "w")) == NULL) +	{ +		if (out_dup >= 0) +			close(out_dup); +		fclose(in_fp); +		close(in_dup); +		free(k); +		enomem(); +		return 1; +	} + +	passfd=passphrasefp(); + +	n=0; +	for (p=k; (p=strtok(p, " ")) != NULL; p=NULL) +		++n; + +	argvec=malloc((n * 2 + 22)*sizeof(char *)); +	if (!argvec) +	{ +		fclose(out_fp); +		close(out_dup); +		fclose(in_fp); +		close(in_dup); +		free(k); +		enomem(); +		return 1; +	} + +	memset(&gi, 0, sizeof(gi)); + +	gi.gnupghome=GPGDIR; +	if (passfd) +	{ +		gi.passphrase_fd=libmail_str_size_t(fileno(passfd), +						    passfd_buf); +	} + +	gi.input_func= read_fd; +	gi.input_func_arg= in_fp; +	gi.output_func= write_fd; +	gi.output_func_arg= out_fp; +	gi.errhandler_func= gpg_error_save; +	gi.errhandler_arg= NULL; + + +	i=0; +	argvec[i++] = "--no-tty"; +	if (signkey) +	{ +		argvec[i++]="--default-key"; +		argvec[i++]=(char *)signkey; +	} + +	argvec[i++]="--always-trust"; + +	for (p=strcpy(k, encryptkeys ? encryptkeys:""); +	     (p=strtok(p, " ")) != NULL; p=NULL) +	{ +		argvec[i++]="-r"; +		argvec[i++]=p; +	} +	argvec[i]=0; +	gi.argc=i; +	gi.argv=argvec; + +	i=libmail_gpg_signencode(signkey ? 1:0, +				 n > 0 ? LIBMAIL_GPG_ENCAPSULATE:0, +				 &gi); + +	free(argvec); +	fclose(out_fp); +	close(out_dup); +	fclose(in_fp); +	close(in_dup); +	free(k); +	if (passfd) +		fclose(passfd); + +	return i; +} + +void sent_gpgerrtxt() +{ +	const char *p; + +	for (p=gpgerrbuf; *p; p++) +	{ +		switch (*p) { +		case '<': +			printf("<"); +			break; +		case '>': +			printf(">"); +			break; +		default: +			putchar((int)(unsigned char)*p); +			break; +		} +	} +} + +void sent_gpgerrresume() +{ +	output_scriptptrget(); +	printf("&form=newmsg&pos=%s&draft=%s", cgi("pos"), +	       cgi("draftmessage")); +} + +int gpgdecode(int in_fd, int out_fd) +{ +	char passfd_buf[NUMBUFSIZE]; +	FILE *fp=passphrasefp(); +	int in_dup, out_dup; +	FILE *in_fp, *out_fp; +	struct libmail_gpg_info gi; +	char *argvec[2]; +	int i; + +	gpginiterr(); + +	if ((in_dup=dup(in_fd)) < 0 || (in_fp=fdopen(in_dup, "r")) == NULL) +	{ +		if (in_dup >= 0) +			close(in_dup); +		fclose(fp); +		enomem(); +		return 1; +	} + +	if ((out_dup=dup(out_fd)) < 0 || (out_fp=fdopen(out_dup, "w")) == NULL) +	{ +		if (out_dup >= 0) +			close(out_dup); +		fclose(in_fp); +		close(in_dup); +		fclose(fp); +		enomem(); +		return 1; +	} + +	memset(&gi, 0, sizeof(gi)); + +	gi.gnupghome=GPGDIR; +	if (fp) +	{ +		gi.passphrase_fd=libmail_str_size_t(fileno(fp), passfd_buf); +	} + +	gi.input_func= read_fd; +	gi.input_func_arg= in_fp; +	gi.output_func= write_fd; +	gi.output_func_arg= out_fp; +	gi.errhandler_func= gpg_error_save; +	gi.errhandler_arg= NULL; + +	argvec[0] = "--no-tty"; +	argvec[1]=NULL; +	gi.argc=1; +	gi.argv=argvec; + +	i=libmail_gpg_decode(LIBMAIL_GPG_UNENCRYPT|LIBMAIL_GPG_CHECKSIGN, +			     &gi); +	fclose(out_fp); +	close(out_dup); +	fclose(in_fp); +	close(in_dup); +	if (fp) +		fclose(fp); + +	if (i) +	{ +		printf("<div class=\"indent\"><pre style=\"color: red;\">"); +		sent_gpgerrtxt(); +		printf("</pre></div>\n"); +	} +	return (i); +} + +int gpgexportkey(const char *fingerprint, int issecret, +		 int (*func)(const char *, size_t, void *), +		 void *arg) +{ +	gpginiterr(); + +	return (libmail_gpg_exportkey(GPGDIR, issecret, fingerprint, +				      func, +				      gpg_error, +				      arg)); +} + | 
