Back to index

courier  0.68.2
tlsclient.c
Go to the documentation of this file.
00001 /*
00002 ** Copyright 2001-2008 Double Precision, Inc.
00003 ** See COPYING for distribution information.
00004 */
00005 #include      "config.h"
00006 #include      "numlib/numlib.h"
00007 #include      <stdio.h>
00008 #include      <string.h>
00009 #include      <stdlib.h>
00010 #include      <errno.h>
00011 #include      <ctype.h>
00012 #if HAVE_DIRENT_H
00013 #include <dirent.h>
00014 #define NAMLEN(dirent) strlen((dirent)->d_name)
00015 #else
00016 #define dirent direct
00017 #define NAMLEN(dirent) (dirent)->d_namlen
00018 #if HAVE_SYS_NDIR_H
00019 #include <sys/ndir.h>
00020 #endif
00021 #if HAVE_SYS_DIR_H
00022 #include <sys/dir.h>
00023 #endif
00024 #if HAVE_NDIR_H
00025 #include <ndir.h>
00026 #endif
00027 #endif
00028 #if    HAVE_UNISTD_H
00029 #include      <unistd.h>
00030 #endif
00031 #if    HAVE_FCNTL_H
00032 #include      <fcntl.h>
00033 #endif
00034 #include      <errno.h>
00035 #include      <sys/time.h>
00036 #if    HAVE_SYS_TYPES_H
00037 #include      <sys/types.h>
00038 #endif
00039 #if    HAVE_SYS_STAT_H
00040 #include      <sys/stat.h>
00041 #endif
00042 #if    HAVE_SYS_WAIT_H
00043 #include      <sys/wait.h>
00044 #endif
00045 #ifndef WEXITSTATUS
00046 #define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
00047 #endif
00048 #ifndef WIFEXITED
00049 #define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
00050 #endif
00051 
00052 #include      "tlsclient.h"
00053 
00054 
00055 
00056 #define ERRMSG(s) (cinfo->errmsg[0]=0, \
00057               strncat(cinfo->errmsg, (s), sizeof(cinfo->errmsg)-3))
00058 
00059 #define SYSERRMSG (strncat(strcpy(cinfo->errmsg, "Failed: "), \
00060               strerror(errno), sizeof(cinfo->errmsg)-15))
00061 
00062 void couriertls_init(struct couriertls_info *cinfo)
00063 {
00064        memset(cinfo, 0, sizeof(*cinfo));
00065        cinfo->cipher=cinfo->version="Unknown";
00066 }
00067 
00068 /*
00069 ** Convenient function to start couriertls, return any client certificate
00070 ** error message, and the x509 certificate info.
00071 */
00072 
00073 static int do_couriertls_start(char **, struct couriertls_info *);
00074 
00075 int couriertls_start(char **args, struct couriertls_info *cinfo)
00076 {
00077        int rc=do_couriertls_start(args, cinfo);
00078        int l;
00079        char *p;
00080 
00081        if (rc && cinfo->errmsg[0] == 0)
00082               strcpy(cinfo->errmsg, "Failed to initialize TLS/SSL\n");
00083 
00084        l=strlen(cinfo->errmsg);
00085 
00086        while (l > 0 && cinfo->errmsg[l-1] == '\n')
00087               --l;
00088        cinfo->errmsg[l]=0;
00089 
00090        if (rc || cinfo->x509info == 0)
00091               return (rc);
00092 
00093        cinfo->x509info[cinfo->x509info_len]=0;
00094        p=strtok(cinfo->x509info, "\r\n");
00095 
00096        while (p)
00097        {
00098               int i;
00099 
00100               for (i=0; p[i]; i++)
00101                      if (!isalpha(p[i]))
00102                             break;
00103 
00104               if (p[i] != ':')
00105               {
00106                      p=strtok(NULL, "\r\n");
00107                      continue;
00108               }
00109               p[i++]=0;
00110 
00111               /*
00112               ** IMPORTANT: UCase *MUST* match the output of couriertls.
00113               ** I'd love to use strcasecmp, here, but certain glibc
00114               ** locale break the standard case of lower ascii chset
00115               ** range.
00116               */
00117 
00118               if (strcmp(p, "Subject") == 0)
00119               {
00120                      struct tls_subject *subj, *subj2;
00121                      struct tls_subjitem **itemptr;
00122 
00123                      p += i;
00124 
00125                      for (subj=cinfo->first_subject; subj && subj->next;
00126                           subj=subj->next)
00127                             ;
00128 
00129                      subj2=(struct tls_subject *)
00130                             malloc(sizeof(struct tls_subject));
00131                      if (!subj2)
00132                      {
00133                             SYSERRMSG;
00134                             return (-1);
00135                      }
00136 
00137                      if (subj)
00138                             subj->next=subj2;
00139                      else
00140                             cinfo->first_subject=subj2;
00141 
00142                      subj2->next=0;
00143                      subj2->firstitem=0;
00144                      itemptr= &subj2->firstitem;
00145 
00146                      while ( p && (*p == 0
00147                                   || isspace((int)(unsigned char)*p)))
00148                      {
00149                             while (*p && isspace((int)(unsigned char)*p))
00150                                    ++p;
00151                             for (i=0; p[i]; i++)
00152                                    if (!isalpha((int)(unsigned char)p[i]))
00153                                           break;
00154                             if (p[i] != '=')
00155                             {
00156                                    p=strtok(NULL, "\r\n");
00157                                    continue;
00158                             }
00159                             p[i++]=0;
00160 
00161                             *itemptr= (struct tls_subjitem *)
00162                                    malloc(sizeof (struct tls_subjitem));
00163 
00164                             if (!*itemptr)
00165                             {
00166                                    SYSERRMSG;
00167                                    return (-1);
00168                             }
00169 
00170                             (*itemptr)->name=p;
00171                             (*itemptr)->value=p+i;
00172                             (*itemptr)->nextitem=0;
00173 
00174                             itemptr= &(*itemptr)->nextitem;
00175                             p=strtok(NULL, "\r\n");
00176                      }
00177                      continue;
00178               }
00179 
00180               if (strcmp(p, "Cipher") == 0)
00181               {
00182                      p += i;
00183                      while (*p && isspace((int)(unsigned char)*p))
00184                             ++p;
00185                      cinfo->cipher=p;
00186               }
00187               else if (strcmp(p, "Version") == 0)
00188               {
00189                      p += i;
00190                      while (*p && isspace((int)(unsigned char)*p))
00191                             ++p;
00192                      cinfo->version=p;
00193               }
00194               else if (strcmp(p, "Bits") == 0)
00195               {
00196                      p += i;
00197                      while (*p && isspace((int)(unsigned char)*p))
00198                             ++p;
00199                      cinfo->bits=atoi(p);
00200               }
00201               p=strtok(NULL, "\r\n");
00202        }
00203 
00204        return (0);
00205 }
00206 
00207 const char *couriertls_get_subject(struct couriertls_info *cinfo,
00208                                const char *subject)
00209 {
00210        struct tls_subject *subj;
00211        struct tls_subjitem *item, *p;
00212 
00213        if ((subj=cinfo->first_subject) == 0)
00214               return NULL;
00215 
00216        p=NULL;
00217 
00218        for (item=subj->firstitem; item; item=item->nextitem)
00219        {
00220               const char *a=item->name;
00221               const char *b=subject;
00222 
00223               while (*a && *b)
00224               {
00225                      int ca= *a++;
00226                      int cb= *b++;
00227 
00228                      /* Locale muddies things up, do this by hand */
00229 
00230                      if (ca >= 'a' && ca <= 'z')
00231                             ca -= 'a' - 'A';
00232 
00233                      if (cb >= 'a' && cb <= 'z')
00234                             cb -= 'a' - 'A';
00235 
00236                      if (ca != cb)
00237                             break;
00238               }
00239 
00240               if (!*a && !*b)
00241                      p=item;
00242               /*
00243               ** We want the last one, to match the behavior when couriertls
00244               ** passes this stuff via the environment.
00245               */
00246        }
00247 
00248        if (p)
00249               return p->value;
00250        return (0);
00251 }
00252 
00253 void couriertls_export_subject_environment(struct couriertls_info *cinfo)
00254 {
00255        struct tls_subject *subj;
00256        struct tls_subjitem *item;
00257 
00258        if ((subj=cinfo->first_subject) == 0)
00259               return;
00260 
00261        for (item=subj->firstitem; item; item=item->nextitem)
00262        {
00263               char *a=malloc(strlen(item->name)+20);
00264               const char *b=item->value;
00265               char *p;
00266 
00267               if (!a) continue;
00268 
00269               strcat(strcpy(a, "TLS_SUBJECT_"), item->name);
00270 
00271               for (p=a; *p; p++)
00272                      if (*p >= 'a' && *p <= 'z')
00273                             *p -= 'a' - 'A';
00274 
00275               setenv(a, b, 1);
00276               free(a);
00277        }
00278 }
00279 
00280 static int do_couriertls_start(char **args, struct couriertls_info *cinfo)
00281 {
00282        pid_t p, p2;
00283        int waitstat;
00284        char **argvec;
00285        int nargs;
00286        char readbuf[BUFSIZ];
00287        fd_set fdr;
00288        int statuspipe_fd[2];
00289        int x509_fd[2];
00290 
00291 
00292        /* Create the pipes, and run couriertls */
00293 
00294        for (nargs=0; args[nargs]; nargs++)
00295               ;
00296 
00297        argvec=malloc(sizeof(char *)*(nargs+10));
00298        if (!argvec)
00299        {
00300               SYSERRMSG;
00301               return (-1);
00302        }
00303 
00304        if (pipe(statuspipe_fd) < 0)
00305        {
00306               free(argvec);
00307               SYSERRMSG;
00308               return (-1);
00309        }
00310 
00311        if (pipe(x509_fd) < 0)
00312        {
00313               close(statuspipe_fd[0]);
00314               close(statuspipe_fd[1]);
00315               free(argvec);
00316               SYSERRMSG;
00317               return (-1);
00318        }
00319 
00320        if ((p=fork()) < 0)
00321        {
00322               close(x509_fd[0]);
00323               close(x509_fd[1]);
00324               close(statuspipe_fd[0]);
00325               close(statuspipe_fd[1]);
00326               free(argvec);
00327               SYSERRMSG;
00328               return (-1);
00329        }
00330 
00331        /* Child process starts another child process, which runs couriertls */
00332 
00333        if (p == 0)
00334        {
00335               static const char msg[]="500 Unable to start couriertls - insufficient resources.\n";
00336 
00337               FILE *fp;
00338               char miscbuf[NUMBUFSIZE];
00339               char statusfd_buf[NUMBUFSIZE+40];
00340               char x509fd_buf[NUMBUFSIZE+40];
00341               const char *s;
00342 
00343               close(statuspipe_fd[0]);
00344               close(x509_fd[0]);
00345 
00346               fp=fdopen(statuspipe_fd[1], "w");
00347 
00348               if (!fp)
00349               {
00350                      if (write(statuspipe_fd[1], msg, sizeof(msg)-1) < 0)
00351                             ; /* Ignore */
00352                      exit(0);
00353               }
00354 
00355               if ((p=fork()) != 0)
00356               {
00357                      if (p < 0)
00358                      {
00359                             fprintf(fp,
00360                                    "500 Unable to start couriertls: %s\n",
00361                                    strerror(errno));
00362                             fflush(fp);
00363                      }
00364                      exit(0);
00365               }
00366 
00367               argvec[0]="couriertls";
00368               argvec[1]=strcat(strcpy(statusfd_buf, "-statusfd="),
00369                              libmail_str_size_t(statuspipe_fd[1], miscbuf));
00370               argvec[2]=strcat(strcpy(x509fd_buf, "-printx509="),
00371                              libmail_str_size_t(x509_fd[1], miscbuf));
00372               for (nargs=0; (argvec[nargs+3]=args[nargs]) != 0; nargs++)
00373                      ;
00374 
00375               s=getenv("COURIERTLS");
00376               if (!s || !*s)
00377                      s="couriertls";
00378 
00379               execv(s, argvec);
00380               fprintf(fp, "500 Unable to start couriertls: %s\n",
00381                      strerror(errno));
00382               fflush(fp);
00383               exit(0);
00384        }
00385 
00386        /* The parent wait for the first child to exit */
00387 
00388        close(statuspipe_fd[1]);
00389        close(x509_fd[1]);
00390 
00391        while ((p2=wait(&waitstat)) != p)
00392               if (p2 < 0 && errno == ECHILD)
00393                      break;
00394 
00395        if (p2 != p || !WIFEXITED(waitstat) || WEXITSTATUS(waitstat))
00396        {
00397               close(statuspipe_fd[0]);
00398               close(x509_fd[0]);
00399               ERRMSG("500 Error starting couriertls.");
00400               return (-1);
00401        }
00402 
00403        /* Now, we need to read from two pipes simultaneously, and save the
00404        ** results.
00405        */
00406 
00407        while (statuspipe_fd[0] >= 0 || x509_fd[0] >= 0)
00408        {
00409               FD_ZERO(&fdr);
00410               if (statuspipe_fd[0] >= 0)
00411                      FD_SET(statuspipe_fd[0], &fdr);
00412               if (x509_fd[0] >= 0)
00413                      FD_SET(x509_fd[0], &fdr);
00414               if (select( (statuspipe_fd[0] > x509_fd[0] ?
00415                           statuspipe_fd[0]:x509_fd[0])+1,
00416                          &fdr, NULL, NULL, NULL) < 0)
00417               {
00418                      close(statuspipe_fd[0]);
00419                      close(x509_fd[0]);
00420                      SYSERRMSG;
00421                      return (-1);
00422               }
00423 
00424               if (statuspipe_fd[0] >= 0 && FD_ISSET(statuspipe_fd[0], &fdr))
00425               {
00426                      int n=read(statuspipe_fd[0], readbuf,
00427                                sizeof(readbuf)-1);
00428 
00429                      if (n <= 0)
00430                      {
00431                             close(statuspipe_fd[0]);
00432                             statuspipe_fd[0]= -1;
00433                      }
00434                      else
00435                      {
00436                             int l=strlen(cinfo->errmsg);
00437 
00438                             readbuf[n]=0;
00439                             if (l < sizeof(cinfo->errmsg)-2)
00440                                    strncat(cinfo->errmsg, readbuf,
00441                                           sizeof(cinfo->errmsg)-2-l);
00442                      }
00443               }
00444 
00445               if (x509_fd[0] >= 0 && FD_ISSET(x509_fd[0], &fdr))
00446               {
00447                      int n=read(x509_fd[0], readbuf, sizeof(readbuf));
00448 
00449                      if (n <= 0)
00450                      {
00451                             close(x509_fd[0]);
00452                             x509_fd[0]= -1;
00453                      }
00454                      else
00455                      {
00456                             if (n + cinfo->x509info_len >=
00457                                 cinfo->x509info_size)
00458                             {
00459                                    size_t news=n+cinfo->x509info_len
00460                                           + 1024;
00461                                    char *newp= cinfo->x509info ?
00462                                           realloc(cinfo->x509info, news)
00463                                           : malloc(news);
00464 
00465                                    if (!newp)
00466                                    {
00467                                           SYSERRMSG;
00468                                           close(x509_fd[0]);
00469                                           x509_fd[0]= -1;
00470                                           continue;
00471                                    }
00472                                    cinfo->x509info=newp;
00473                                    cinfo->x509info_size=news;
00474                             }
00475 
00476                             memcpy(cinfo->x509info + cinfo->x509info_len,
00477                                    readbuf, n);
00478                             cinfo->x509info_len += n;
00479                      }
00480               }
00481 
00482        }
00483        return (cinfo->errmsg[0] ? -1:0);
00484 }
00485 
00486 void couriertls_destroy(struct couriertls_info *info)
00487 {
00488        struct tls_subject *subj;
00489        struct tls_subjitem *subjitem;
00490 
00491        if (info->x509info)
00492               free(info->x509info);
00493 
00494        while ((subj=info->first_subject) != 0)
00495        {
00496               info->first_subject=subj->next;
00497               while ((subjitem=subj->firstitem) != 0)
00498               {
00499                      subj->firstitem=subjitem->nextitem;
00500                      free(subjitem);
00501               }
00502               free(subj);
00503        }
00504 }
00505 
00506 #if 0
00507 int main(int argc, char **argv)
00508 {
00509        struct couriertls_info cinfo;
00510        struct tls_subject *subj;
00511        struct tls_subjitem *subjitem;
00512 
00513        couriertls_init(&cinfo);
00514 
00515        if (couriertls_start(argv+1, &cinfo))
00516        {
00517               printf("ERROR: %s\n",
00518                      cinfo.errmsg[0] ? cinfo.errmsg:"unknown error");
00519               exit(0);
00520        }
00521 
00522        printf("version=%s, cipher=%s, bits=%d\n", cinfo.cipher,
00523               cinfo.version, cinfo.bits);
00524 
00525        for (subj=cinfo.first_subject; subj; subj=subj->next)
00526        {
00527               printf("Subject: ");
00528 
00529               for (subjitem=subj->firstitem; subjitem;
00530                    subjitem=subjitem->nextitem)
00531               {
00532                      printf("/%s=%s", subjitem->name,
00533                             subjitem->value);
00534               }
00535               printf("\n");
00536        }
00537        couriertls_destroy(&cinfo);
00538        sleep(300);
00539        exit(0);
00540 }
00541 #endif