diff options
| author | Sam Varshavchik | 2013-08-19 16:39:41 -0400 |
|---|---|---|
| committer | Sam Varshavchik | 2013-08-25 14:43:51 -0400 |
| commit | 9c45d9ad13fdf439d44d7443ae75da15ea0223ed (patch) | |
| tree | 7a81a04cb51efb078ee350859a64be2ebc6b8813 /sqwebmail/gpg.c | |
| parent | a9520698b770168d1f33d6301463bb70a19655ec (diff) | |
| download | courier-libs-9c45d9ad13fdf439d44d7443ae75da15ea0223ed.tar.bz2 | |
Initial checkin
Imported from subversion report, converted to git. Updated all paths in
scripts and makefiles, reflecting the new directory hierarchy.
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)); +} + |
