int detect_mitm(isieve_t *obj, char *mechlist) { char *new_mechlist; int ch, r = 0; /* wait and probe for possible automatic capability response */ usleep(250000); prot_NONBLOCK(obj->pin); if ((ch = prot_getc(obj->pin)) != EOF) { /* automatic capability response */ prot_ungetc(ch, obj->pin); } else { /* manually ask for capabilities */ prot_printf(obj->pout, "CAPABILITY\r\n"); prot_flush(obj->pout); } prot_BLOCK(obj->pin); if ((new_mechlist = read_capability(obj))) { /* if the server still advertises SASL mechs, compare lists */ r = strcmp(new_mechlist, mechlist); free(new_mechlist); } return r; }
/* xxx start of separate proxy-only code (remove when we move to a unified environment) */ static int chomp(struct protstream *p, const char *s) { int c = prot_getc(p); while (*s) { if (tolower(c) != tolower(*s)) { break; } s++; c = prot_getc(p); } if (*s) { if (c != EOF) prot_ungetc(c, p); c = EOF; } return c; }
HIDDEN int parse_backup_line(struct protstream *in, time_t *ts, struct buf *cmd, struct dlist **kin) { struct dlist *dl = NULL; struct buf buf = BUF_INITIALIZER; int64_t t; int c; c = prot_getc(in); if (c == '#') eatline(in, c); else prot_ungetc(c, in); c = getint64(in, &t); if (c == EOF) goto fail; c = getword(in, &buf); if (c == EOF) goto fail; c = dlist_parse(&dl, /*parsekeys*/ 1, 1, in); if (!dl) { fprintf(stderr, "\ndidn't parse dlist, error %i\n", c); goto fail; } if (c == '\r') c = prot_getc(in); if (c != '\n') { fprintf(stderr, "expected newline, got '%c'\n", c); eatline(in, c); goto fail; } if (kin) *kin = dl; if (cmd) buf_copy(cmd, &buf); if (ts) *ts = (time_t) t; buf_free(&buf); return c; fail: if (dl) dlist_free(&dl); buf_free(&buf); return c; }
int yylex(lexstate_t * lvalp, void * client) { int ch; char buffer[ACAP_MAX_QSTR_LEN]; /* big enough for everything */ char *buff_ptr = buffer; /* ptr into the buffer */ char *buff_end = buffer + ACAP_MAX_QSTR_LEN -1; unsigned long count=0; int result = SIEVE_OK; int synchronizing; /* wheather we are in the process of reading a synchronizing string or not */ struct protstream *stream=(struct protstream *) client; while (1) { /* get a character this may block on a read if there is nothing in the buffer */ ch = prot_getc(stream); if (ch == -1) return SIEVE_FAIL; switch (lexer_state) { case LEXER_STATE_RECOVER: if (ch == '\r') lexer_state=LEXER_STATE_RECOVER_CR; break; case LEXER_STATE_RECOVER_CR: if (ch == '\n') lexer_state=LEXER_STATE_NORMAL; return EOL; case LEXER_STATE_CR: if (ch == '\n') { lexer_state=LEXER_STATE_NORMAL; return EOL; } /* otherwise, life is bad */ ERR_PUSHBACK(); case LEXER_STATE_QSTR: if (ch == '\"') { /* End of the string */ lvalp->str = NULL; result = string_allocate(buff_ptr - buffer, buffer, &lvalp->str); if (result != SIEVE_OK) ERR_PUSHBACK(); lexer_state=LEXER_STATE_NORMAL; return STRING; } if (ch == '\0' || 0x7F < ((unsigned char)ch)) ERR_PUSHBACK(); /* Otherwise, we're appending a character */ if (buff_end <= buff_ptr) ERR_PUSHBACK(); /* too long! */ if (ch == '\\') { ch=prot_getc(stream); if (result != SIEVE_OK) ERR(); if (ch != '\"' && ch != '\\') ERR_PUSHBACK(); } *buff_ptr++ = ch; break; case LEXER_STATE_LITERAL: if (('0' <= ch) && (ch <= '9')) { unsigned long newcount = count * 10 + (ch - '0'); if (newcount < count) ERR_PUSHBACK(); /* overflow */ /* * XXX This should be fatal if non-synchronizing. */ count = newcount; break; } synchronizing = FALSE; if (ch != '}') ERR_PUSHBACK(); ch=prot_getc(stream); if (ch < 0) ERR(); if (ch != '\r') ERR_PUSHBACK(); ch=prot_getc(stream); if (ch < 0) ERR(); if (ch != '\n') ERR_PUSHBACK(); lvalp->str = NULL; result = string_allocate(count, NULL, &lvalp->str); if (result != SIEVE_OK) ERR_PUSHBACK(); /* there is a literal string on the wire. let's read it */ { char *it = string_DATAPTR(lvalp->str), *end = it + count; while (it < end) { *it=prot_getc(stream); it++; } *it = '\0'; } lexer_state=LEXER_STATE_NORMAL; return STRING; case LEXER_STATE_NUMBER: if (('0' <= ch) && (ch <= '9')) { unsigned long newcount = count * 10 + (ch - '0'); if (newcount < count) ERR_PUSHBACK(); /* overflow */ count = newcount; } else { lvalp->number = count; lexer_state=LEXER_STATE_NORMAL; prot_ungetc(ch, stream); return NUMBER; } break; case LEXER_STATE_NORMAL: if (isalpha((unsigned char) ch)) { lexer_state=LEXER_STATE_ATOM; *buff_ptr++ = tolower(ch); break; } switch (ch) { case '(': return '('; case ')': return ')'; case ' ': return ' '; case '\"': lexer_state=LEXER_STATE_QSTR; break; case '*': return '*'; case '0': /* fall through all numbers */ case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': count = ch - '0'; lexer_state=LEXER_STATE_NUMBER; break; case '{': count = 0; synchronizing = TRUE; lexer_state=LEXER_STATE_LITERAL; break; case '\r': lexer_state=LEXER_STATE_CR; break; case '\n': lexer_state=LEXER_STATE_NORMAL; return EOL; break; default: ERR_PUSHBACK(); } break; case LEXER_STATE_ATOM: if (!isalpha((unsigned char) ch)) { int token; buffer[ buff_ptr - buffer] = '\0'; /* We've got the atom. */ token = token_lookup((char *) buffer, (int) (buff_ptr - buffer)); if (token!=-1) { lexer_state=LEXER_STATE_NORMAL; prot_ungetc(ch, stream); return token; } else ERR_PUSHBACK(); } if (buff_end <= buff_ptr) ERR_PUSHBACK(); /* atom too long */ *buff_ptr++ = tolower(ch); break; } } /* while (1) */ /* never reached */ }
int timlex(mystring_t **outstr, unsigned long *outnum, struct protstream *stream) { int ch; char *buff_ptr; char *buff_end; unsigned long tmpnum = 0; unsigned long count=0; int result = TIMSIEVE_OK; buff_ptr = buffer; /* ptr into the buffer */ buff_end = buffer + maxscriptsize - 10; /* ptr to end of buffer */ while (1) { /* get a character this may block on a read if there is nothing in the buffer */ ch=prot_getc(stream); if (ch==EOF) { /* Lost connection */ return EOF; } switch (lexer_state) { case LEXER_STATE_RECOVER: if (ch == '\n') { lexer_state=LEXER_STATE_NORMAL; } if (ch == '\r') lexer_state=LEXER_STATE_RECOVER_CR; break; case LEXER_STATE_RECOVER_CR: if (ch == '\n') lexer_state=LEXER_STATE_NORMAL; break; case LEXER_STATE_CR: if (ch == '\n') { lexer_state=LEXER_STATE_NORMAL; return EOL; } /* otherwise, life is bad */ ERR_PUSHBACK(); case LEXER_STATE_QSTR: if (ch == '\"') { /* End of the string */ if (outstr!=NULL) { *outstr = NULL; result = string_allocate(buff_ptr - buffer, buffer, outstr); if (result != TIMSIEVE_OK) ERR_PUSHBACK(); } /*} */ lexer_state=LEXER_STATE_NORMAL; return STRING; } /* illegal character */ if (ch == '\0' || ch == '\r' || ch == '\n' || 0x7F < ((unsigned char)ch)) { ERR_PUSHBACK(); } /* Otherwise, we're appending a character */ if (buff_end <= buff_ptr) ERR_PUSHBACK(); /* too long! */ if (ch == '\\') { ch=prot_getc(stream); if (result != TIMSIEVE_OK) ERR(); if (ch != '\"' && ch != '\\') ERR_PUSHBACK(); } *buff_ptr++ = ch; break; case LEXER_STATE_LITERAL: if (('0' <= ch) && (ch <= '9')) { unsigned long newcount = count * 10 + (ch - '0'); if (newcount < count) ERR_PUSHBACK(); /* overflow */ /* * XXX This should be fatal if non-synchronizing. */ count = newcount; break; } if (ch != '+') ERR_PUSHBACK(); ch=prot_getc(stream); if (ch != '}') ERR_PUSHBACK(); ch=prot_getc(stream); if (ch < 0) ERR(); if (ch != '\r') ERR_PUSHBACK(); ch=prot_getc(stream); if (ch < 0) ERR(); if (ch != '\n') ERR_PUSHBACK(); if (count > maxscriptsize) { /* too big, eat the input */ for(;count > 0;count--) { if(prot_getc(stream)==EOF) break; } ERR(); } if (outstr!=NULL) { *outstr = NULL; result = string_allocate(count, NULL, outstr); if (result != TIMSIEVE_OK) ERR_PUSHBACK(); } /* there is a literal string on the wire. let's read it */ if (outstr!=NULL) { char *it = string_DATAPTR(*outstr), *end = it + count; while (it < end) { *it=prot_getc(stream); it++; } } else { /* just read the chars and throw them away */ unsigned long lup; for (lup=0;lup<count;lup++) (void)prot_getc(stream); } lexer_state=LEXER_STATE_NORMAL; return STRING; case LEXER_STATE_NUMBER: if (Uisdigit(ch)) { unsigned long newcount = tmpnum * 10 + (ch - '0'); if (newcount < tmpnum) ERR_PUSHBACK(); /* overflow */ tmpnum = newcount; } else { lexer_state=LEXER_STATE_NORMAL; prot_ungetc(ch, stream); if (outnum) *outnum = tmpnum; return NUMBER; } break; case LEXER_STATE_NORMAL: if (Uisalpha(ch)) { lexer_state=LEXER_STATE_ATOM; *buff_ptr++ = tolower(ch); break; } if (Uisdigit(ch)) { lexer_state=LEXER_STATE_NUMBER; tmpnum = ch -'0'; break; } switch (ch) { case '(': return '('; case ')': return ')'; case ' ': return ' '; case '\"': lexer_state=LEXER_STATE_QSTR; break; case '*': return '*'; case '{': count = 0; lexer_state=LEXER_STATE_LITERAL; break; case '\r': lexer_state=LEXER_STATE_CR; break; case '\n': lexer_state=LEXER_STATE_NORMAL; return EOL; break; default: return ch; } break; case LEXER_STATE_ATOM: if (!Uisalpha(ch)) { int token; buffer[ buff_ptr - buffer] = '\0'; /* We've got the atom. */ token = token_lookup((char *) buffer, (int) (buff_ptr - buffer)); if (token!=-1) { lexer_state=LEXER_STATE_NORMAL; prot_ungetc(ch, stream); return token; } else ERR_PUSHBACK(); } if (buff_end <= buff_ptr) ERR_PUSHBACK(); /* atom too long */ *buff_ptr++ = tolower(ch); break; } } /* while (1) */ /* never reached */ }
void proxy_copy(const char *tag, char *sequence, char *name, int myrights, int usinguid, struct backend *s) { char mytag[128]; struct d { char *idate; char *flags; unsigned int seqno, uid; struct d *next; } *head, *p, *q; int c; /* find out what the flags & internaldate for this message are */ proxy_gentag(mytag, sizeof(mytag)); prot_printf(backend_current->out, "%s %s %s (Flags Internaldate)\r\n", tag, usinguid ? "Uid Fetch" : "Fetch", sequence); head = (struct d *) xmalloc(sizeof(struct d)); head->flags = NULL; head->idate = NULL; head->seqno = head->uid = 0; head->next = NULL; p = head; /* read all the responses into the linked list */ for (/* each FETCH response */;;) { unsigned int seqno = 0, uidno = 0; char *flags = NULL, *idate = NULL; /* read a line */ c = prot_getc(backend_current->in); if (c != '*') break; c = prot_getc(backend_current->in); if (c != ' ') { /* protocol error */ c = EOF; break; } /* check for OK/NO/BAD/BYE response */ if (!isdigit(c = prot_getc(backend_current->in))) { prot_printf(imapd_out, "* %c", c); pipe_to_end_of_response(backend_current, 0); continue; } /* read seqno */ prot_ungetc(c, backend_current->in); c = getuint32(backend_current->in, &seqno); if (seqno == 0 || c != ' ') { /* we suck and won't handle this case */ c = EOF; break; } c = chomp(backend_current->in, "fetch ("); if (c == EOF) { c = chomp(backend_current->in, "exists\r"); if (c == '\n') { /* got EXISTS response */ prot_printf(imapd_out, "* %d EXISTS\r\n", seqno); continue; } } if (c == EOF) { /* XXX the "exists" check above will eat "ex" */ c = chomp(backend_current->in, "punge\r"); if (c == '\n') { /* got EXPUNGE response */ prot_printf(imapd_out, "* %d EXPUNGE\r\n", seqno); continue; } } if (c == EOF) { c = chomp(backend_current->in, "recent\r"); if (c == '\n') { /* got RECENT response */ prot_printf(imapd_out, "* %d RECENT\r\n", seqno); continue; } } /* huh, don't get this response */ if (c == EOF) break; for (/* each fetch item */;;) { /* looking at the first character in an item */ switch (c) { case 'f': case 'F': /* flags? */ c = chomp(backend_current->in, "lags"); if (c != ' ') { c = EOF; } else c = prot_getc(backend_current->in); if (c != '(') { c = EOF; } else { flags = grab(backend_current->in, ')'); c = prot_getc(backend_current->in); } break; case 'i': case 'I': /* internaldate? */ c = chomp(backend_current->in, "nternaldate"); if (c != ' ') { c = EOF; } else c = prot_getc(backend_current->in); if (c != '"') { c = EOF; } else { idate = grab(backend_current->in, '"'); c = prot_getc(backend_current->in); } break; case 'u': case 'U': /* uid */ c = chomp(backend_current->in, "id"); if (c != ' ') { c = EOF; } else c = getuint32(backend_current->in, &uidno); break; default: /* hmm, don't like the smell of it */ c = EOF; break; } /* looking at either SP seperating items or a RPAREN */ if (c == ' ') { c = prot_getc(backend_current->in); } else if (c == ')') break; else { c = EOF; break; } } /* if c == EOF we have either a protocol error or a situation we can't handle, and we should die. */ if (c == ')') c = prot_getc(backend_current->in); if (c == '\r') c = prot_getc(backend_current->in); if (c != '\n') { c = EOF; free(flags); free(idate); break; } /* if we're missing something, we should echo */ if (!flags || !idate) { char sep = '('; prot_printf(imapd_out, "* %d FETCH ", seqno); if (uidno) { prot_printf(imapd_out, "%cUID %d", sep, uidno); sep = ' '; } if (flags) { prot_printf(imapd_out, "%cFLAGS %s", sep, flags); sep = ' '; } if (idate) { prot_printf(imapd_out, "%cINTERNALDATE %s", sep, flags); sep = ' '; } prot_printf(imapd_out, ")\r\n"); if (flags) free(flags); if (idate) free(idate); continue; } /* add to p->next */ p->next = xmalloc(sizeof(struct d)); p = p->next; p->idate = idate; p->flags = editflags(flags); p->uid = uidno; p->seqno = seqno; p->next = NULL; } if (c != EOF) { prot_ungetc(c, backend_current->in); /* we should be looking at the tag now */ pipe_until_tag(backend_current, tag, 0); } if (c == EOF) { /* uh oh, we're not happy */ fatal("Lost connection to selected backend", EC_UNAVAILABLE); } /* start the append */ prot_printf(s->out, "%s Append {" SIZE_T_FMT "+}\r\n%s", tag, strlen(name), name); prot_printf(backend_current->out, "%s %s %s (Rfc822.peek)\r\n", mytag, usinguid ? "Uid Fetch" : "Fetch", sequence); for (/* each FETCH response */;;) { unsigned int seqno = 0, uidno = 0; /* read a line */ c = prot_getc(backend_current->in); if (c != '*') break; c = prot_getc(backend_current->in); if (c != ' ') { /* protocol error */ c = EOF; break; } /* check for OK/NO/BAD/BYE response */ if (!isdigit(c = prot_getc(backend_current->in))) { prot_printf(imapd_out, "* %c", c); pipe_to_end_of_response(backend_current, 0); continue; } /* read seqno */ prot_ungetc(c, backend_current->in); c = getuint32(backend_current->in, &seqno); if (seqno == 0 || c != ' ') { /* we suck and won't handle this case */ c = EOF; break; } c = chomp(backend_current->in, "fetch ("); if (c == EOF) { /* not a fetch response */ c = chomp(backend_current->in, "exists\r"); if (c == '\n') { /* got EXISTS response */ prot_printf(imapd_out, "* %d EXISTS\r\n", seqno); continue; } } if (c == EOF) { /* not an exists response */ /* XXX the "exists" check above will eat "ex" */ c = chomp(backend_current->in, "punge\r"); if (c == '\n') { /* got EXPUNGE response */ prot_printf(imapd_out, "* %d EXPUNGE\r\n", seqno); continue; } } if (c == EOF) { /* not an exists response */ c = chomp(backend_current->in, "recent\r"); if (c == '\n') { /* got RECENT response */ prot_printf(imapd_out, "* %d RECENT\r\n", seqno); continue; } } if (c == EOF) { /* huh, don't get this response */ break; } /* find seqno in the list */ p = head; while (p->next && seqno != p->next->seqno) p = p->next; if (!p->next) break; q = p->next; p->next = q->next; for (/* each fetch item */;;) { int sz = 0; switch (c) { case 'u': case 'U': c = chomp(backend_current->in, "id"); if (c != ' ') { c = EOF; } else c = getuint32(backend_current->in, &uidno); break; case 'r': case 'R': c = chomp(backend_current->in, "fc822"); if (c == ' ') c = prot_getc(backend_current->in); if (c != '{') { /* NIL? */ eatline(backend_current->in, c); c = EOF; } else c = getint32(backend_current->in, &sz); if (c == '}') c = prot_getc(backend_current->in); if (c == '\r') c = prot_getc(backend_current->in); if (c != '\n') c = EOF; if (c != EOF) { /* append p to s->out */ prot_printf(s->out, " (%s) \"%s\" {%d+}\r\n", q->flags, q->idate, sz); while (sz) { char buf[2048]; int j = (sz > (int) sizeof(buf) ? (int) sizeof(buf) : sz); j = prot_read(backend_current->in, buf, j); if(!j) break; prot_write(s->out, buf, j); sz -= j; } c = prot_getc(backend_current->in); } break; /* end of case */ default: c = EOF; break; } /* looking at either SP seperating items or a RPAREN */ if (c == ' ') { c = prot_getc(backend_current->in); } else if (c == ')') break; else { c = EOF; break; } } /* if c == EOF we have either a protocol error or a situation we can't handle, and we should die. */ if (c == ')') c = prot_getc(backend_current->in); if (c == '\r') c = prot_getc(backend_current->in); if (c != '\n') { c = EOF; break; } /* free q */ free(q->idate); free(q->flags); free(q); } if (c != EOF) { char *appenduid, *b; int res; /* pushback the first character of the tag we're looking at */ prot_ungetc(c, backend_current->in); /* nothing should be left in the linked list */ assert(head->next == NULL); /* ok, finish the append; we need the UIDVALIDITY and UIDs to return as part of our COPYUID response code */ prot_printf(s->out, "\r\n"); /* should be looking at 'mytag' on 'backend_current', 'tag' on 's' */ pipe_until_tag(backend_current, mytag, 0); res = pipe_until_tag(s, tag, 0); if (res == PROXY_OK) { if (myrights & ACL_READ) { appenduid = strchr(s->last_result.s, '['); /* skip over APPENDUID */ if (appenduid) { appenduid += strlen("[appenduid "); b = strchr(appenduid, ']'); if (b) *b = '\0'; prot_printf(imapd_out, "%s OK [COPYUID %s] %s\r\n", tag, appenduid, error_message(IMAP_OK_COMPLETED)); } else prot_printf(imapd_out, "%s OK %s\r\n", tag, s->last_result.s); } else { prot_printf(imapd_out, "%s OK %s\r\n", tag, error_message(IMAP_OK_COMPLETED)); } } else { prot_printf(imapd_out, "%s %s", tag, s->last_result.s); } } else { /* abort the append */ prot_printf(s->out, " {0+}\r\n\r\n"); pipe_until_tag(backend_current, mytag, 0); pipe_until_tag(s, tag, 0); /* report failure */ prot_printf(imapd_out, "%s NO inter-server COPY failed\r\n", tag); } /* free dynamic memory */ while (head) { p = head; head = head->next; if (p->idate) free(p->idate); if (p->flags) free(p->flags); free(p); } }
static void proxy_part_filldata(partlist_t *part_list, int idx) { char mytag[128]; struct backend *be; partitem_t *item = &part_list->items[idx]; item->id = 0; item->available = 0; item->total = 0; item->quota = 0.; syslog(LOG_DEBUG, "checking free space on server '%s'", item->value); /* connect to server */ be = proxy_findserver(item->value, &imap_protocol, proxy_userid, &backend_cached, &backend_current, &backend_inbox, imapd_in); if (be) { uint64_t server_available = 0; uint64_t server_total = 0; const char *annot = (part_list->mode == PART_SELECT_MODE_FREESPACE_MOST) ? "freespace/total" : "freespace/percent/most"; struct buf cmd = BUF_INITIALIZER; int c; /* fetch annotation from remote */ proxy_gentag(mytag, sizeof(mytag)); if (CAPA(be, CAPA_METADATA)) { buf_printf(&cmd, "METADATA \"\" (\"/shared" IMAP_ANNOT_NS "%s\"", annot); } else { buf_printf(&cmd, "ANNOTATION \"\" \"" IMAP_ANNOT_NS "%s\" " "(\"value.shared\"", annot); } prot_printf(be->out, "%s GET%s)\r\n", mytag, buf_cstring(&cmd)); prot_flush(be->out); for (/* each annotation response */;;) { /* read a line */ c = prot_getc(be->in); if (c != '*') break; c = prot_getc(be->in); if (c != ' ') { /* protocol error */ c = EOF; break; } c = chomp(be->in, buf_cstring(&cmd)); if (c == ' ') c = prot_getc(be->in); if ((c == EOF) || (c != '\"')) { /* we don't care about this response */ eatline(be->in, c); continue; } /* read available */ c = getuint64(be->in, &server_available); if (c != ';') { c = EOF; break; } /* read total */ c = getuint64(be->in, &server_total); if (c != '\"') { c = EOF; break; } eatline(be->in, c); /* we don't care about the rest of the line */ } buf_free(&cmd); if (c != EOF) { prot_ungetc(c, be->in); /* we should be looking at the tag now */ eatline(be->in, c); } if (c == EOF) { /* uh oh, we're not happy */ fatal("Lost connection to backend", EC_UNAVAILABLE); } /* unique id */ item->id = idx; item->available = server_available; item->total = server_total; } }
int proxy_catenate_url(struct backend *s, struct imapurl *url, FILE *f, unsigned long *size, const char **parseerr) { char mytag[128]; int c, r = 0, found = 0; unsigned int uidvalidity = 0; *size = 0; *parseerr = NULL; /* select the mailbox (read-only) */ proxy_gentag(mytag, sizeof(mytag)); prot_printf(s->out, "%s Examine {" SIZE_T_FMT "+}\r\n%s\r\n", mytag, strlen(url->mailbox), url->mailbox); for (/* each examine response */;;) { /* read a line */ c = prot_getc(s->in); if (c != '*') break; c = prot_getc(s->in); if (c != ' ') { /* protocol error */ c = EOF; break; } c = chomp(s->in, "ok [uidvalidity"); if (c == EOF) { /* we don't care about this response */ eatline(s->in, c); continue; } /* read uidvalidity */ c = getuint32(s->in, &uidvalidity); if (c != ']') { c = EOF; break; } eatline(s->in, c); /* we don't care about the rest of the line */ } if (c != EOF) { prot_ungetc(c, s->in); /* we should be looking at the tag now */ eatline(s->in, c); } if (c == EOF) { /* uh oh, we're not happy */ fatal("Lost connection to backend", EC_UNAVAILABLE); } if (url->uidvalidity && (uidvalidity != url->uidvalidity)) { *parseerr = "Uidvalidity of mailbox has changed"; r = IMAP_BADURL; goto unselect; } /* fetch the bodypart */ proxy_gentag(mytag, sizeof(mytag)); prot_printf(s->out, "%s Uid Fetch %lu Body.Peek[%s]\r\n", mytag, url->uid, url->section ? url->section : ""); for (/* each fetch response */;;) { unsigned int seqno; next_resp: /* read a line */ c = prot_getc(s->in); if (c != '*') break; c = prot_getc(s->in); if (c != ' ') { /* protocol error */ c = EOF; break; } /* read seqno */ c = getuint32(s->in, &seqno); if (seqno == 0 || c != ' ') { /* we suck and won't handle this case */ c = EOF; break; } c = chomp(s->in, "fetch ("); if (c == EOF) { /* not a fetch response */ eatline(s->in, c); continue; } for (/* each fetch item */;;) { unsigned uid, sz = 0; switch (c) { case 'u': case 'U': c = chomp(s->in, "id"); if (c != ' ') { c = EOF; } else { c = getuint32(s->in, &uid); if (uid != url->uid) { /* not our response */ eatline(s->in, c); goto next_resp; } } break; case 'b': case 'B': c = chomp(s->in, "ody["); while (c != ']') c = prot_getc(s->in); if (c == ']') c = prot_getc(s->in); if (c == ' ') c = prot_getc(s->in); if (c == '{') { c = getuint32(s->in, &sz); if (c == '}') c = prot_getc(s->in); if (c == '\r') c = prot_getc(s->in); if (c != '\n') c = EOF; } else if (c == 'n' || c == 'N') { c = chomp(s->in, "il"); r = IMAP_BADURL; *parseerr = "No such message part"; } if (c != EOF) { /* catenate to f */ found = 1; *size = sz; while (sz) { char buf[2048]; int j = (sz > sizeof(buf) ? sizeof(buf) : sz); j = prot_read(s->in, buf, j); if(!j) break; fwrite(buf, j, 1, f); sz -= j; } c = prot_getc(s->in); } break; /* end of case */ default: /* probably a FLAGS item */ eatline(s->in, c); goto next_resp; } /* looking at either SP separating items or a RPAREN */ if (c == ' ') { c = prot_getc(s->in); } else if (c == ')') break; else { c = EOF; break; } } /* if c == EOF we have either a protocol error or a situation we can't handle, and we should die. */ if (c == ')') c = prot_getc(s->in); if (c == '\r') c = prot_getc(s->in); if (c != '\n') { c = EOF; break; } } if (c != EOF) { prot_ungetc(c, s->in); /* we should be looking at the tag now */ eatline(s->in, c); } if (c == EOF) { /* uh oh, we're not happy */ fatal("Lost connection to backend", EC_UNAVAILABLE); } unselect: /* unselect the mailbox */ proxy_gentag(mytag, sizeof(mytag)); prot_printf(s->out, "%s Unselect\r\n", mytag); for (/* each unselect response */;;) { /* read a line */ c = prot_getc(s->in); if (c != '*') break; c = prot_getc(s->in); if (c != ' ') { /* protocol error */ c = EOF; break; } /* we don't care about this response */ eatline(s->in, c); } if (c != EOF) { prot_ungetc(c, s->in); /* we should be looking at the tag now */ eatline(s->in, c); } if (c == EOF) { /* uh oh, we're not happy */ fatal("Lost connection to backend", EC_UNAVAILABLE); } if (!r && !found) { r = IMAP_BADURL; *parseerr = "No such message in mailbox"; } return r; }
struct backend *backend_connect(struct backend *ret_backend, const char *server, struct protocol_t *prot, const char *userid, sasl_callback_t *cb, const char **auth_status) { /* need to (re)establish connection to server or create one */ int sock = -1; int r; int err = -1; int ask = 1; /* should we explicitly ask for capabilities? */ struct addrinfo hints, *res0 = NULL, *res; struct sockaddr_un sunsock; char buf[2048]; struct sigaction action; struct backend *ret; char rsessionid[MAX_SESSIONID_SIZE]; if (!ret_backend) { ret = xzmalloc(sizeof(struct backend)); strlcpy(ret->hostname, server, sizeof(ret->hostname)); ret->timeout = NULL; } else ret = ret_backend; if (server[0] == '/') { /* unix socket */ res0 = &hints; memset(res0, 0, sizeof(struct addrinfo)); res0->ai_family = PF_UNIX; res0->ai_socktype = SOCK_STREAM; res0->ai_addr = (struct sockaddr *) &sunsock; res0->ai_addrlen = sizeof(sunsock.sun_family) + strlen(server) + 1; #ifdef SIN6_LEN res0->ai_addrlen += sizeof(sunsock.sun_len); sunsock.sun_len = res0->ai_addrlen; #endif sunsock.sun_family = AF_UNIX; strlcpy(sunsock.sun_path, server, sizeof(sunsock.sun_path)); /* XXX set that we are preauthed */ /* change hostname to 'config_servername' */ strlcpy(ret->hostname, config_servername, sizeof(ret->hostname)); } else { /* inet socket */ memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; err = getaddrinfo(server, prot->service, &hints, &res0); if (err) { syslog(LOG_ERR, "getaddrinfo(%s) failed: %s", server, gai_strerror(err)); goto error; } } /* Setup timeout */ timedout = 0; action.sa_flags = 0; action.sa_handler = timed_out; sigemptyset(&action.sa_mask); if(sigaction(SIGALRM, &action, NULL) < 0) { syslog(LOG_ERR, "Setting timeout in backend_connect failed: sigaction: %m"); /* continue anyway */ } for (res = res0; res; res = res->ai_next) { sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (sock < 0) continue; alarm(config_getint(IMAPOPT_CLIENT_TIMEOUT)); if (connect(sock, res->ai_addr, res->ai_addrlen) >= 0) break; if(errno == EINTR && timedout == 1) errno = ETIMEDOUT; close(sock); sock = -1; } /* Remove timeout code */ alarm(0); signal(SIGALRM, SIG_IGN); if (sock < 0) { if (res0 != &hints) freeaddrinfo(res0); syslog(LOG_ERR, "connect(%s) failed: %m", server); goto error; } memcpy(&ret->addr, res->ai_addr, res->ai_addrlen); if (res0 != &hints) freeaddrinfo(res0); ret->in = prot_new(sock, 0); ret->out = prot_new(sock, 1); ret->sock = sock; prot_setflushonread(ret->in, ret->out); ret->prot = prot; /* use literal+ to send literals */ prot_setisclient(ret->in, 1); prot_setisclient(ret->out, 1); if (prot->banner.auto_capa) { /* try to get the capabilities from the banner */ r = ask_capability(ret, /*dobanner*/1, AUTO_CAPA_BANNER); if (r) { /* found capabilities in banner -> don't ask */ ask = 0; } } else { do { /* read the initial greeting */ if (!prot_fgets(buf, sizeof(buf), ret->in)) { syslog(LOG_ERR, "backend_connect(): couldn't read initial greeting: %s", ret->in->error ? ret->in->error : "(null)"); goto error; } } while (strncasecmp(buf, prot->banner.resp, strlen(prot->banner.resp))); strncpy(ret->banner, buf, 2048); } if (ask) { /* get the capabilities */ ask_capability(ret, /*dobanner*/0, AUTO_CAPA_NO); } /* now need to authenticate to backend server, unless we're doing LMTP/CSYNC on a UNIX socket (deliver/sync_client) */ if ((server[0] != '/') || (strcmp(prot->sasl_service, "lmtp") && strcmp(prot->sasl_service, "csync"))) { char *old_mechlist = backend_get_cap_params(ret, CAPA_AUTH); const char *my_status; if ((r = backend_authenticate(ret, userid, cb, &my_status))) { syslog(LOG_ERR, "couldn't authenticate to backend server: %s", sasl_errstring(r, NULL, NULL)); free(old_mechlist); goto error; } else { const void *ssf; sasl_getprop(ret->saslconn, SASL_SSF, &ssf); if (*((sasl_ssf_t *) ssf)) { /* if we have a SASL security layer, compare SASL mech lists before/after AUTH to check for a MITM attack */ char *new_mechlist; int auto_capa = (prot->sasl_cmd.auto_capa == AUTO_CAPA_AUTH_SSF); if (!strcmp(prot->service, "sieve")) { /* XXX Hack to handle ManageSieve servers. * No way to tell from protocol if server will * automatically send capabilities, so we treat it * as optional. */ char ch; /* wait and probe for possible auto-capability response */ usleep(250000); prot_NONBLOCK(ret->in); if ((ch = prot_getc(ret->in)) != EOF) { prot_ungetc(ch, ret->in); } else { auto_capa = AUTO_CAPA_AUTH_NO; } prot_BLOCK(ret->in); } ask_capability(ret, /*dobanner*/0, auto_capa); new_mechlist = backend_get_cap_params(ret, CAPA_AUTH); if (new_mechlist && old_mechlist && strcmp(new_mechlist, old_mechlist)) { syslog(LOG_ERR, "possible MITM attack:" "list of available SASL mechanisms changed"); free(new_mechlist); free(old_mechlist); goto error; } free(new_mechlist); } else if (prot->sasl_cmd.auto_capa == AUTO_CAPA_AUTH_OK) { /* try to get the capabilities from the AUTH success response */ forget_capabilities(ret); parse_capability(ret, my_status); post_parse_capability(ret); } if (!(strcmp(prot->service, "imap") && (strcmp(prot->service, "pop3")))) { parse_sessionid(my_status, rsessionid); syslog(LOG_NOTICE, "proxy %s sessionid=<%s> remote=<%s>", userid, session_id(), rsessionid); } } if (auth_status) *auth_status = my_status; free(old_mechlist); } /* start compression if requested and both client/server support it */ if (config_getswitch(IMAPOPT_PROXY_COMPRESS) && ret && CAPA(ret, CAPA_COMPRESS) && prot->compress_cmd.cmd && do_compress(ret, &prot->compress_cmd)) { syslog(LOG_ERR, "couldn't enable compression on backend server"); goto error; } return ret; error: forget_capabilities(ret); if (ret->in) { prot_free(ret->in); ret->in = NULL; } if (ret->out) { prot_free(ret->out); ret->out = NULL; } if (sock >= 0) close(sock); if (ret->saslconn) { sasl_dispose(&ret->saslconn); ret->saslconn = NULL; } if (!ret_backend) free(ret); return NULL; }
static int backend_login(struct backend *ret, const char *userid, sasl_callback_t *cb, const char **auth_status, int noauth) { int r = 0; int ask = 1; /* should we explicitly ask for capabilities? */ char buf[2048]; struct protocol_t *prot = ret->prot; if (prot->type != TYPE_STD) return -1; if (prot->u.std.banner.auto_capa) { /* try to get the capabilities from the banner */ r = ask_capability(ret, /*dobanner*/1, AUTO_CAPA_BANNER); if (r) { /* found capabilities in banner -> don't ask */ ask = 0; } } else { do { /* read the initial greeting */ if (!prot_fgets(buf, sizeof(buf), ret->in)) { syslog(LOG_ERR, "backend_login(): couldn't read initial greeting: %s", ret->in->error ? ret->in->error : "(null)"); return -1; } } while (strncasecmp(buf, prot->u.std.banner.resp, strlen(prot->u.std.banner.resp))); xstrncpy(ret->banner, buf, 2048); } if (ask) { /* get the capabilities */ ask_capability(ret, /*dobanner*/0, AUTO_CAPA_NO); } /* now need to authenticate to backend server, unless we're doing LMTP/CSYNC on a UNIX socket (deliver/sync_client) */ if (!noauth) { char *old_mechlist = backend_get_cap_params(ret, CAPA_AUTH); const char *my_status; if ((r = backend_authenticate(ret, userid, cb, &my_status))) { syslog(LOG_ERR, "couldn't authenticate to backend server: %s", sasl_errstring(r, NULL, NULL)); free(old_mechlist); return -1; } else { const void *ssf; sasl_getprop(ret->saslconn, SASL_SSF, &ssf); if (*((sasl_ssf_t *) ssf)) { /* if we have a SASL security layer, compare SASL mech lists before/after AUTH to check for a MITM attack */ char *new_mechlist; int auto_capa = (prot->u.std.sasl_cmd.auto_capa == AUTO_CAPA_AUTH_SSF); if (!strcmp(prot->service, "sieve")) { /* XXX Hack to handle ManageSieve servers. * No way to tell from protocol if server will * automatically send capabilities, so we treat it * as optional. */ char ch; /* wait and probe for possible auto-capability response */ usleep(250000); prot_NONBLOCK(ret->in); if ((ch = prot_getc(ret->in)) != EOF) { prot_ungetc(ch, ret->in); } else { auto_capa = AUTO_CAPA_AUTH_NO; } prot_BLOCK(ret->in); } ask_capability(ret, /*dobanner*/0, auto_capa); new_mechlist = backend_get_cap_params(ret, CAPA_AUTH); if (new_mechlist && old_mechlist && strcmp(new_mechlist, old_mechlist)) { syslog(LOG_ERR, "possible MITM attack:" "list of available SASL mechanisms changed"); if (new_mechlist) free(new_mechlist); if (old_mechlist) free(old_mechlist); return -1; } free(new_mechlist); } else if (prot->u.std.sasl_cmd.auto_capa == AUTO_CAPA_AUTH_OK) { /* try to get the capabilities from the AUTH success response */ forget_capabilities(ret); parse_capability(ret, my_status); post_parse_capability(ret); } if (!(strcmp(prot->service, "imap") && (strcmp(prot->service, "pop3")))) { char rsessionid[MAX_SESSIONID_SIZE]; parse_sessionid(my_status, rsessionid); syslog(LOG_NOTICE, "auditlog: proxy %s sessionid=<%s> remote=<%s>", userid, session_id(), rsessionid); } } if (auth_status) *auth_status = my_status; free(old_mechlist); } /* start compression if requested and both client/server support it */ if (config_getswitch(IMAPOPT_PROXY_COMPRESS) && CAPA(ret, CAPA_COMPRESS) && prot->u.std.compress_cmd.cmd) { r = do_compress(ret, &prot->u.std.compress_cmd); if (r) { syslog(LOG_NOTICE, "couldn't enable compression on backend server: %s", error_message(r)); r = 0; /* not a fail-level error */ } } return 0; }
EXPORTED int dlist_parse(struct dlist **dlp, int parsekey, struct protstream *in, const char *alt_reserve_base) { struct dlist *dl = NULL; static struct buf kbuf; static struct buf vbuf; int c; /* handle the key if wanted */ if (parsekey) { c = getastring(in, NULL, &kbuf); c = next_nonspace(in, c); } else { buf_setcstr(&kbuf, ""); c = prot_getc(in); } /* connection dropped? */ if (c == EOF) goto fail; /* check what sort of value we have */ if (c == '(') { dl = dlist_newlist(NULL, kbuf.s); c = next_nonspace(in, ' '); while (c != ')') { struct dlist *di = NULL; prot_ungetc(c, in); c = dlist_parse(&di, 0, in, alt_reserve_base); if (di) dlist_stitch(dl, di); c = next_nonspace(in, c); if (c == EOF) goto fail; } c = prot_getc(in); } else if (c == '%') { /* no whitespace allowed here */ c = prot_getc(in); if (c == '(') { dl = dlist_newkvlist(NULL, kbuf.s); c = next_nonspace(in, ' '); while (c != ')') { struct dlist *di = NULL; prot_ungetc(c, in); c = dlist_parse(&di, 1, in, alt_reserve_base); if (di) dlist_stitch(dl, di); c = next_nonspace(in, c); if (c == EOF) goto fail; } } else if (c == '{') { struct message_guid tmp_guid; static struct buf pbuf, gbuf; unsigned size = 0; const char *fname; const char *part; c = getastring(in, NULL, &pbuf); if (c != ' ') goto fail; c = getastring(in, NULL, &gbuf); if (c != ' ') goto fail; c = getuint32(in, &size); if (c != '}') goto fail; c = prot_getc(in); if (c == '\r') c = prot_getc(in); if (c != '\n') goto fail; if (!message_guid_decode(&tmp_guid, gbuf.s)) goto fail; part = alt_reserve_base ? alt_reserve_base : pbuf.s; if (reservefile(in, part, &tmp_guid, size, &fname)) goto fail; dl = dlist_setfile(NULL, kbuf.s, pbuf.s, &tmp_guid, size, fname); /* file literal */ } else { /* unknown percent type */ goto fail; } c = prot_getc(in); } else if (c == '{') { prot_ungetc(c, in); /* could be binary in a literal */ c = getbastring(in, NULL, &vbuf); dl = dlist_setmap(NULL, kbuf.s, vbuf.s, vbuf.len); } else if (c == '\\') { /* special case for flags */ prot_ungetc(c, in); c = getastring(in, NULL, &vbuf); dl = dlist_setflag(NULL, kbuf.s, vbuf.s); } else { prot_ungetc(c, in); c = getnastring(in, NULL, &vbuf); dl = dlist_setatom(NULL, kbuf.s, vbuf.s); } /* success */ *dlp = dl; return c; fail: dlist_free(&dl); return EOF; }
char *find_free_server(void) { const char *servers = config_getstring(IMAPOPT_SERVERLIST); unsigned long max_avail = 0; char *server = NULL; if (servers) { char *tmpbuf, *cur_server, *next_server; char mytag[128]; struct backend *be; /* make a working copy of the list */ cur_server = tmpbuf = xstrdup(servers); while (cur_server) { /* eat any leading whitespace */ while (Uisspace(*cur_server)) cur_server++; if (!*cur_server) break; /* find end of server */ if ((next_server = strchr(cur_server, ' ')) || (next_server = strchr(cur_server, '\t'))) *next_server++ = '\0'; syslog(LOG_DEBUG, "checking free space on server '%s'", cur_server); /* connect to server */ be = proxy_findserver(cur_server, &imap_protocol, proxy_userid, &backend_cached, &backend_current, &backend_inbox, imapd_in); if (be) { unsigned avail = 0; int c; /* fetch annotation from remote */ proxy_gentag(mytag, sizeof(mytag)); prot_printf(be->out, "%s GETANNOTATION \"\" " "\"/vendor/cmu/cyrus-imapd/freespace\" " "\"value.shared\"\r\n", mytag); prot_flush(be->out); for (/* each annotation response */;;) { /* read a line */ c = prot_getc(be->in); if (c != '*') break; c = prot_getc(be->in); if (c != ' ') { /* protocol error */ c = EOF; break; } c = chomp(be->in, "ANNOTATION \"\" " "\"/vendor/cmu/cyrus-imapd/freespace\" " "(\"value.shared\" \""); if (c == EOF) { /* we don't care about this response */ eatline(be->in, c); continue; } /* read uidvalidity */ c = getuint32(be->in, &avail); if (c != '\"') { c = EOF; break; } eatline(be->in, c); /* we don't care about the rest of the line */ } if (c != EOF) { prot_ungetc(c, be->in); /* we should be looking at the tag now */ eatline(be->in, c); } if (c == EOF) { /* uh oh, we're not happy */ fatal("Lost connection to backend", EC_UNAVAILABLE); } if (avail > max_avail) { server = cur_server; max_avail = avail; } } /* move to next server */ cur_server = next_server; } if (server) server = xstrdup(server); free(tmpbuf); } return server; }