static int mavis_send_in(mavis_ctx * mcx, av_ctx ** ac) { char *t, *u; DebugIn(DEBUG_MAVIS); t = av_get(*ac, AV_A_TYPE); u = av_get(*ac, AV_A_USER); if (strcmp(t, AV_V_TYPE_FTP) || (strcasecmp(u, "ftp") && strcasecmp(u, "anonymous"))) { Debug((DEBUG_MAVIS, "- %s = MAVIS_DOWN (no anon ftp)\n", __func__)); return MAVIS_DOWN; } av_set(*ac, AV_A_RESULT, AV_V_RESULT_OK); av_setf(*ac, AV_A_UID, "%d", (int) mcx->uid); av_setf(*ac, AV_A_GID, "%d", (int) mcx->gid); av_set(*ac, AV_A_HOME, mcx->home); av_set(*ac, AV_A_ROOT, mcx->root); av_set(*ac, AV_A_FTP_ANONYMOUS, AV_V_BOOL_TRUE); if (mcx->incoming) av_set(*ac, AV_A_ANON_INCOMING, mcx->incoming); if ((t = av_get(*ac, AV_A_PASSWORD))) av_set(*ac, AV_A_EMAIL, t); Debug((DEBUG_MAVIS, "- %s = MAVIS_FINAL\n", __func__)); return MAVIS_FINAL; }
static void mavis_io(struct context *ctx) { av_ctx *avc = NULL; struct query *q; rb_node_t *r; char *serial; Debug((DEBUG_PROC, "mavis_io %p\n", ctx)); switch (mavis_recv(mcx, &avc, ctx)) { case MAVIS_FINAL: break; case MAVIS_TIMEOUT: counter_expired++, counter_p_expired++; default: return; } if (!(serial = av_get(avc, AV_A_SERIAL))) return; q = alloca(sizeof(struct query)); q->serial = serial; q->serial_crc = crc32_update(INITCRC32, (u_char *) serial, strlen(serial)); if (!(r = RB_search(deferred_by_serial, q))) return; q = RB_payload(r, struct query *); /* XXX -- move the unset functionality to a separate module? */ if (!transmit_password) { av_unset(avc, AV_A_PASSWORD); av_unset(avc, AV_A_DBPASSWORD); } ctx = io_get_ctx(io, q->fd); /* Send answer to client */ av_send(avc, q->fd, &q->sa, ctx->blowfish); /* Remove query from deferred queue */ RB_delete(deferred_by_serial, r); backlog--; setproctitle("%s: backlog: %d", common_data.progname, backlog); counter_answered++, counter_p_answered++; return; } /* if */
static void find_ssluser(av_ctx * ac, int fn, char *user) { char inbuf[8192]; size_t offset = 0; ssize_t inlength; char *linestart = inbuf; char *lineend; while ((inlength = Read(fn, inbuf + offset, sizeof(inbuf) - 1 - offset)) > 0) { inlength += offset; inbuf[inlength] = 0; linestart = inbuf; while ((lineend = strchr(linestart, '\n'))) { *lineend = 0; if (*linestart != '#') { char *subj = strchr(linestart, ':'); if (subj) { char *s = linestart; char *t; *subj++ = 0; for (t = strtok(s, ","); t; t = strtok(NULL, ",")) if (!strcmp(user, t)) { char *a = av_get(ac, AV_A_DBCERTSUBJ); if (a) av_setf(ac, AV_A_DBCERTSUBJ, "%s\r%s", a, subj); else av_set(ac, AV_A_DBCERTSUBJ, subj); break /* out of for-loop */ ; } } } linestart = lineend + 1; } offset = inbuf + inlength - linestart; if (offset) memmove(inbuf, linestart, offset); } }
static int mavis_send_in(mavis_ctx * mcx, av_ctx ** ac) { struct passwd *pw; char *t, *u, *p, *m; char buf[1024]; t = av_get(*ac, AV_A_TYPE); u = av_get(*ac, AV_A_USER); p = av_get(*ac, AV_A_PASSWORD); if (strcmp(t, AV_V_TYPE_FTP)) return MAVIS_DOWN; /* no VHOST support yet */ m = av_get(*ac, AV_A_FTP_ANONYMOUS); if (m && !strcmp(m, AV_V_BOOL_TRUE)) return MAVIS_DOWN; if (mcx->honour_ftpusers && !valid_user(mcx, u)) { av_set(*ac, AV_A_COMMENT, "user found in ftpusers file"); av_set(*ac, AV_A_RESULT, AV_V_RESULT_FAIL); return MAVIS_FINAL; } #ifdef HAVE_SHADOWPWD if (!mcx->passwordfile) { struct spwd *spw; uid_t uid; uid = geteuid(); seteuid(0); spw = getspnam(u); seteuid(uid); if (!spw) return MAVIS_DOWN; if (strcmp(spw->sp_pwdp, mcx->crypt(p, spw->sp_pwdp))) { av_set(*ac, AV_A_COMMENT, "password mismatch"); av_unset(*ac, AV_A_DBPASSWORD); } else av_set(*ac, AV_A_DBPASSWORD, p); pw = getpwnam(u); if (!pw) { av_set(*ac, AV_A_COMMENT, "user not found in password file"); av_set(*ac, AV_A_RESULT, AV_V_RESULT_FAIL); return MAVIS_FINAL; } } else #endif /* HAVE_SHADOWPWD */ { int f; f = open(mcx->passwordfile, O_RDONLY); if (f < 0) { av_set(*ac, AV_A_COMMENT, "error opening password file"); av_set(*ac, AV_A_RESULT, AV_V_RESULT_ERROR); return MAVIS_DOWN; } pw = get_pwent(mcx, f, u); close(f); if (!pw) return MAVIS_DOWN; #undef crypt /* may be set by openssl include stuff */ if (strcmp(pw->pw_passwd, mcx->crypt(p, pw->pw_passwd))) { av_set(*ac, AV_A_COMMENT, "password mismatch"); av_unset(*ac, AV_A_DBPASSWORD); } else av_set(*ac, AV_A_DBPASSWORD, p); } if (mcx->require_valid_shell && (!pw->pw_shell || !valid_shell(mcx, pw->pw_shell))) { av_set(*ac, AV_A_COMMENT, "invalid shell"); av_set(*ac, AV_A_RESULT, AV_V_RESULT_FAIL); return MAVIS_FINAL; } if (!pw->pw_dir) { av_set(*ac, AV_A_COMMENT, "home dir not set"); av_set(*ac, AV_A_RESULT, AV_V_RESULT_FAIL); return MAVIS_FINAL; } av_setf(*ac, AV_A_UID, "%lu", (u_long) pw->pw_uid); av_setf(*ac, AV_A_GID, "%lu", (u_long) pw->pw_gid); /* attempt to get supplemental groups */ av_set(*ac, AV_A_GIDS, groups_getlist(pw->pw_name, pw->pw_gid, buf, sizeof(buf))); if (mcx->ftp_chroot) { char *tp = strstr(pw->pw_dir, "/./"); if (tp) { *tp = 0; av_set(*ac, AV_A_HOME, tp + 2); } else av_set(*ac, AV_A_HOME, "/"); av_set(*ac, AV_A_ROOT, pw->pw_dir); } else { av_set(*ac, AV_A_HOME, pw->pw_dir); av_set(*ac, AV_A_ROOT, "/"); } if (mcx->lookup_sslusers) lookup_ssluser(mcx, *ac, u); return MAVIS_FINAL; }
void client_io(struct context *ctx, int cur) { /* We have incoming data. */ char *serial; int res; ssize_t buflen; sockaddr_union sa; socklen_t sinlen = (socklen_t) sizeof(sockaddr_union); char *avt; char buf[BUFSIZE_MAVIS]; av_ctx *avc; static struct query *q = NULL; if (!q) q = Xcalloc(1, sizeof(struct query)); Debug((DEBUG_PROC, "client_io\n")); /* Receive request from client */ buflen = recvfrom(cur, buf, sizeof(buf) - 1, 0, &sa.sa, &sinlen); if (buflen <= 0) return; buf[buflen] = 0; /* Decode data, if neccessary */ if (ctx->blowfish) blowfish_dec(ctx->blowfish, (a_char *) buf, buflen); /* Check client IP address */ res = acl_check(&sa); if (!res) { char ibuf[INET6_ADDRSTRLEN]; logmsg("Ignoring query from %s", su_ntop(&sa, ibuf, (socklen_t) sizeof(ibuf))); return; } counter_query++, counter_p_query++; avc = av_new(NULL, NULL); av_char_to_array(avc, buf, NULL); serial = av_get(avc, AV_A_SERIAL); if (!serial) { char ibuf[INET6_ADDRSTRLEN]; logmsg("query from %s lacks serial", su_ntop(&sa, ibuf, (socklen_t) sizeof(ibuf))); counter_err++, counter_p_err++; av_free(avc); return; } q->serial = serial; q->serial_crc = crc32_update(INITCRC32, (u_char *) serial, strlen(serial)); if (RB_search(deferred_by_serial, q)) { char ibuf[INET6_ADDRSTRLEN]; Debug((DEBUG_PROC, "Duplicate detected\n")); logmsg("Ignoring duplicate query from %s (backlog: %d)", su_ntop(&sa, ibuf, (socklen_t) sizeof(ibuf)), backlog); counter_retry++, counter_p_retry++; av_free(avc); return; } if (av_get(avc, AV_A_RESULT)) { char ibuf[INET6_ADDRSTRLEN]; Debug((DEBUG_PROC, "AV_A_RESULT already set. Spoofing?\n")); logmsg("Ignoring query with pre-set result code " "from %s (backlog: %d)", su_ntop(&sa, ibuf, (socklen_t) sizeof(ibuf)), backlog); counter_err++, counter_p_err++; av_free(avc); return; } avt = av_get(avc, AV_A_TYPE); if (!avt || !strncmp(avt, AV_V_TYPE_PRIVATE_PREFIX, AV_V_TYPE_PRIVATE_PREFIX_LEN)) { counter_err++, counter_p_err++; av_free(avc); return; } av_setcb(avc, (void *) mavis_io, (void *) q); switch (mavis_send(mcx, &avc)) { case MAVIS_DEFERRED: Debug((DEBUG_PROC, "mavis_send yields DEFERRED\n")); q->sa = sa; q->fd = cur; q->serial = Xstrdup(serial); RB_insert(deferred_by_serial, q); q = NULL; backlog++; if (backlog > backlog_max) backlog_max = backlog; if (backlog > backlog_max_p) backlog_max_p = backlog; setproctitle("%s: backlog: %d", common_data.progname, backlog); return; case MAVIS_TIMEOUT: counter_expired++, counter_p_expired++; break; case MAVIS_FINAL: if (!transmit_password) { av_unset(avc, AV_A_PASSWORD); av_unset(avc, AV_A_DBPASSWORD); } av_send(avc, cur, &sa, ctx->blowfish); counter_answered++, counter_p_answered++; } av_free(avc); }
static void read_from_child(struct context *ctx, int cur) { ssize_t len; DebugIn(DEBUG_MAVIS); len = Read(ctx->fd_in, ctx->b_in + ctx->b_in_len, sizeof(ctx->b_in) - ctx->b_in_len - 1); if (len > 0) { char *t; int matchlevel = 0; Debug((DEBUG_PROC, "%s:%d %s\n", __FILE__, __LINE__, ctx->mcx->path)); ctx->b_in_len += len; ctx->b_in[ctx->b_in_len] = 0; for (t = ctx->b_in + ctx->b_in_len - 1; t > ctx->b_in; t--) switch (matchlevel) { case 0: if (*t != '\n') { DebugOut(DEBUG_MAVIS); return; } matchlevel++; break; case 1: if (!isdigit((int) *t)) { DebugOut(DEBUG_MAVIS); return; } matchlevel++; break; case 2: if (!isdigit((int) *t) && *t != '-' && *t != '=') { DebugOut(DEBUG_MAVIS); return; } if (*t == '=') matchlevel++; break; case 3: if (*t == '\n') { rb_node_t *r; struct query *q; char *serial = av_get(ctx->ac, AV_A_SERIAL); char *serial_old = alloca(strlen(serial) + 1); int result; strcpy(serial_old, serial); io_clr_i(ctx->mcx->io, ctx->fd_in); av_clear(ctx->ac); *++t = 0; av_char_to_array(ctx->ac, ctx->b_in, NULL); result = atoi(++t); ctx->in_use = 0; ctx->mcx->usage--; serial = av_get(ctx->ac, AV_A_SERIAL); if (!serial || strcmp(serial, serial_old)) { if (serial) logmsg("%s: %lu: out of sync: " "got %s, expected %s. Terminating.", ctx->mcx->argv[0], (u_long) ctx->pid, serial, serial_old); else logmsg("%s: %lu: missing serial. Terminating.", ctx->mcx->argv[0], (u_long) ctx->pid); av_free(ctx->ac); ctx->ac = NULL; kill(ctx->pid, SIGTERM); child_died(ctx, ctx->fd_in); DebugOut(DEBUG_MAVIS); return; } q = Xcalloc(1, sizeof(struct context)); q->ac = ctx->ac; ctx->ac = NULL; q->result = result; q->canceled = ctx->canceled; ctx->canceled = 0; RB_insert(ctx->mcx->outgoing, q); #ifdef DEBUG_RB fprintf(stderr, "EXT insert outgoing %p\n", q); #endif if (ctx->mcx->io_context_parent) { if (!RB_empty(ctx->mcx->backlog_fifo)) { rb_node_t *rbn = RB_first(ctx->mcx->backlog_fifo); struct query *qp = RB_payload(rbn, struct query *); Debug((DEBUG_PROC, "%s:%d\n", __FILE__, __LINE__)); RB_search_and_delete(ctx->mcx->backlog_app_ctx, qp); RB_search_and_delete(ctx->mcx->backlog_serial, qp); ctx->ac = qp->ac; qp->ac = NULL; RB_delete(ctx->mcx->backlog_fifo, rbn); #ifdef DEBUG_RB fprintf(stderr, "EXT remove backlog_fifo %p\n", RB_payload(rbn, void *)); #endif ctx->mcx->backlog_cur--; ctx->mcx->usage++; ctx->mcx->cx_stat[ctx->index].counter++; ctx->mcx->cx_stat[ctx->index].counter_p++; start_query(ctx); } while ((r = RB_first(ctx->mcx->outgoing))) { struct query *qp = RB_payload(r, struct query *); if (ctx->mcx->ac_bak) av_free(ctx->mcx->ac_bak); ctx->mcx->ac_bak = qp->ac_bak; qp->ac_bak = NULL; if (q->canceled) { av_free(ctx->mcx->ac_bak); ctx->mcx->ac_bak = NULL; RB_delete(ctx->mcx->outgoing, r); } else ((void (*)(void *)) qp->ac->app_cb) (qp->ac->app_ctx); } } DebugOut(DEBUG_MAVIS); return; }
static int mavis_send_in(mavis_ctx * mcx, av_ctx ** ac) { struct passwd *pw; char *t, *u, *p, *m; uid_t uid; int res; t = av_get(*ac, AV_A_TYPE); u = av_get(*ac, AV_A_USER); p = av_get(*ac, AV_A_PASSWORD); if (strcmp(t, AV_V_TYPE_FTP)) return MAVIS_DOWN; /* no VHOST support yet */ m = av_get(*ac, AV_A_FTP_ANONYMOUS); if (m && !strcmp(m, AV_V_BOOL_TRUE)) return MAVIS_DOWN; if (!(pw = getpwnam(u))) return MAVIS_DOWN; if (!pw->pw_dir) { av_set(*ac, AV_A_COMMENT, "home dir not set"); av_set(*ac, AV_A_RESULT, AV_V_RESULT_FAIL); return MAVIS_FINAL; } uid = geteuid(); seteuid(0); res = check_auth(mcx, u, p); seteuid(uid); /* The PAM routines may have spoiled our logging identity. */ logopen(); if (res) { char buf[1024]; av_set(*ac, AV_A_DBPASSWORD, p); av_setf(*ac, AV_A_UID, "%lu", (u_long) pw->pw_uid); av_setf(*ac, AV_A_GID, "%lu", (u_long) pw->pw_gid); /* attempt to get supplemental groups */ av_set(*ac, AV_A_GIDS, groups_getlist(pw->pw_name, pw->pw_gid, buf, sizeof(buf))); if (mcx->ftp_chroot) { t = strstr(pw->pw_dir, "/./"); if (t) { *t = 0; av_set(*ac, AV_A_HOME, t + 2); } else av_set(*ac, AV_A_HOME, "/"); av_set(*ac, AV_A_ROOT, pw->pw_dir); } else { av_set(*ac, AV_A_HOME, pw->pw_dir); av_set(*ac, AV_A_ROOT, "/"); } } else av_set(*ac, AV_A_RESULT, AV_V_RESULT_FAIL); return MAVIS_FINAL; }