Logo Search packages:      
Sourcecode: cyrus-sasl2 version File versions  Download package

smtptest.c

/* smtpauth.c -- authenticate to SMTP server, then give normal protocol
 *
 * uses sfio
 *
 */

#include <config.h>

#include <sfio.h>
#include <sfio/stdio.h>
#include <ctype.h>
#include <pwd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>

#include <sys/socket.h>
#include <sys/file.h>
#include <netinet/in.h>
#include <netdb.h>

#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif

#include <sasl.h>
#include <saslutil.h>

#include "sfsasl.h"

/* from OS: */
extern char *getpass();
extern struct hostent *gethostbyname();

static char *authname = NULL;
static char *username = NULL;
static char *realm = NULL;

extern char *optarg;
extern int optind;

int verbose = 0;
int emacs = 0;

int iptostring(const struct sockaddr *addr, socklen_t addrlen,
                 char *out, unsigned outlen) {
    char hbuf[NI_MAXHOST], pbuf[NI_MAXSERV];
    int niflags;

    if(!addr || !out) return SASL_BADPARAM;

    niflags = (NI_NUMERICHOST | NI_NUMERICSERV);
#ifdef NI_WITHSCOPEID
    if (addr->sa_family == AF_INET6)
      niflags |= NI_WITHSCOPEID;
#endif
    if (getnameinfo(addr, addrlen, hbuf, sizeof(hbuf), pbuf, sizeof(pbuf),
                niflags) != 0)
      return SASL_BADPARAM;

    if(outlen < strlen(hbuf) + strlen(pbuf) + 2)
      return SASL_BUFOVER;

    snprintf(out, outlen, "%s;%s", hbuf, pbuf);

    return SASL_OK;
}

void usage(char *p)
{
    fprintf(stderr, "%s [-v] [-l] [-u username] [-a authname] [-s ssf] [-m mech] host[:port]\n", p);
    fprintf(stderr, " -v\tVerbose Output\n");
    fprintf(stderr, " -l\tLMTP semantics\n");
    exit(EX_USAGE);
}

#define ISGOOD(r) (((r) / 100) == 2)
#define TEMPFAIL(r) (((r) / 100) == 4)
#define PERMFAIL(r) (((r) / 100) == 5)
#define ISCONT(s) (s && (s[3] == '-'))

static int ask_code(const char *s)
{
    int ret = 0;
    
    if (s==NULL) return -1;

    if (strlen(s) < 3) return -1;

    /* check to make sure 0-2 are digits */
    if ((isdigit((int) s[0])==0) ||
      (isdigit((int) s[1])==0) ||
      (isdigit((int) s[2])==0))
    {
      return -1;
    }

    ret = ((s[0]-'0')*100)+((s[1]-'0')*10)+(s[2]-'0');
    
    return ret;
}

static void chop(char *s)
{
    char *p;

    assert(s);
    p = s + strlen(s) - 1;
    if (p[0] == '\n') {
      *p-- = '\0';
    }
    if (p >= s && p[0] == '\r') {
      *p-- = '\0';
    }
}

void interaction (int id, const char *prompt,
              char **tresult, unsigned int *tlen)
{
    char result[1024];
    
    if (id==SASL_CB_PASS) {
      fprintf(stderr, "%s: ", prompt);
      *tresult = strdup(getpass("")); /* leaks! */
      *tlen=   strlen(*tresult);
      return;
    } else if (id==SASL_CB_USER) {
      if (username != NULL) {
          strcpy(result, username);
      } else {
          strcpy(result, getpwuid(getuid())->pw_name);
      }
    } else if (id==SASL_CB_AUTHNAME) {
      if (authname != NULL) {
          strcpy(result, authname);
      } else {
          strcpy(result, getpwuid(getuid())->pw_name);
      }
    } else if ((id==SASL_CB_GETREALM) && (realm != NULL)) {
      strcpy(result, realm);
    } else {
      int c;
      
      fprintf(stderr, "%s: ",prompt);
      fgets(result, sizeof(result) - 1, stdin);
      c = strlen(result);
      result[c - 1] = '\0';
    }

    *tlen = strlen(result);
    *tresult = (char *) malloc(*tlen+1); /* leaks! */
    memset(*tresult, 0, *tlen+1);
    memcpy((char *) *tresult, result, *tlen);
}

