summaryrefslogtreecommitdiffstats
path: root/tcpd/tlsclient.c
diff options
context:
space:
mode:
authorSam Varshavchik2013-08-19 16:39:41 -0400
committerSam Varshavchik2013-08-25 14:43:51 -0400
commit9c45d9ad13fdf439d44d7443ae75da15ea0223ed (patch)
tree7a81a04cb51efb078ee350859a64be2ebc6b8813 /tcpd/tlsclient.c
parenta9520698b770168d1f33d6301463bb70a19655ec (diff)
downloadcourier-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 'tcpd/tlsclient.c')
-rw-r--r--tcpd/tlsclient.c541
1 files changed, 541 insertions, 0 deletions
diff --git a/tcpd/tlsclient.c b/tcpd/tlsclient.c
new file mode 100644
index 0000000..f68bb16
--- /dev/null
+++ b/tcpd/tlsclient.c
@@ -0,0 +1,541 @@
+/*
+** Copyright 2001-2008 Double Precision, Inc.
+** See COPYING for distribution information.
+*/
+#include "config.h"
+#include "numlib/numlib.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+#if HAVE_DIRENT_H
+#include <dirent.h>
+#define NAMLEN(dirent) strlen((dirent)->d_name)
+#else
+#define dirent direct
+#define NAMLEN(dirent) (dirent)->d_namlen
+#if HAVE_SYS_NDIR_H
+#include <sys/ndir.h>
+#endif
+#if HAVE_SYS_DIR_H
+#include <sys/dir.h>
+#endif
+#if HAVE_NDIR_H
+#include <ndir.h>
+#endif
+#endif
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include <errno.h>
+#include <sys/time.h>
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+#ifndef WEXITSTATUS
+#define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
+#endif
+#ifndef WIFEXITED
+#define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
+#endif
+
+#include "tlsclient.h"
+
+
+
+#define ERRMSG(s) (cinfo->errmsg[0]=0, \
+ strncat(cinfo->errmsg, (s), sizeof(cinfo->errmsg)-3))
+
+#define SYSERRMSG (strncat(strcpy(cinfo->errmsg, "Failed: "), \
+ strerror(errno), sizeof(cinfo->errmsg)-15))
+
+void couriertls_init(struct couriertls_info *cinfo)
+{
+ memset(cinfo, 0, sizeof(*cinfo));
+ cinfo->cipher=cinfo->version="Unknown";
+}
+
+/*
+** Convenient function to start couriertls, return any client certificate
+** error message, and the x509 certificate info.
+*/
+
+static int do_couriertls_start(char **, struct couriertls_info *);
+
+int couriertls_start(char **args, struct couriertls_info *cinfo)
+{
+ int rc=do_couriertls_start(args, cinfo);
+ int l;
+ char *p;
+
+ if (rc && cinfo->errmsg[0] == 0)
+ strcpy(cinfo->errmsg, "Failed to initialize TLS/SSL\n");
+
+ l=strlen(cinfo->errmsg);
+
+ while (l > 0 && cinfo->errmsg[l-1] == '\n')
+ --l;
+ cinfo->errmsg[l]=0;
+
+ if (rc || cinfo->x509info == 0)
+ return (rc);
+
+ cinfo->x509info[cinfo->x509info_len]=0;
+ p=strtok(cinfo->x509info, "\r\n");
+
+ while (p)
+ {
+ int i;
+
+ for (i=0; p[i]; i++)
+ if (!isalpha(p[i]))
+ break;
+
+ if (p[i] != ':')
+ {
+ p=strtok(NULL, "\r\n");
+ continue;
+ }
+ p[i++]=0;
+
+ /*
+ ** IMPORTANT: UCase *MUST* match the output of couriertls.
+ ** I'd love to use strcasecmp, here, but certain glibc
+ ** locale break the standard case of lower ascii chset
+ ** range.
+ */
+
+ if (strcmp(p, "Subject") == 0)
+ {
+ struct tls_subject *subj, *subj2;
+ struct tls_subjitem **itemptr;
+
+ p += i;
+
+ for (subj=cinfo->first_subject; subj && subj->next;
+ subj=subj->next)
+ ;
+
+ subj2=(struct tls_subject *)
+ malloc(sizeof(struct tls_subject));
+ if (!subj2)
+ {
+ SYSERRMSG;
+ return (-1);
+ }
+
+ if (subj)
+ subj->next=subj2;
+ else
+ cinfo->first_subject=subj2;
+
+ subj2->next=0;
+ subj2->firstitem=0;
+ itemptr= &subj2->firstitem;
+
+ while ( p && (*p == 0
+ || isspace((int)(unsigned char)*p)))
+ {
+ while (*p && isspace((int)(unsigned char)*p))
+ ++p;
+ for (i=0; p[i]; i++)
+ if (!isalpha((int)(unsigned char)p[i]))
+ break;
+ if (p[i] != '=')
+ {
+ p=strtok(NULL, "\r\n");
+ continue;
+ }
+ p[i++]=0;
+
+ *itemptr= (struct tls_subjitem *)
+ malloc(sizeof (struct tls_subjitem));
+
+ if (!*itemptr)
+ {
+ SYSERRMSG;
+ return (-1);
+ }
+
+ (*itemptr)->name=p;
+ (*itemptr)->value=p+i;
+ (*itemptr)->nextitem=0;
+
+ itemptr= &(*itemptr)->nextitem;
+ p=strtok(NULL, "\r\n");
+ }
+ continue;
+ }
+
+ if (strcmp(p, "Cipher") == 0)
+ {
+ p += i;
+ while (*p && isspace((int)(unsigned char)*p))
+ ++p;
+ cinfo->cipher=p;
+ }
+ else if (strcmp(p, "Version") == 0)
+ {
+ p += i;
+ while (*p && isspace((int)(unsigned char)*p))
+ ++p;
+ cinfo->version=p;
+ }
+ else if (strcmp(p, "Bits") == 0)
+ {
+ p += i;
+ while (*p && isspace((int)(unsigned char)*p))
+ ++p;
+ cinfo->bits=atoi(p);
+ }
+ p=strtok(NULL, "\r\n");
+ }
+
+ return (0);
+}
+
+const char *couriertls_get_subject(struct couriertls_info *cinfo,
+ const char *subject)
+{
+ struct tls_subject *subj;
+ struct tls_subjitem *item, *p;
+
+ if ((subj=cinfo->first_subject) == 0)
+ return NULL;
+
+ p=NULL;
+
+ for (item=subj->firstitem; item; item=item->nextitem)
+ {
+ const char *a=item->name;
+ const char *b=subject;
+
+ while (*a && *b)
+ {
+ int ca= *a++;
+ int cb= *b++;
+
+ /* Locale muddies things up, do this by hand */
+
+ if (ca >= 'a' && ca <= 'z')
+ ca -= 'a' - 'A';
+
+ if (cb >= 'a' && cb <= 'z')
+ cb -= 'a' - 'A';
+
+ if (ca != cb)
+ break;
+ }
+
+ if (!*a && !*b)
+ p=item;
+ /*
+ ** We want the last one, to match the behavior when couriertls
+ ** passes this stuff via the environment.
+ */
+ }
+
+ if (p)
+ return p->value;
+ return (0);
+}
+
+void couriertls_export_subject_environment(struct couriertls_info *cinfo)
+{
+ struct tls_subject *subj;
+ struct tls_subjitem *item;
+
+ if ((subj=cinfo->first_subject) == 0)
+ return;
+
+ for (item=subj->firstitem; item; item=item->nextitem)
+ {
+ char *a=malloc(strlen(item->name)+20);
+ const char *b=item->value;
+ char *p;
+
+ if (!a) continue;
+
+ strcat(strcpy(a, "TLS_SUBJECT_"), item->name);
+
+ for (p=a; *p; p++)
+ if (*p >= 'a' && *p <= 'z')
+ *p -= 'a' - 'A';
+
+ setenv(a, b, 1);
+ free(a);
+ }
+}
+
+static int do_couriertls_start(char **args, struct couriertls_info *cinfo)
+{
+ pid_t p, p2;
+ int waitstat;
+ char **argvec;
+ int nargs;
+ char readbuf[BUFSIZ];
+ fd_set fdr;
+ int statuspipe_fd[2];
+ int x509_fd[2];
+
+
+ /* Create the pipes, and run couriertls */
+
+ for (nargs=0; args[nargs]; nargs++)
+ ;
+
+ argvec=malloc(sizeof(char *)*(nargs+10));
+ if (!argvec)
+ {
+ SYSERRMSG;
+ return (-1);
+ }
+
+ if (pipe(statuspipe_fd) < 0)
+ {
+ free(argvec);
+ SYSERRMSG;
+ return (-1);
+ }
+
+ if (pipe(x509_fd) < 0)
+ {
+ close(statuspipe_fd[0]);
+ close(statuspipe_fd[1]);
+ free(argvec);
+ SYSERRMSG;
+ return (-1);
+ }
+
+ if ((p=fork()) < 0)
+ {
+ close(x509_fd[0]);
+ close(x509_fd[1]);
+ close(statuspipe_fd[0]);
+ close(statuspipe_fd[1]);
+ free(argvec);
+ SYSERRMSG;
+ return (-1);
+ }
+
+ /* Child process starts another child process, which runs couriertls */
+
+ if (p == 0)
+ {
+ static const char msg[]="500 Unable to start couriertls - insufficient resources.\n";
+
+ FILE *fp;
+ char miscbuf[NUMBUFSIZE];
+ char statusfd_buf[NUMBUFSIZE+40];
+ char x509fd_buf[NUMBUFSIZE+40];
+ const char *s;
+
+ close(statuspipe_fd[0]);
+ close(x509_fd[0]);
+
+ fp=fdopen(statuspipe_fd[1], "w");
+
+ if (!fp)
+ {
+ if (write(statuspipe_fd[1], msg, sizeof(msg)-1) < 0)
+ ; /* Ignore */
+ exit(0);
+ }
+
+ if ((p=fork()) != 0)
+ {
+ if (p < 0)
+ {
+ fprintf(fp,
+ "500 Unable to start couriertls: %s\n",
+ strerror(errno));
+ fflush(fp);
+ }
+ exit(0);
+ }
+
+ argvec[0]="couriertls";
+ argvec[1]=strcat(strcpy(statusfd_buf, "-statusfd="),
+ libmail_str_size_t(statuspipe_fd[1], miscbuf));
+ argvec[2]=strcat(strcpy(x509fd_buf, "-printx509="),
+ libmail_str_size_t(x509_fd[1], miscbuf));
+ for (nargs=0; (argvec[nargs+3]=args[nargs]) != 0; nargs++)
+ ;
+
+ s=getenv("COURIERTLS");
+ if (!s || !*s)
+ s="couriertls";
+
+ execv(s, argvec);
+ fprintf(fp, "500 Unable to start couriertls: %s\n",
+ strerror(errno));
+ fflush(fp);
+ exit(0);
+ }
+
+ /* The parent wait for the first child to exit */
+
+ close(statuspipe_fd[1]);
+ close(x509_fd[1]);
+
+ while ((p2=wait(&waitstat)) != p)
+ if (p2 < 0 && errno == ECHILD)
+ break;
+
+ if (p2 != p || !WIFEXITED(waitstat) || WEXITSTATUS(waitstat))
+ {
+ close(statuspipe_fd[0]);
+ close(x509_fd[0]);
+ ERRMSG("500 Error starting couriertls.");
+ return (-1);
+ }
+
+ /* Now, we need to read from two pipes simultaneously, and save the
+ ** results.
+ */
+
+ while (statuspipe_fd[0] >= 0 || x509_fd[0] >= 0)
+ {
+ FD_ZERO(&fdr);
+ if (statuspipe_fd[0] >= 0)
+ FD_SET(statuspipe_fd[0], &fdr);
+ if (x509_fd[0] >= 0)
+ FD_SET(x509_fd[0], &fdr);
+ if (select( (statuspipe_fd[0] > x509_fd[0] ?
+ statuspipe_fd[0]:x509_fd[0])+1,
+ &fdr, NULL, NULL, NULL) < 0)
+ {
+ close(statuspipe_fd[0]);
+ close(x509_fd[0]);
+ SYSERRMSG;
+ return (-1);
+ }
+
+ if (statuspipe_fd[0] >= 0 && FD_ISSET(statuspipe_fd[0], &fdr))
+ {
+ int n=read(statuspipe_fd[0], readbuf,
+ sizeof(readbuf)-1);
+
+ if (n <= 0)
+ {
+ close(statuspipe_fd[0]);
+ statuspipe_fd[0]= -1;
+ }
+ else
+ {
+ int l=strlen(cinfo->errmsg);
+
+ readbuf[n]=0;
+ if (l < sizeof(cinfo->errmsg)-2)
+ strncat(cinfo->errmsg, readbuf,
+ sizeof(cinfo->errmsg)-2-l);
+ }
+ }
+
+ if (x509_fd[0] >= 0 && FD_ISSET(x509_fd[0], &fdr))
+ {
+ int n=read(x509_fd[0], readbuf, sizeof(readbuf));
+
+ if (n <= 0)
+ {
+ close(x509_fd[0]);
+ x509_fd[0]= -1;
+ }
+ else
+ {
+ if (n + cinfo->x509info_len >=
+ cinfo->x509info_size)
+ {
+ size_t news=n+cinfo->x509info_len
+ + 1024;
+ char *newp= cinfo->x509info ?
+ realloc(cinfo->x509info, news)
+ : malloc(news);
+
+ if (!newp)
+ {
+ SYSERRMSG;
+ close(x509_fd[0]);
+ x509_fd[0]= -1;
+ continue;
+ }
+ cinfo->x509info=newp;
+ cinfo->x509info_size=news;
+ }
+
+ memcpy(cinfo->x509info + cinfo->x509info_len,
+ readbuf, n);
+ cinfo->x509info_len += n;
+ }
+ }
+
+ }
+ return (cinfo->errmsg[0] ? -1:0);
+}
+
+void couriertls_destroy(struct couriertls_info *info)
+{
+ struct tls_subject *subj;
+ struct tls_subjitem *subjitem;
+
+ if (info->x509info)
+ free(info->x509info);
+
+ while ((subj=info->first_subject) != 0)
+ {
+ info->first_subject=subj->next;
+ while ((subjitem=subj->firstitem) != 0)
+ {
+ subj->firstitem=subjitem->nextitem;
+ free(subjitem);
+ }
+ free(subj);
+ }
+}
+
+#if 0
+int main(int argc, char **argv)
+{
+ struct couriertls_info cinfo;
+ struct tls_subject *subj;
+ struct tls_subjitem *subjitem;
+
+ couriertls_init(&cinfo);
+
+ if (couriertls_start(argv+1, &cinfo))
+ {
+ printf("ERROR: %s\n",
+ cinfo.errmsg[0] ? cinfo.errmsg:"unknown error");
+ exit(0);
+ }
+
+ printf("version=%s, cipher=%s, bits=%d\n", cinfo.cipher,
+ cinfo.version, cinfo.bits);
+
+ for (subj=cinfo.first_subject; subj; subj=subj->next)
+ {
+ printf("Subject: ");
+
+ for (subjitem=subj->firstitem; subjitem;
+ subjitem=subjitem->nextitem)
+ {
+ printf("/%s=%s", subjitem->name,
+ subjitem->value);
+ }
+ printf("\n");
+ }
+ couriertls_destroy(&cinfo);
+ sleep(300);
+ exit(0);
+}
+#endif