Back to index

courier  0.68.2
imaplogin.c
Go to the documentation of this file.
00001 /*
00002 ** Copyright 1998 - 2011 Double Precision, Inc.
00003 ** See COPYING for distribution information.
00004 */
00005 
00006 #if    HAVE_CONFIG_H
00007 #include      "config.h"
00008 #endif
00009 #include      <stdio.h>
00010 #include      <stdlib.h>
00011 #include      <string.h>
00012 #include      <errno.h>
00013 #include      <ctype.h>
00014 #include      <fcntl.h>
00015 #include      <time.h>
00016 #if    HAVE_UNISTD_H
00017 #include      <unistd.h>
00018 #endif
00019 #if    HAVE_SYS_WAIT_H
00020 #include      <sys/wait.h>
00021 #endif
00022 #include      <sys/socket.h>
00023 #include      <netinet/in.h>
00024 #include      <arpa/inet.h>
00025 #include      <netdb.h>
00026 #if HAVE_SYS_SELECT_H
00027 #include      <sys/select.h>
00028 #endif
00029 #include      <sys/time.h>
00030 
00031 #include      "imaptoken.h"
00032 #include      "imapwrite.h"
00033 #include      "proxy.h"
00034 
00035 #include      <courierauth.h>
00036 #include      <courierauthdebug.h>
00037 #include      "tcpd/spipe.h"
00038 #include      "numlib/numlib.h"
00039 #include      "tcpd/tlsclient.h"
00040 
00041 
00042 FILE *debugfile=0;
00043 extern void initcapability();
00044 extern void mainloop();
00045 extern void imapcapability();
00046 extern int have_starttls();
00047 extern int tlsrequired();
00048 extern int authenticate(const char *, char *, int);
00049 unsigned long header_count=0, body_count=0;      /* Dummies */
00050 
00051 extern unsigned long bytes_received_count; /* counter for received bytes (imaptoken.c) */
00052 extern unsigned long bytes_sent_count; /* counter for sent bytes (imapwrite.c) */
00053 
00054 int main_argc;
00055 char **main_argv;
00056 extern time_t start_time;
00057 
00058 static const char *imapd;
00059 static const char *defaultmaildir;
00060 
00061 void rfc2045_error(const char *p)
00062 {
00063        if (write(2, p, strlen(p)) < 0)
00064               _exit(1);
00065        _exit(0);
00066 }
00067 
00068 extern void cmdfail(const char *, const char *);
00069 extern void cmdsuccess(const char *, const char *);
00070 
00071 static int    starttls(const char *tag)
00072 {
00073        char *argvec[4];
00074 
00075        char localfd_buf[NUMBUFSIZE+40];
00076        char buf2[NUMBUFSIZE];
00077        struct couriertls_info cinfo;
00078        int pipefd[2];
00079 
00080        if (libmail_streampipe(pipefd))
00081        {
00082               cmdfail(tag, "libmail_streampipe() failed.\r\n");
00083               return (-1);
00084        }
00085 
00086        couriertls_init(&cinfo);
00087        fcntl(pipefd[0], F_SETFD, FD_CLOEXEC);
00088 
00089        strcat(strcpy(localfd_buf, "-localfd="),
00090               libmail_str_size_t(pipefd[1], buf2));
00091 
00092        argvec[0]=localfd_buf;
00093        argvec[1]="-tcpd";
00094        argvec[2]="-server";
00095        argvec[3]=NULL;
00096 
00097        cmdsuccess(tag, "Begin SSL/TLS negotiation now.\r\n");
00098        writeflush();
00099 
00100        if (couriertls_start(argvec, &cinfo))
00101        {
00102               close(pipefd[0]);
00103               close(pipefd[1]);
00104               cmdfail(tag, "STARTTLS failed: ");
00105               writes(cinfo.errmsg);
00106               writes("\r\n");
00107               couriertls_destroy(&cinfo);
00108               return (-1);
00109        }
00110 
00111        couriertls_export_subject_environment(&cinfo);
00112        couriertls_destroy(&cinfo);
00113 
00114        close(pipefd[1]);
00115        close(0);
00116        close(1);
00117        if (dup(pipefd[0]) != 0 || dup(pipefd[0]) != 1)
00118        {
00119               perror("dup");
00120               exit(1);
00121        }
00122        close(pipefd[0]);
00123 
00124        /* We use select() with a timeout, so use non-blocking filedescs */
00125 
00126        if (fcntl(0, F_SETFL, O_NONBLOCK) ||
00127            fcntl(1, F_SETFL, O_NONBLOCK))
00128        {
00129               perror("fcntl");
00130               exit(1);
00131        }
00132        return (0);
00133 }
00134 
00135 struct imapproxyinfo {
00136        const char *tag;
00137        const char *uid;
00138        const char *pwd;
00139 };
00140 
00141 static int login_imap(int, const char *, void *);
00142 
00143 int login_callback(struct authinfo *ainfo, void *dummy)
00144 {
00145        int rc;
00146        const char *tag=(const char *)dummy;
00147        char *p;
00148 
00149        p=getenv("IMAP_PROXY");
00150 
00151        if (p && atoi(p))
00152        {
00153               if (ainfo->options == NULL ||
00154                   (p=auth_getoption(ainfo->options,
00155                                   "mailhost")) == NULL)
00156               {
00157                      fprintf(stderr, "WARN: proxy enabled, but no proxy"
00158                              " host defined for %s\n",
00159                              ainfo->address);
00160 
00161                      /* Fallthru to account login */
00162 
00163               }
00164               else if (ainfo->clearpasswd == NULL)
00165               {
00166                      free(p);
00167                      fprintf(stderr, "WARN: proxy enabled, but no password"
00168                              " for %s\n", ainfo->address);
00169                      return -1;
00170               }
00171               else
00172               {
00173                      struct proxyinfo pi;
00174                      struct imapproxyinfo ipi;
00175                      struct servent *se;
00176                      int fd;
00177 
00178                      se=getservbyname("imap", NULL);
00179 
00180                      pi.host=p;
00181                      pi.port=se ? ntohs(se->s_port):143;
00182 
00183                      ipi.uid=ainfo->address;
00184                      ipi.pwd=ainfo->clearpasswd;
00185                      ipi.tag=tag;
00186 
00187                      pi.connected_func=login_imap;
00188                      pi.void_arg=&ipi;
00189 
00190                      if ((fd=connect_proxy(&pi)) < 0)
00191                      {
00192                             free(p);
00193                             return -1;
00194                      }
00195                      free(p);
00196                      if (fd > 0)
00197                      {
00198                             alarm(0);
00199                             proxyloop(fd);
00200                             exit(0);
00201                      }
00202 
00203                      /* FALLTHRU */
00204               }
00205        }
00206 
00207        rc=auth_callback_default(ainfo);
00208 
00209        if (rc == 0)
00210        {
00211               p=malloc(sizeof("OPTIONS=") + strlen(ainfo->options ?
00212                                                ainfo->options:""));
00213 
00214               if (p)
00215               {
00216                      strcat(strcpy(p, "OPTIONS="),
00217                             ainfo->options ? ainfo->options:"");
00218                      putenv(p);
00219 
00220                      p=malloc(sizeof("IMAPLOGINTAG=")+strlen(tag));
00221                      if (p)
00222                      {
00223                             strcat(strcpy(p, "IMAPLOGINTAG="), tag);
00224                             putenv(p);
00225 
00226                             p=malloc(sizeof("AUTHENTICATED=")+
00227                                     strlen(ainfo->address));
00228                             if (p)
00229                             {
00230                                    strcat(strcpy(p, "AUTHENTICATED="),
00231                                           ainfo->address);
00232                                    putenv(p);
00233                                    alarm(0);
00234                                    execl(imapd, imapd,
00235                                          ainfo->maildir ?
00236                                          ainfo->maildir:defaultmaildir,
00237                                          NULL);
00238                                    fprintf(stderr, "ERR: exec(%s) failed!!\n",
00239                                                          imapd);
00240                             }
00241                      }
00242               }
00243        }
00244 
00245        return(rc);
00246 }
00247 
00248 int do_imap_command(const char *tag)
00249 {
00250        struct imaptoken *curtoken=nexttoken();
00251        char authservice[40];
00252 
00253 #if SMAP
00254        if (strcmp(tag, "\\SMAP1") == 0)
00255        {
00256               const char *p=getenv("SMAP_CAPABILITY");
00257 
00258               if (p && *p)
00259                      putenv("PROTOCOL=SMAP1");
00260               else
00261                      return -1;
00262        }
00263 #endif
00264 
00265        courier_authdebug_login( 1, "command=%s", curtoken->tokenbuf );
00266 
00267        if (strcmp(curtoken->tokenbuf, "LOGOUT") == 0)
00268        {
00269               if (nexttoken()->tokentype != IT_EOL)   return (-1);
00270               writes("* BYE Courier-IMAP server shutting down\r\n");
00271               cmdsuccess(tag, "LOGOUT completed\r\n");
00272               writeflush();
00273               fprintf(stderr, "INFO: LOGOUT, ip=[%s], rcvd=%lu, sent=%lu\n",
00274                      getenv("TCPREMOTEIP"), bytes_received_count, bytes_sent_count);
00275               exit(0);
00276        }
00277        if (strcmp(curtoken->tokenbuf, "NOOP") == 0)
00278        {
00279               if (nexttoken()->tokentype != IT_EOL)     return (-1);
00280               cmdsuccess(tag, "NOOP completed\r\n");
00281               return (0);
00282        }
00283        if (strcmp(curtoken->tokenbuf, "CAPABILITY") == 0)
00284        {
00285               if (nexttoken()->tokentype != IT_EOL)     return (-1);
00286 
00287               writes("* CAPABILITY ");
00288               imapcapability();
00289               writes("\r\n");
00290               cmdsuccess(tag, "CAPABILITY completed\r\n");
00291               return (0);
00292        }
00293 
00294        if (strcmp(curtoken->tokenbuf, "STARTTLS") == 0)
00295        {
00296               if (!have_starttls())       return (-1);
00297               if (starttls(tag))          return (-2);
00298               putenv("IMAP_STARTTLS=NO");
00299               putenv("IMAP_TLS_REQUIRED=0");
00300               putenv("IMAP_TLS=1");
00301 
00302               return (0);
00303        }
00304 
00305        if (strcmp(curtoken->tokenbuf, "LOGIN") == 0)
00306        {
00307        struct imaptoken *tok=nexttoken_nouc();
00308        char   *userid;
00309        char   *passwd;
00310        const char *p;
00311        int    rc;
00312 
00313               if (have_starttls() && tlsrequired())     /* Not yet */
00314               {
00315                      cmdfail(tag, "STARTTLS required\r\n");
00316                      return (0);
00317               }
00318 
00319               switch (tok->tokentype)     {
00320               case IT_ATOM:
00321               case IT_NUMBER:
00322               case IT_QUOTED_STRING:
00323                      break;
00324               default:
00325                      return (-1);
00326               }
00327 
00328               userid=strdup(tok->tokenbuf);
00329               if (!userid)
00330                      write_error_exit(0);
00331               tok=nexttoken_nouc_okbracket();
00332               switch (tok->tokentype)     {
00333               case IT_ATOM:
00334               case IT_NUMBER:
00335               case IT_QUOTED_STRING:
00336                      break;
00337               default:
00338                      free(userid);
00339                      return (-1);
00340               }
00341 
00342               passwd=my_strdup(tok->tokenbuf);
00343 
00344               if (nexttoken()->tokentype != IT_EOL)
00345               {
00346                      free(userid);
00347                      free(passwd);
00348                      return (-1);
00349               }
00350 
00351               strcat(strcpy(authservice, "AUTHSERVICE"),
00352                      getenv("TCPLOCALPORT"));
00353 
00354               p=getenv(authservice);
00355 
00356               if (!p || !*p)
00357                      p="imap";
00358 
00359               rc=auth_login(p, userid, passwd, login_callback, (void *)tag);
00360               courier_safe_printf("INFO: LOGIN FAILED, user=%s, ip=[%s]",
00361                               userid, getenv("TCPREMOTEIP"));
00362               free(userid);
00363               free(passwd);
00364               if (rc > 0)
00365               {
00366                      perror("ERR: authentication error");
00367                      writes("* BYE Temporary problem, please try again later\r\n");
00368                      writeflush();
00369                      exit(1);
00370               }
00371               sleep(5);
00372               cmdfail(tag, "Login failed.\r\n");
00373               return (0);
00374        }
00375 
00376        if (strcmp(curtoken->tokenbuf, "AUTHENTICATE") == 0)
00377        {
00378        char   method[32];
00379        int    rc;
00380 
00381               if (have_starttls() && tlsrequired())     /* Not yet */
00382               {
00383                      cmdfail(tag, "STARTTLS required\r\n");
00384                      return (0);
00385               }
00386               rc=authenticate(tag, method, sizeof(method));
00387               courier_safe_printf("INFO: LOGIN FAILED, method=%s, ip=[%s]",
00388                               method, getenv("TCPREMOTEIP"));
00389               if (rc > 0)
00390               {
00391                      perror("ERR: authentication error");
00392                      writes("* BYE Temporary problem, please try again later\r\n");
00393                      writeflush();
00394                      exit(1);
00395               }
00396               sleep(5);
00397               cmdfail(tag, "Login failed.\r\n");
00398               writeflush();
00399               return (-2);
00400        }
00401 
00402        return (-1);
00403 }
00404 
00405 extern void ignorepunct();
00406 
00407 int main(int argc, char **argv)
00408 {
00409        const char    *ip;
00410 
00411 #ifdef HAVE_SETVBUF_IOLBF
00412        setvbuf(stderr, NULL, _IOLBF, BUFSIZ);
00413 #endif
00414 
00415        if (argc != 3)
00416        {
00417               printf("* BYE imaplogin expected exactly two arguments.\r\n");
00418               fflush(stdout);
00419               exit(1);
00420        }
00421 
00422        alarm(60);
00423        imapd=argv[1];
00424        defaultmaildir=argv[2];
00425        initcapability();
00426 
00427        ip=getenv("TCPREMOTEIP");
00428        if (!ip)
00429               putenv("TCPREMOTEIP=127.0.0.1");
00430        ip=getenv("TCPREMOTEIP");
00431 
00432        if (!getenv("TCPLOCALPORT"))
00433               putenv("TCPLOCALPORT=143");
00434 
00435        time(&start_time);
00436 
00437 #if    IMAP_CLIENT_BUGS
00438 
00439        ignorepunct();
00440 
00441 #endif
00442 
00443        courier_authdebug_login_init();
00444 
00445        /* We use select() with a timeout, so use non-blocking filedescs */
00446 
00447        if (fcntl(0, F_SETFL, O_NONBLOCK) ||
00448            fcntl(1, F_SETFL, O_NONBLOCK))
00449        {
00450               perror("fcntl");
00451               exit(1);
00452        }
00453 
00454        writes("* OK [CAPABILITY ");
00455        imapcapability();
00456        writes("] Courier-IMAP ready. "
00457               "Copyright 1998-2011 Double Precision, Inc.  "
00458               "See COPYING for distribution information.\r\n");
00459        fprintf(stderr, "DEBUG: Connection, ip=[%s]\n", ip);
00460        writeflush();
00461        main_argc=argc;
00462        main_argv=argv;
00463 
00464        putenv("PROTOCOL=IMAP");
00465 
00466        mainloop();
00467        return (0);
00468 }
00469 
00470 void bye()
00471 {
00472        exit(0);
00473 }
00474 
00475 static void imap_write_str(const char *c,
00476                         void (*cb_func)(const char *,
00477                                       size_t,
00478                                       void *),
00479                         void *cb_arg)
00480 {
00481        if (c == NULL)
00482        {
00483               (*cb_func)("NIL", 3, cb_arg);
00484        }
00485 
00486        (*cb_func)("\"", 1, cb_arg);
00487 
00488        while (*c)
00489        {
00490               size_t n;
00491 
00492               for (n=0; c[n]; n++)
00493                      if (c[n] == '"' || c[n] == '\\')
00494                             break;
00495 
00496               if (n)
00497               {
00498                      (*cb_func)(c, n, cb_arg);
00499 
00500                      c += n;
00501               }
00502 
00503               if (*c)
00504               {
00505                      (*cb_func)("\\", 1, cb_arg);
00506                      (*cb_func)(c, 1, cb_arg);
00507                      ++c;
00508               }
00509        }
00510        (*cb_func)("\"", 1, cb_arg);
00511 }
00512 
00513 static void imap_login_cmd(struct imapproxyinfo *ipi,
00514                         void (*cb_func)(const char *,
00515                                       size_t,
00516                                       void *),
00517                         void *cb_arg)
00518 {
00519        (*cb_func)(ipi->tag, strlen(ipi->tag), cb_arg);
00520        (*cb_func)(" LOGIN ", 7, cb_arg);
00521        imap_write_str(ipi->uid, cb_func, cb_arg);
00522        (*cb_func)(" ", 1, cb_arg);
00523        imap_write_str(ipi->pwd, cb_func, cb_arg);
00524        (*cb_func)("\r\n", 2, cb_arg);
00525 }
00526 
00527 static void imap_capability_cmd(struct imapproxyinfo *ipi,
00528                             void (*cb_func)(const char *,
00529                                           size_t,
00530                                           void *),
00531                             void *cb_arg)
00532 {
00533        (*cb_func)(ipi->tag, strlen(ipi->tag), cb_arg);
00534        (*cb_func)(" CAPABILITY\r\n", 13, cb_arg);
00535 }
00536 
00537 static void cb_cnt(const char *c, size_t l,
00538                  void *arg)
00539 {
00540        *(size_t *)arg += l;
00541 }
00542 
00543 static void cb_cpy(const char *c, size_t l,
00544                  void *arg)
00545 {
00546        char **p=(char **)arg;
00547 
00548        memcpy(*p, c, l);
00549        *p += l;
00550 }
00551 
00552 static char *get_imap_cmd(struct imapproxyinfo *ipi,
00553                        void (*cmd)(struct imapproxyinfo *ipi,
00554                                   void (*cb_func)(const char *,
00555                                                 size_t,
00556                                                 void *),
00557                                   void *cb_arg))
00558 
00559 {
00560        size_t cnt=1;
00561        char *buf;
00562        char *p;
00563 
00564        (*cmd)(ipi, &cb_cnt, &cnt);
00565 
00566        buf=malloc(cnt);
00567 
00568        if (!buf)
00569        {
00570               fprintf(stderr, "CRIT: Out of memory!\n");
00571               return NULL;
00572        }
00573 
00574        p=buf;
00575        (*cmd)(ipi, &cb_cpy, &p);
00576        *p=0;
00577        return buf;
00578 }
00579 
00580 static int login_imap(int fd, const char *hostname, void *void_arg)
00581 {
00582        struct imapproxyinfo *ipi=(struct imapproxyinfo *)void_arg;
00583        struct proxybuf pb;
00584        char linebuf[256];
00585        const char *p;
00586        char *cmd;
00587 
00588        DPRINTF("Proxy connected to %s", hostname);
00589 
00590        memset(&pb, 0, sizeof(pb));
00591 
00592        if (proxy_readline(fd, &pb, linebuf, sizeof(linebuf), 1) < 0)
00593               return -1;
00594 
00595        DPRINTF("%s: %s", hostname, linebuf);
00596 
00597        if ((p=strtok(linebuf, " \t")) == NULL ||
00598            strcmp(p, "*") ||
00599            (p=strtok(NULL, " \t")) == NULL ||
00600            strcasecmp(p, "OK"))
00601        {
00602               fprintf(stderr, "WARN: Did not receive greeting from %s\n",
00603                      hostname);
00604               return -1;
00605        }
00606 
00607        cmd=get_imap_cmd(ipi, imap_login_cmd);
00608 
00609        if (!cmd)
00610               return -1;
00611 
00612        if (proxy_write(fd, hostname, cmd, strlen(cmd)))
00613        {
00614               free(cmd);
00615               return -1;
00616        }
00617        free(cmd);
00618 
00619 #if SMAP
00620        if (strcmp(ipi->tag, "\\SMAP1") == 0)
00621        {
00622               do
00623               {
00624                      if (proxy_readline(fd, &pb, linebuf, sizeof(linebuf),
00625                                       0) < 0)
00626                             return -1;
00627 
00628                      DPRINTF("%s: %s", hostname, linebuf);
00629 
00630               } while (linebuf[0] != '+' && linebuf[0] != '-');
00631 
00632 
00633               if (linebuf[0] != '+')
00634               {
00635                      fprintf(stderr, "WARN: Login to %s failed\n", hostname);
00636                      return -1;
00637               }
00638 
00639               if (fcntl(1, F_SETFL, 0) < 0 ||
00640                   (printf("+OK connected to proxy server.\r\n"),
00641                    fflush(stdout)) < 0)
00642                      return -1;
00643               return (0);
00644        }
00645 #endif
00646 
00647 
00648        for (;;)
00649        {
00650               if (proxy_readline(fd, &pb, linebuf, sizeof(linebuf), 1) < 0)
00651                      return -1;
00652 
00653               DPRINTF("%s: %s", hostname, linebuf);
00654 
00655               if ((p=strtok(linebuf, " \t")) == NULL ||
00656                   strcmp(p, ipi->tag) ||
00657                   (p=strtok(NULL, " \t")) == NULL)
00658                      continue;
00659 
00660               if (strcasecmp(p, "OK"))
00661               {
00662                      fprintf(stderr, "WARN: Login to %s failed\n", hostname);
00663                      return -1;
00664               }
00665               break;
00666        }
00667 
00668        p=getenv("IMAP_PROXY_FOREIGN");
00669 
00670        if (p && atoi(p))
00671        {
00672               cmd=get_imap_cmd(ipi, imap_capability_cmd);
00673 
00674               if (!cmd)
00675                      return -1;
00676 
00677               if (proxy_write(fd, hostname, cmd, strlen(cmd)))
00678               {
00679                      free(cmd);
00680                      return -1;
00681               }
00682               free(cmd);
00683        }
00684        else
00685        {
00686               if (fcntl(1, F_SETFL, 0) < 0 ||
00687                   (printf("%s OK connected to proxy server.\r\n",
00688                          ipi->tag), fflush(stdout)) < 0)
00689                      return -1;
00690        }
00691 
00692        return 0;
00693 }
00694