void fillin_interactions(sasl_interact_t *tlist)
{
    while (tlist->id != SASL_CB_LIST_END)
    {
      interaction(tlist->id, tlist->prompt,
                (void *) &(tlist->result), 
                &(tlist->len));
      tlist++;
    }
}

static sasl_callback_t callbacks[] = {
    { SASL_CB_GETREALM, NULL, NULL }, 
    { SASL_CB_USER, NULL, NULL }, 
    { SASL_CB_AUTHNAME, NULL, NULL }, 
    { SASL_CB_PASS, NULL, NULL }, 
    { SASL_CB_LIST_END, NULL, NULL }
};

static sasl_security_properties_t *make_secprops(int min,int max)
{
    sasl_security_properties_t *ret=(sasl_security_properties_t *)
      malloc(sizeof(sasl_security_properties_t));

    ret->maxbufsize = 8192;
    ret->min_ssf = min;
    ret->max_ssf = max;

    ret->security_flags = 0;
    ret->property_names = NULL;
    ret->property_values = NULL;

    return ret;
}

Sfio_t *debug;

int main(int argc, char **argv)
{
    char *mechlist = NULL;
    const char *mechusing = NULL;
    int minssf = 0, maxssf = 128;
    char *p;
    Sfio_t *server_in, *server_out;
    sasl_conn_t *conn = NULL;
    sasl_interact_t *client_interact = NULL;
    char in[4096];
    const char *out;
    unsigned int inlen, outlen;
    char out64[4096];
    int c;

    char *host;
    struct servent *service;
    int port;
    struct hostent *hp;
    struct sockaddr_in addr;
    char remote_ip[64], local_ip[64];
    int sock;

    char buf[1024];
    int sz;
    char greeting[1024];
    int code;
    int do_lmtp=0;
    int r = 0;

    debug = stderr;

    while ((c = getopt(argc, argv, "vElm:s:u:a:d:")) != EOF) {
      switch (c) {
      case 'm':
          mechlist = optarg;
          break;

      case 'l':
          do_lmtp = 1;
          break;

      case 's':
          maxssf = atoi(optarg);
          break;
          
      case 'u':
          username = optarg;
          break;

      case 'a':
          authname = optarg;
          break;

      case 'v':
          verbose++;
          break;
          
      case 'E':
          emacs++;
          break;

      case 'd':
          sprintf(buf, "%s-%d", optarg, getpid());
          debug = sfopen(NULL, buf, "w");
          sfsetbuf(debug, NULL, 0);
          break;

      case '?':
      default:
          usage(argv[0]);
          break;
      }
    }

    if (optind != argc - 1) {
      usage(argv[0]);
    }

    host = argv[optind];
    p = strchr(host, ':');
    if (p) {
      *p++ = '\0';
    } else {
      if(do_lmtp) {
          p = "lmtp";
      } else {
          p = "smtp";
      }
    }
    service = getservbyname(p, "tcp");
    if (service) {
      port = service->s_port;
    } else {
      port = atoi(p);
      if (!port) usage(argv[0]);
      port = htons(port);
    }

    if ((hp = gethostbyname(host)) == NULL) {
      perror("gethostbyname");
      exit(EX_NOHOST);
    }

    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
      perror("socket");
      exit(EX_OSERR);
    }

    addr.sin_family = AF_INET;
    memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
    addr.sin_port = port;

    if (connect(sock, (struct sockaddr *) &addr, sizeof (addr)) < 0) {
      perror("connect");
      exit(EX_NOHOST);
    }

    server_in = sfnew(NULL, NULL, SF_UNBOUND, sock, SF_READ);
    server_out = sfnew(NULL, NULL, SF_UNBOUND, sock, SF_WRITE);

    /* read greeting */
    greeting[0] = '\0';
    for (;;) {
      sfsync(server_out);
      if (fgets(buf, sizeof(buf)-1, server_in)) {
          if (greeting[0] == '\0') {
            strncpy(greeting, buf, sizeof(greeting) - 1);
          }

          if (verbose) fprintf(debug, "%s", buf);
          code = ask_code(buf);
          if (ISCONT(buf) && ISGOOD(code)) continue;
      } else {
          code = 400;
      }
      break;
    }

    if (!ISGOOD(code)) goto done;

    /* EHLO */
    gethostname(buf, sizeof(buf)-1);
    if(do_lmtp) {
      if(verbose) fprintf(debug, "LHLO %s\r\n", buf);
      fprintf(server_out, "LHLO %s\r\n", buf);
    } else {
      if (verbose) fprintf(debug, "EHLO %s\r\n", buf);
      fprintf(server_out, "EHLO %s\r\n", buf);
    }
    
    /* read responses */
    for (;;) {
      sfsync(server_out);
      if (!fgets(buf, sizeof(buf)-1, server_in)) {
          code = 400;
          goto done;
      }
      if (verbose) fprintf(debug, "%s", buf);
      code = ask_code(buf);
      if (code == 250) {
          /* we're only looking for AUTH */
          if (!strncasecmp(buf + 4, "AUTH ", 5)) {
            chop(buf);
            if (!mechlist) mechlist = strdup(buf + 9);
          }
      }
      if (ISCONT(buf) && ISGOOD(code)) {
          continue;
      } else {
          break;
      }
    }
    if (!ISGOOD(code)) goto done;

    /* attempt authentication */
    if (!mechlist) {
      if (verbose > 2) fprintf(debug, "no authentication\n");
      goto doneauth;
    }

    if (!r) r = sasl_client_init(callbacks);
    if (!r) {
      struct sockaddr_in saddr_r;
      int addrsize = sizeof(struct sockaddr_in);

      if (getpeername(sock, (struct sockaddr *) &saddr_r, &addrsize) < 0) {
          perror("getpeername");
          exit(EX_NOHOST);
      }
      r = iptostring((struct sockaddr *)&saddr_r,
                   sizeof(struct sockaddr_in), remote_ip, 64);
    }
    if (!r) {
      struct sockaddr_in saddr_l;
      int addrsize = sizeof(struct sockaddr_in);

      if (getsockname(sock, (struct sockaddr *) &saddr_l, &addrsize) < 0) {
          perror("getsockname");
          exit(EX_OSERR);
      }
      r = iptostring((struct sockaddr *)&saddr_l,
                   sizeof(struct sockaddr_in), local_ip, 64);
    }

    if (!r) {
      if(do_lmtp) {
          r = sasl_client_new("lmtp", host, local_ip, remote_ip,
                        NULL, 0, &conn);
      } else {
          r = sasl_client_new("smtp", host, local_ip, remote_ip,
                        NULL, 0, &conn);
      }
    }
    
    if (!r) {
      sasl_security_properties_t *secprops = make_secprops(minssf, maxssf);
      r = sasl_setprop(conn, SASL_SEC_PROPS, secprops);
      free(secprops);
    }
    
    if (!r) {
      do {
          r = sasl_client_start(conn, mechlist,
                          &client_interact, &out, &outlen, &mechusing);
          if (r == SASL_INTERACT) {
            fillin_interactions(client_interact);
          }
      } while (r == SASL_INTERACT);

      if (r == SASL_OK || r == SASL_CONTINUE) {
          if (outlen > 0) {
            r = sasl_encode64(out, outlen, out64, sizeof out64, NULL);
            if (!r) {
                if (verbose) 
                  fprintf(debug, "AUTH %s %s\r\n", mechusing, out64);
                fprintf(server_out, "AUTH %s %s\r\n", mechusing, out64);
            }
          } else {
            if (verbose) fprintf(debug, "AUTH %s\r\n", mechusing);
            fprintf(server_out, "AUTH %s\r\n", mechusing);
          }
      } else {
          fprintf(debug, "\nclient start failed: %s\n", sasl_errdetail(conn));
      }
      
    }

    /* jump to doneauth if we succeed */
    while (r == SASL_OK || r == SASL_CONTINUE) {
      sfsync(server_out);
      if (!fgets(buf, sizeof(buf)-1, server_in)) {
          code = 400;
          goto done;
      }
      if (verbose) fprintf(debug, "%s", buf);
      code = ask_code(buf);
      if (ISCONT(buf)) continue;
      if (ISGOOD(code)) {
          if (code != 235) {
            /* weird! */
          }
          /* yay, we won! */
          sfdcsasl(server_in, conn);
          sfdcsasl(server_out, conn);
          goto doneauth;
      } else if (code != 334) {
          /* unexpected response */
          break;
      }
      r = sasl_decode64(buf + 4, strlen(buf) - 6, in, 4096, &inlen);
      if (r != SASL_OK) break;
      
      do {
          r = sasl_client_step(conn, in, inlen, &client_interact,
                         &out, &outlen);
          if (r == SASL_INTERACT) {
            fillin_interactions(client_interact);
          }
      } while (r == SASL_INTERACT);

      if (r == SASL_OK || r == SASL_CONTINUE) {
          r = sasl_encode64(out, outlen, out64, sizeof out64, NULL);
      }
      if (r == SASL_OK) {
          if (verbose) fprintf(debug, "%s\r\n", out64);
          fprintf(server_out, "%s\r\n", out64);
      }
    }

    /* auth failed! */
    if (!r) {
      fprintf(debug, "%d authentication failed\n", code);
    } else {
      fprintf(debug, "400 authentication failed: %s\n", 
            sasl_errstring(r, NULL, NULL));
    }
    exit(EX_SOFTWARE);

 doneauth:
    /* ready for application */
    greeting[3] = '-';
    printf("%s", greeting);
    printf("220 %s %s\r\n", host, conn ? "authenticated" : "no auth");

    fcntl(0, F_SETFL, O_NONBLOCK);
    fcntl(sock, F_SETFL, O_NONBLOCK);
    sfset(stdin, SF_SHARE, 0);

    /* feed data back 'n forth */
    for (;;) {
      Sfio_t *flist[3];

    top:
      flist[0] = stdin;
      flist[1] = server_in;

      /* sfpoll */
      if (verbose > 5) fprintf(debug, "poll\n");
      r = sfpoll(flist, 2, -1);
      if (verbose > 5) fprintf(debug, "poll 2\n");

      while (r--) {
          if (flist[r] == server_in) {
            do {
                if (verbose > 5) fprintf(debug, "server!\n");
                errno = 0;
                sz = sfread(server_in, buf, sizeof(buf)-1);
                if (sz == 0 && (errno == EAGAIN)) goto top;
                if (sz <= 0) goto out;
                buf[sz] = '\0';
                if (verbose > 5) fprintf(debug, "server 2 '%s'!\n", buf);
                sfwrite(stdout, buf, sz);
            } while (sfpoll(&server_in, 1, 0));
            sfsync(stdout);
          } else if (flist[r] == stdin) {
            Sfio_t *p[1];

            p[0] = stdin;
            do {
                if (verbose > 5) fprintf(debug, "stdin!\n");
                errno = 0;
                sz = sfread(stdin, buf, sizeof(buf)-1);
                if (sz == 0 && (errno == EAGAIN)) goto top;
                if (sz <= 0) goto out;
                buf[sz] = '\0';
                if (verbose > 5) fprintf(debug, "stdin 2 '%s'!\n", buf);
                if (emacs) {
                  int i;

                  /* fix emacs stupidness */
                  for (i = 0; i < sz - 1; i++) {
                      if (buf[i] == '\n' && buf[i+1] == '\n')
                        buf[i++] = '\r';
                  }
                  if (buf[sz-2] != '\r' && buf[sz-1] == '\n') {
                      sfungetc(stdin, buf[sz--]);
                      buf[sz] = '\0';
                  }

                  if (verbose > 5) fprintf(debug, "emacs '%s'!\n", buf);
                }
                sfwrite(server_out, buf, sz);
                if (verbose > 7) fprintf(debug, "stdin 3!\n");
            } while (sfpoll(p, 1, 0));
            sfsync(server_out);
          } else {
            abort();
          }
      }
    }
 out:
    if (verbose > 3) fprintf(debug, "exiting! %d %s\n", sz, strerror(errno));
    exit(EX_OK);

 done:
    if (ISGOOD(code)) {
      if (verbose > 1) fprintf(debug, "ok\n");
      exit(EX_OK);
    }
    if (TEMPFAIL(code)) {
      if (verbose > 1) fprintf(debug, "tempfail\n");
      exit(EX_TEMPFAIL);
    }
    if (PERMFAIL(code)) {
      if (verbose > 1) fprintf(debug, "permfail\n");
      exit(EX_UNAVAILABLE);
    }
    
    if (verbose > 1) fprintf(debug, "unknown failure\n");
    exit(EX_TEMPFAIL);
}

Generated by  Doxygen 1.6.0   Back to index