static void msg_vstream_print(int level, const char *text) { static const char *level_text[] = { "info", "warning", "error", "fatal", "panic", }; if (level < 0 || level >= (int) (sizeof(level_text) / sizeof(level_text[0]))) msg_panic("invalid severity level: %d", level); if (level == MSG_INFO) { vstream_fprintf(msg_stream, "%s: %s\n", msg_tag, text); } else { vstream_fprintf(msg_stream, "%s: %s: %s\n", msg_tag, level_text[level], text); } vstream_fflush(msg_stream); }
void netstring_put(VSTREAM *stream, const char *data, ssize_t len) { const char *myname = "netstring_put"; if (msg_verbose > 1) msg_info("%s: write netstring len %ld data %.*s", myname, (long) len, (int) (len < 30 ? len : 30), data); vstream_fprintf(stream, "%ld:", (long) len); vstream_fwrite(stream, data, len); VSTREAM_PUTC(',', stream); }
int main(void) { VSTRING *src = vstring_alloc(100); VSTRING *dst = vstring_alloc(100); while (vstring_fgets_nonl(src, VSTREAM_IN)) { vstream_fprintf(VSTREAM_OUT, "%s\n", vstring_str(quote_821_local(dst, vstring_str(src)))); vstream_fflush(VSTREAM_OUT); } exit(0); }
main(void) { VSTRING *vp = vstring_alloc(1); VSTREAM *fp; if ((fp = vstream_fopen(TEXT_VSTREAM, O_RDONLY, 0)) == 0) msg_fatal("open %s: %m", TEXT_VSTREAM); while (vstring_fgets(vp, fp)) vstream_fprintf(VSTREAM_OUT, "%s", vstring_str(vp)); vstream_fclose(fp); vstream_fflush(VSTREAM_OUT); vstring_free(vp); }
static int flush_add_path(const char *path, const char *queue_id) { const char *myname = "flush_add_path"; VSTREAM *log; /* * Sanity check. */ if (!mail_queue_id_ok(path)) return (FLUSH_STAT_BAD); /* * Open the logfile or bust. */ if ((log = mail_queue_open(MAIL_QUEUE_FLUSH, path, O_CREAT | O_APPEND | O_WRONLY, 0600)) == 0) msg_fatal("%s: open fast flush logfile %s: %m", myname, path); /* * We must lock the logfile, so that we don't lose information due to * concurrent access. If the lock takes too long, the Postfix watchdog * will eventually take care of the problem, but it will take a while. */ if (myflock(vstream_fileno(log), INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0) msg_fatal("%s: lock fast flush logfile %s: %m", myname, path); /* * Append the queue ID. With 15 bits of microsecond time, a queue ID is * not recycled often enough for false hits to be a problem. If it does, * then we could add other signature information, such as the file size * in bytes. */ vstream_fprintf(log, "%s\n", queue_id); if (vstream_fflush(log)) msg_warn("write fast flush logfile %s: %m", path); /* * Clean up. */ if (myflock(vstream_fileno(log), INTERNAL_LOCK, MYFLOCK_OP_NONE) < 0) msg_fatal("%s: unlock fast flush logfile %s: %m", myname, path); if (vstream_fclose(log) != 0) msg_warn("write fast flush logfile %s: %m", path); return (FLUSH_STAT_OK); }
static int xsasl_dovecot_server_next(XSASL_SERVER *xp, const char *request, VSTRING *reply) { XSASL_DOVECOT_SERVER *server = (XSASL_DOVECOT_SERVER *) xp; if (!is_valid_base64(request)) { vstring_strcpy(reply, "Invalid base64 data in continued response"); return XSASL_AUTH_FAIL; } /* XXX Encapsulate for logging. */ vstream_fprintf(server->impl->sasl_stream, "CONT\t%u\t%s\n", server->last_request_id, request); if (vstream_fflush(server->impl->sasl_stream) == VSTREAM_EOF) { vstring_strcpy(reply, "Connection lost to authentication server"); return XSASL_AUTH_TEMP; } return xsasl_dovecot_handle_reply(server, reply); }
static void usage(void) { vstream_fprintf(VSTREAM_ERR, "usage: \n" " create names... create and connect\n" #if 0 " conn_macros names... define connect macros\n" " helo_macros names... define helo command macros\n" " mail_macros names... define mail command macros\n" " rcpt_macros names... define rcpt command macros\n" " data_macros names... define data command macros\n" " unk_macros names... unknown command macros\n" " message_macros names... define message macros\n" #endif " free disconnect and destroy\n" " connect name addr port family\n" " helo hostname\n" " ehlo hostname\n" " mail from sender...\n" " rcpt to recipient...\n" " data\n" " disconnect\n" " unknown command\n"); vstream_fflush(VSTREAM_ERR); }
int mail_copy(const char *sender, const char *orig_rcpt, const char *delivered, VSTREAM *src, VSTREAM *dst, int flags, const char *eol, DSN_BUF *why) { const char *myname = "mail_copy"; VSTRING *buf; char *bp; off_t orig_length; int read_error; int write_error; int corrupt_error = 0; time_t now; int type; int prev_type; struct stat st; off_t size_limit; /* * Workaround 20090114. This will hopefully get someone's attention. The * problem with file_size_limit < message_size_limit is that mail will be * delivered again and again until someone removes it from the queue by * hand, because Postfix cannot mark a recipient record as "completed". */ if (fstat(vstream_fileno(src), &st) < 0) msg_fatal("fstat: %m"); if ((size_limit = get_file_limit()) < st.st_size) msg_panic("file size limit %lu < message size %lu. This " "causes large messages to be delivered repeatedly " "after they were submitted with \"sendmail -t\" " "or after recipients were added with the Milter " "SMFIR_ADDRCPT request", (unsigned long) size_limit, (unsigned long) st.st_size); /* * Initialize. */ #ifndef NO_TRUNCATE if ((flags & MAIL_COPY_TOFILE) != 0) if ((orig_length = vstream_fseek(dst, (off_t) 0, SEEK_END)) < 0) msg_fatal("seek file %s: %m", VSTREAM_PATH(dst)); #endif buf = vstring_alloc(100); /* * Prepend a bunch of headers to the message. */ if (flags & (MAIL_COPY_FROM | MAIL_COPY_RETURN_PATH)) { if (sender == 0) msg_panic("%s: null sender", myname); quote_822_local(buf, sender); if (flags & MAIL_COPY_FROM) { time(&now); vstream_fprintf(dst, "From %s %.24s%s", *sender == 0 ? MAIL_ADDR_MAIL_DAEMON : vstring_str(buf), asctime(localtime(&now)), eol); } if (flags & MAIL_COPY_RETURN_PATH) { vstream_fprintf(dst, "Return-Path: <%s>%s", *sender ? vstring_str(buf) : "", eol); } } if (flags & MAIL_COPY_ORIG_RCPT) { if (orig_rcpt == 0) msg_panic("%s: null orig_rcpt", myname); /* * An empty original recipient record almost certainly means that * original recipient processing was disabled. */ if (*orig_rcpt) { quote_822_local(buf, orig_rcpt); vstream_fprintf(dst, "X-Original-To: %s%s", vstring_str(buf), eol); } } if (flags & MAIL_COPY_DELIVERED) { if (delivered == 0) msg_panic("%s: null delivered", myname); quote_822_local(buf, delivered); vstream_fprintf(dst, "Delivered-To: %s%s", vstring_str(buf), eol); } /* * Copy the message. Escape lines that could be confused with the ugly * From_ line. Make sure that there is a blank line at the end of the * message so that the next ugly From_ can be found by mail reading * software. * * XXX Rely on the front-end services to enforce record size limits. */ #define VSTREAM_FWRITE_BUF(s,b) \ vstream_fwrite((s),vstring_str(b),VSTRING_LEN(b)) prev_type = REC_TYPE_NORM; while ((type = rec_get(src, buf, 0)) > 0) { if (type != REC_TYPE_NORM && type != REC_TYPE_CONT) break; bp = vstring_str(buf); if (prev_type == REC_TYPE_NORM) { if ((flags & MAIL_COPY_QUOTE) && *bp == 'F' && !strncmp(bp, "From ", 5)) VSTREAM_PUTC('>', dst); if ((flags & MAIL_COPY_DOT) && *bp == '.') VSTREAM_PUTC('.', dst); } if (VSTRING_LEN(buf) && VSTREAM_FWRITE_BUF(dst, buf) != VSTRING_LEN(buf)) break; if (type == REC_TYPE_NORM && vstream_fputs(eol, dst) == VSTREAM_EOF) break; prev_type = type; } if (vstream_ferror(dst) == 0) { if (var_fault_inj_code == 1) type = 0; if (type != REC_TYPE_XTRA) { /* XXX Where is the queue ID? */ msg_warn("bad record type: %d in message content", type); corrupt_error = mark_corrupt(src); } if (prev_type != REC_TYPE_NORM) vstream_fputs(eol, dst); if (flags & MAIL_COPY_BLANK) vstream_fputs(eol, dst); } vstring_free(buf); /* * Make sure we read and wrote all. Truncate the file to its original * length when the delivery failed. POSIX does not require ftruncate(), * so we may have a portability problem. Note that fclose() may fail even * while fflush and fsync() succeed. Think of remote file systems such as * AFS that copy the file back to the server upon close. Oh well, no * point optimizing the error case. XXX On systems that use flock() * locking, we must truncate the file file before closing it (and losing * the exclusive lock). */ read_error = vstream_ferror(src); write_error = vstream_fflush(dst); #ifdef HAS_FSYNC if ((flags & MAIL_COPY_TOFILE) != 0) write_error |= fsync(vstream_fileno(dst)); #endif if (var_fault_inj_code == 2) { read_error = 1; errno = ENOENT; } if (var_fault_inj_code == 3) { write_error = 1; errno = ENOENT; } #ifndef NO_TRUNCATE if ((flags & MAIL_COPY_TOFILE) != 0) if (corrupt_error || read_error || write_error) /* Complain about ignored "undo" errors? So sue me. */ (void) ftruncate(vstream_fileno(dst), orig_length); #endif write_error |= vstream_fclose(dst); /* * Return the optional verbose error description. */ #define TRY_AGAIN_ERROR(errno) \ (errno == EAGAIN || errno == ESTALE) if (why && read_error) dsb_unix(why, TRY_AGAIN_ERROR(errno) ? "4.3.0" : "5.3.0", sys_exits_detail(EX_IOERR)->text, "error reading message: %m"); if (why && write_error) dsb_unix(why, mbox_dsn(errno, "5.3.0"), sys_exits_detail(EX_IOERR)->text, "error writing message: %m"); /* * Use flag+errno description when the optional verbose description is * not desired. */ return ((corrupt_error ? MAIL_COPY_STAT_CORRUPT : 0) | (read_error ? MAIL_COPY_STAT_READ : 0) | (write_error ? MAIL_COPY_STAT_WRITE : 0)); }
void edit_parameters(int mode, int argc, char **argv) { char *path; EDIT_FILE *ep; VSTREAM *src; VSTREAM *dst; VSTRING *buf = vstring_alloc(100); VSTRING *key = vstring_alloc(10); char *cp; char *edit_key; char *edit_val; HTABLE *table; struct cvalue { char *value; int found; }; struct cvalue *cvalue; HTABLE_INFO **ht_info; HTABLE_INFO **ht; int interesting; const char *err; /* * Store command-line parameters for quick lookup. */ table = htable_create(argc); while ((cp = *argv++) != 0) { if (strchr(cp, '\n') != 0) msg_fatal("-e or -# accepts no multi-line input"); while (ISSPACE(*cp)) cp++; if (*cp == '#') msg_fatal("-e or -# accepts no comment input"); if (mode & EDIT_MAIN) { if ((err = split_nameval(cp, &edit_key, &edit_val)) != 0) msg_fatal("%s: \"%s\"", err, cp); } else if (mode & COMMENT_OUT) { if (*cp == 0) msg_fatal("-# requires non-blank parameter names"); if (strchr(cp, '=') != 0) msg_fatal("-# requires parameter names only"); edit_key = mystrdup(cp); trimblanks(edit_key, 0); edit_val = 0; } else { msg_panic("edit_parameters: unknown mode %d", mode); } cvalue = (struct cvalue *) mymalloc(sizeof(*cvalue)); cvalue->value = edit_val; cvalue->found = 0; htable_enter(table, edit_key, (char *) cvalue); } /* * Open a temp file for the result. This uses a deterministic name so we * don't leave behind thrash with random names. */ set_config_dir(); path = concatenate(var_config_dir, "/", MAIN_CONF_FILE, (char *) 0); if ((ep = edit_file_open(path, O_CREAT | O_WRONLY, 0644)) == 0) msg_fatal("open %s%s: %m", path, EDIT_FILE_SUFFIX); dst = ep->tmp_fp; /* * Open the original file for input. */ if ((src = vstream_fopen(path, O_RDONLY, 0)) == 0) { /* OK to delete, since we control the temp file name exclusively. */ (void) unlink(ep->tmp_path); msg_fatal("open %s for reading: %m", path); } /* * Copy original file to temp file, while replacing parameters on the * fly. Issue warnings for names found multiple times. */ #define STR(x) vstring_str(x) interesting = 0; while (vstring_get(buf, src) != VSTREAM_EOF) { for (cp = STR(buf); ISSPACE(*cp) /* including newline */ ; cp++) /* void */ ; /* Copy comment, all-whitespace, or empty line. */ if (*cp == '#' || *cp == 0) { vstream_fputs(STR(buf), dst); } /* Copy, skip or replace continued text. */ else if (cp > STR(buf)) { if (interesting == 0) vstream_fputs(STR(buf), dst); else if (mode & COMMENT_OUT) vstream_fprintf(dst, "#%s", STR(buf)); } /* Copy or replace start of logical line. */ else { vstring_strncpy(key, cp, strcspn(cp, " \t\r\n=")); cvalue = (struct cvalue *) htable_find(table, STR(key)); if ((interesting = !!cvalue) != 0) { if (cvalue->found++ == 1) msg_warn("%s: multiple entries for \"%s\"", path, STR(key)); if (mode & EDIT_MAIN) vstream_fprintf(dst, "%s = %s\n", STR(key), cvalue->value); else if (mode & COMMENT_OUT) vstream_fprintf(dst, "#%s", cp); else msg_panic("edit_parameters: unknown mode %d", mode); } else { vstream_fputs(STR(buf), dst); } } } /* * Generate new entries for parameters that were not found. */ if (mode & EDIT_MAIN) { for (ht_info = ht = htable_list(table); *ht; ht++) { cvalue = (struct cvalue *) ht[0]->value; if (cvalue->found == 0) vstream_fprintf(dst, "%s = %s\n", ht[0]->key, cvalue->value); } myfree((char *) ht_info); } /* * When all is well, rename the temp file to the original one. */ if (vstream_fclose(src)) msg_fatal("read %s: %m", path); if (edit_file_close(ep) != 0) msg_fatal("close %s%s: %m", path, EDIT_FILE_SUFFIX); /* * Cleanup. */ myfree(path); vstring_free(buf); vstring_free(key); htable_free(table, myfree); }
int main(int argc, char **argv) { static VSTREAM *lock_fp; static VSTREAM *data_lock_fp; VSTRING *lock_path; VSTRING *data_lock_path; off_t inherited_limit; int debug_me = 0; int ch; int fd; int n; int test_lock = 0; VSTRING *why; WATCHDOG *watchdog; ARGV *import_env; /* * Fingerprint executables and core dumps. */ MAIL_VERSION_STAMP_ALLOCATE; /* * Initialize. */ umask(077); /* never fails! */ /* * Process environment options as early as we can. */ if (getenv(CONF_ENV_VERB)) msg_verbose = 1; if (getenv(CONF_ENV_DEBUG)) debug_me = 1; /* * Don't die when a process goes away unexpectedly. */ signal(SIGPIPE, SIG_IGN); /* * Strip and save the process name for diagnostics etc. */ var_procname = mystrdup(basename(argv[0])); /* * When running a child process, don't leak any open files that were * leaked to us by our own (privileged) parent process. Descriptors 0-2 * are taken care of after we have initialized error logging. * * Some systems such as AIX have a huge per-process open file limit. In * those cases, limit the search for potential file descriptor leaks to * just the first couple hundred. * * The Debian post-installation script passes an open file descriptor into * the master process and waits forever for someone to close it. Because * of this we have to close descriptors > 2, and pray that doing so does * not break things. */ closefrom(3); /* * Initialize logging and exit handler. */ msg_syslog_init(mail_task(var_procname), LOG_PID, LOG_FACILITY); /* * Check the Postfix library version as soon as we enable logging. */ MAIL_VERSION_CHECK; /* * The mail system must be run by the superuser so it can revoke * privileges for selected operations. That's right - it takes privileges * to toss privileges. */ if (getuid() != 0) msg_fatal("the master command is reserved for the superuser"); if (unsafe() != 0) msg_fatal("the master command must not run as a set-uid process"); /* * Process JCL. */ while ((ch = GETOPT(argc, argv, "c:Dde:tv")) > 0) { switch (ch) { case 'c': if (setenv(CONF_ENV_PATH, optarg, 1) < 0) msg_fatal("out of memory"); break; case 'd': master_detach = 0; break; case 'e': event_request_timer(master_exit_event, (char *) 0, atoi(optarg)); break; case 'D': debug_me = 1; break; case 't': test_lock = 1; break; case 'v': msg_verbose++; break; default: usage(argv[0]); /* NOTREACHED */ } } /* * This program takes no other arguments. */ if (argc > optind) usage(argv[0]); /* * If started from a terminal, get rid of any tty association. This also * means that all errors and warnings must go to the syslog daemon. */ if (master_detach) for (fd = 0; fd < 3; fd++) { (void) close(fd); if (open("/dev/null", O_RDWR, 0) != fd) msg_fatal("open /dev/null: %m"); } /* * Run in a separate process group, so that "postfix stop" can terminate * all MTA processes cleanly. Give up if we can't separate from our * parent process. We're not supposed to blow away the parent. */ if (debug_me == 0 && master_detach != 0 && setsid() == -1 && getsid(0) != getpid()) msg_fatal("unable to set session and process group ID: %m"); /* * Make some room for plumbing with file descriptors. XXX This breaks * when a service listens on many ports. In order to do this right we * must change the master-child interface so that descriptors do not need * to have fixed numbers. * * In a child we need two descriptors for the flow control pipe, one for * child->master status updates and at least one for listening. */ for (n = 0; n < 5; n++) { if (close_on_exec(dup(0), CLOSE_ON_EXEC) < 0) msg_fatal("dup(0): %m"); } /* * Final initializations. Unfortunately, we must read the global Postfix * configuration file after doing command-line processing, so that we get * consistent results when we SIGHUP the server to reload configuration * files. */ master_vars_init(); /* * In case of multi-protocol support. This needs to be done because * master does not invoke mail_params_init() (it was written before that * code existed). */ (void) inet_proto_init(VAR_INET_PROTOCOLS, var_inet_protocols); /* * Environment import filter, to enforce consistent behavior whether * Postfix is started by hand, or at system boot time. */ import_env = argv_split(var_import_environ, ", \t\r\n"); clean_env(import_env->argv); argv_free(import_env); if ((inherited_limit = get_file_limit()) < 0) set_file_limit(OFF_T_MAX); if (chdir(var_queue_dir)) msg_fatal("chdir %s: %m", var_queue_dir); /* * Lock down the master.pid file. In test mode, no file means that it * isn't locked. */ lock_path = vstring_alloc(10); data_lock_path = vstring_alloc(10); why = vstring_alloc(10); vstring_sprintf(lock_path, "%s/%s.pid", DEF_PID_DIR, var_procname); if (test_lock && access(vstring_str(lock_path), F_OK) < 0) exit(0); lock_fp = open_lock(vstring_str(lock_path), O_RDWR | O_CREAT, 0644, why); if (test_lock) exit(lock_fp ? 0 : 1); if (lock_fp == 0) msg_fatal("open lock file %s: %s", vstring_str(lock_path), vstring_str(why)); vstream_fprintf(lock_fp, "%*lu\n", (int) sizeof(unsigned long) * 4, (unsigned long) var_pid); if (vstream_fflush(lock_fp)) msg_fatal("cannot update lock file %s: %m", vstring_str(lock_path)); close_on_exec(vstream_fileno(lock_fp), CLOSE_ON_EXEC); /* * Lock down the Postfix-writable data directory. */ vstring_sprintf(data_lock_path, "%s/%s.lock", var_data_dir, var_procname); set_eugid(var_owner_uid, var_owner_gid); data_lock_fp = open_lock(vstring_str(data_lock_path), O_RDWR | O_CREAT, 0644, why); set_ugid(getuid(), getgid()); if (data_lock_fp == 0) msg_fatal("open lock file %s: %s", vstring_str(data_lock_path), vstring_str(why)); vstream_fprintf(data_lock_fp, "%*lu\n", (int) sizeof(unsigned long) * 4, (unsigned long) var_pid); if (vstream_fflush(data_lock_fp)) msg_fatal("cannot update lock file %s: %m", vstring_str(data_lock_path)); close_on_exec(vstream_fileno(data_lock_fp), CLOSE_ON_EXEC); /* * Clean up. */ vstring_free(why); vstring_free(lock_path); vstring_free(data_lock_path); /* * Optionally start the debugger on ourself. */ if (debug_me) debug_process(); /* * Finish initialization, last part. We must process configuration files * after processing command-line parameters, so that we get consistent * results when we SIGHUP the server to reload configuration files. */ master_config(); master_sigsetup(); master_flow_init(); msg_info("daemon started -- version %s, configuration %s", var_mail_version, var_config_dir); /* * Process events. The event handler will execute the read/write/timer * action routines. Whenever something has happened, see if we received * any signal in the mean time. Although the master process appears to do * multiple things at the same time, it really is all a single thread, so * that there are no concurrency conflicts within the master process. */ #define MASTER_WATCHDOG_TIME 1000 watchdog = watchdog_create(MASTER_WATCHDOG_TIME, (WATCHDOG_FN) 0, (char *) 0); for (;;) { #ifdef HAS_VOLATILE_LOCKS if (myflock(vstream_fileno(lock_fp), INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0) msg_fatal("refresh exclusive lock: %m"); if (myflock(vstream_fileno(data_lock_fp), INTERNAL_LOCK, MYFLOCK_OP_EXCLUSIVE) < 0) msg_fatal("refresh exclusive lock: %m"); #endif watchdog_start(watchdog); /* same as trigger servers */ event_loop(MASTER_WATCHDOG_TIME / 2); if (master_gotsighup) { msg_info("reload -- version %s, configuration %s", var_mail_version, var_config_dir); master_gotsighup = 0; /* this first */ master_vars_init(); /* then this */ master_refresh(); /* then this */ } if (master_gotsigchld) { if (msg_verbose) msg_info("got sigchld"); master_gotsigchld = 0; /* this first */ master_reap_child(); /* then this */ } } }
static const char *dict_tcp_lookup(DICT *dict, const char *key) { DICT_TCP *dict_tcp = (DICT_TCP *) dict; const char *myname = "dict_tcp_lookup"; int tries; char *start; int last_ch; #define RETURN(errval, result) { dict_errno = errval; return (result); } if (msg_verbose) msg_info("%s: key %s", myname, key); /* * Optionally fold the key. */ if (dict->flags & DICT_FLAG_FOLD_MUL) { if (dict->fold_buf == 0) dict->fold_buf = vstring_alloc(10); vstring_strcpy(dict->fold_buf, key); key = lowercase(vstring_str(dict->fold_buf)); } for (tries = 0; /* see below */ ; /* see below */ ) { /* * Connect to the server, or use an existing connection. */ if (dict_tcp->fp != 0 || dict_tcp_connect(dict_tcp) == 0) { /* * Send request and receive response. Both are %XX quoted and * both are terminated by newline. This encoding is convenient * for data that is mostly text. */ hex_quote(dict_tcp->hex_buf, key); vstream_fprintf(dict_tcp->fp, "get %s\n", STR(dict_tcp->hex_buf)); if (msg_verbose) msg_info("%s: send: get %s", myname, STR(dict_tcp->hex_buf)); last_ch = vstring_get_nonl_bound(dict_tcp->hex_buf, dict_tcp->fp, DICT_TCP_MAXLEN); if (last_ch == '\n') break; /* * Disconnect from the server if it can't talk to us. */ if (last_ch < 0) msg_warn("read TCP map reply from %s: unexpected EOF (%m)", dict_tcp->dict.name); else msg_warn("read TCP map reply from %s: text longer than %d", dict_tcp->dict.name, DICT_TCP_MAXLEN); dict_tcp_disconnect(dict_tcp); } /* * Try to connect a limited number of times before giving up. */ if (++tries >= DICT_TCP_MAXTRY) RETURN(DICT_ERR_RETRY, 0); /* * Sleep between attempts, instead of hammering the server. */ sleep(1); } if (msg_verbose) msg_info("%s: recv: %s", myname, STR(dict_tcp->hex_buf)); /* * Check the general reply syntax. If the reply is malformed, disconnect * and try again later. */ if (start = STR(dict_tcp->hex_buf), !ISDIGIT(start[0]) || !ISDIGIT(start[1]) || !ISDIGIT(start[2]) || !ISSPACE(start[3]) || !hex_unquote(dict_tcp->raw_buf, start + 4)) { msg_warn("read TCP map reply from %s: malformed reply: %.100s", dict_tcp->dict.name, printable(STR(dict_tcp->hex_buf), '_')); dict_tcp_disconnect(dict_tcp); RETURN(DICT_ERR_RETRY, 0); } /* * Examine the reply status code. If the reply is malformed, disconnect * and try again later. */ switch (start[0]) { default: msg_warn("read TCP map reply from %s: bad status code: %.100s", dict_tcp->dict.name, printable(STR(dict_tcp->hex_buf), '_')); dict_tcp_disconnect(dict_tcp); RETURN(DICT_ERR_RETRY, 0); case '4': if (msg_verbose) msg_info("%s: soft error: %s", myname, printable(STR(dict_tcp->hex_buf), '_')); dict_tcp_disconnect(dict_tcp); RETURN(DICT_ERR_RETRY, 0); case '5': if (msg_verbose) msg_info("%s: not found: %s", myname, printable(STR(dict_tcp->hex_buf), '_')); RETURN(DICT_ERR_NONE, 0); case '2': if (msg_verbose) msg_info("%s: found: %s", myname, printable(STR(dict_tcp->raw_buf), '_')); RETURN(DICT_ERR_NONE, STR(dict_tcp->raw_buf)); } }
int bounce_append_service(int unused_flags, char *service, char *queue_id, RECIPIENT *rcpt, DSN *dsn) { VSTRING *in_buf = vstring_alloc(100); VSTREAM *log; long orig_length; /* * This code is paranoid for a good reason. Once the bounce service takes * responsibility, the mail system will make no further attempts to * deliver this recipient. Whenever file access fails, assume that the * system is under stress or that something has been mis-configured, and * force a backoff by raising a fatal run-time error. */ log = mail_queue_open(service, queue_id, O_WRONLY | O_APPEND | O_CREAT, 0600); if (log == 0) msg_fatal("open file %s %s: %m", service, queue_id); /* * Lock out other processes to avoid truncating someone else's data in * case of trouble. */ if (deliver_flock(vstream_fileno(log), INTERNAL_LOCK, (VSTRING *) 0) < 0) msg_fatal("lock file %s %s: %m", service, queue_id); /* * Now, go for it. Append a record. Truncate the log to the original * length when the append operation fails. We use the plain stream-lf * file format because we do not need anything more complicated. As a * benefit, we can still recover some data when the file is a little * garbled. * * XXX addresses in defer logfiles are in printable quoted form, while * addresses in message envelope records are in raw unquoted form. This * may change once we replace the present ad-hoc bounce/defer logfile * format by one that is transparent for control etc. characters. See * also: showq/showq.c. * * While migrating from old format to new format, allow backwards * compatibility by writing an old-style record before the new-style * records. */ if ((orig_length = vstream_fseek(log, 0L, SEEK_END)) < 0) msg_fatal("seek file %s %s: %m", service, queue_id); #define NOT_NULL_EMPTY(s) ((s) != 0 && *(s) != 0) #define STR(x) vstring_str(x) vstream_fputs("\n", log); if (var_oldlog_compat) { vstream_fprintf(log, "<%s>: %s\n", *rcpt->address == 0 ? "" : STR(quote_822_local(in_buf, rcpt->address)), dsn->reason); } vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_RECIP, *rcpt->address ? STR(quote_822_local(in_buf, rcpt->address)) : "<>"); if (NOT_NULL_EMPTY(rcpt->orig_addr) && strcasecmp(rcpt->address, rcpt->orig_addr) != 0) vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_ORCPT, STR(quote_822_local(in_buf, rcpt->orig_addr))); if (rcpt->offset > 0) vstream_fprintf(log, "%s=%ld\n", MAIL_ATTR_OFFSET, rcpt->offset); if (NOT_NULL_EMPTY(rcpt->dsn_orcpt)) vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_ORCPT, rcpt->dsn_orcpt); if (rcpt->dsn_notify != 0) vstream_fprintf(log, "%s=%d\n", MAIL_ATTR_DSN_NOTIFY, rcpt->dsn_notify); if (NOT_NULL_EMPTY(dsn->status)) vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_STATUS, dsn->status); if (NOT_NULL_EMPTY(dsn->action)) vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_ACTION, dsn->action); if (NOT_NULL_EMPTY(dsn->dtype) && NOT_NULL_EMPTY(dsn->dtext)) { vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_DTYPE, dsn->dtype); vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_DTEXT, dsn->dtext); } if (NOT_NULL_EMPTY(dsn->mtype) && NOT_NULL_EMPTY(dsn->mname)) { vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_MTYPE, dsn->mtype); vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_DSN_MNAME, dsn->mname); } if (NOT_NULL_EMPTY(dsn->reason)) vstream_fprintf(log, "%s=%s\n", MAIL_ATTR_WHY, dsn->reason); vstream_fputs("\n", log); if (vstream_fflush(log) != 0 || fsync(vstream_fileno(log)) < 0) { #ifndef NO_TRUNCATE if (ftruncate(vstream_fileno(log), (off_t) orig_length) < 0) msg_fatal("truncate file %s %s: %m", service, queue_id); #endif msg_fatal("append file %s %s: %m", service, queue_id); } /* * Darn. If closing the log detects a problem, the only way to undo the * damage is to open the log once more, and to truncate the log to the * original length. But, this could happen only when the log is kept on a * remote file system, and that is not recommended practice anyway. */ if (vstream_fclose(log) != 0) msg_warn("append file %s %s: %m", service, queue_id); vstring_free(in_buf); return (0); }
int attr_vprint_plain(VSTREAM *fp, int flags, va_list ap) { const char *myname = "attr_print_plain"; int attr_type; char *attr_name; unsigned int_val; unsigned long long_val; char *str_val; HTABLE_INFO **ht_info_list; HTABLE_INFO **ht; static VSTRING *base64_buf; ssize_t len_val; ATTR_PRINT_SLAVE_FN print_fn; void *print_arg; /* * Sanity check. */ if (flags & ~ATTR_FLAG_ALL) msg_panic("%s: bad flags: 0x%x", myname, flags); /* * Iterate over all (type, name, value) triples, and produce output on * the fly. */ while ((attr_type = va_arg(ap, int)) != ATTR_TYPE_END) { switch (attr_type) { case ATTR_TYPE_INT: attr_name = va_arg(ap, char *); int_val = va_arg(ap, int); vstream_fprintf(fp, "%s=%u\n", attr_name, (unsigned) int_val); if (msg_verbose) msg_info("send attr %s = %u", attr_name, (unsigned) int_val); break; case ATTR_TYPE_LONG: attr_name = va_arg(ap, char *); long_val = va_arg(ap, long); vstream_fprintf(fp, "%s=%lu\n", attr_name, long_val); if (msg_verbose) msg_info("send attr %s = %lu", attr_name, long_val); break; case ATTR_TYPE_STR: attr_name = va_arg(ap, char *); str_val = va_arg(ap, char *); vstream_fprintf(fp, "%s=%s\n", attr_name, str_val); if (msg_verbose) msg_info("send attr %s = %s", attr_name, str_val); break; case ATTR_TYPE_DATA: attr_name = va_arg(ap, char *); len_val = va_arg(ap, ssize_t); str_val = va_arg(ap, char *); if (base64_buf == 0) base64_buf = vstring_alloc(10); base64_encode(base64_buf, str_val, len_val); vstream_fprintf(fp, "%s=%s\n", attr_name, STR(base64_buf)); if (msg_verbose) msg_info("send attr %s = [data %ld bytes]", attr_name, (long) len_val); break; case ATTR_TYPE_FUNC: print_fn = va_arg(ap, ATTR_PRINT_SLAVE_FN); print_arg = va_arg(ap, void *); print_fn(attr_print_plain, fp, flags | ATTR_FLAG_MORE, print_arg); break; case ATTR_TYPE_HASH: ht_info_list = htable_list(va_arg(ap, HTABLE *)); for (ht = ht_info_list; *ht; ht++) { vstream_fprintf(fp, "%s=%s\n", ht[0]->key, ht[0]->value); if (msg_verbose) msg_info("send attr name %s value %s", ht[0]->key, ht[0]->value); } myfree((char *) ht_info_list); break; default: msg_panic("%s: unknown type code: %d", myname, attr_type); } } if ((flags & ATTR_FLAG_MORE) == 0) VSTREAM_PUTC('\n', fp); return (vstream_ferror(fp)); }
int xsasl_dovecot_server_first(XSASL_SERVER *xp, const char *sasl_method, const char *init_response, VSTRING *reply) { const char *myname = "xsasl_dovecot_server_first"; XSASL_DOVECOT_SERVER *server = (XSASL_DOVECOT_SERVER *) xp; int i; char **cpp; #define IFELSE(e1,e2,e3) ((e1) ? (e2) : (e3)) if (msg_verbose) msg_info("%s: sasl_method %s%s%s", myname, sasl_method, IFELSE(init_response, ", init_response ", ""), IFELSE(init_response, init_response, "")); if (server->mechanism_argv == 0) msg_panic("%s: no mechanism list", myname); for (cpp = server->mechanism_argv->argv; /* see below */ ; cpp++) { if (*cpp == 0) { vstring_strcpy(reply, "Invalid authentication mechanism"); return XSASL_AUTH_FAIL; } if (strcasecmp(sasl_method, *cpp) == 0) break; } if (init_response) if (!is_valid_base64(init_response)) { vstring_strcpy(reply, "Invalid base64 data in initial response"); return XSASL_AUTH_FAIL; } for (i = 0; i < 2; i++) { if (!server->impl->sasl_stream) { if (xsasl_dovecot_server_connect(server->impl) < 0) return XSASL_AUTH_TEMP; } /* send the request */ server->last_request_id = ++server->impl->request_id_counter; /* XXX Encapsulate for logging. */ vstream_fprintf(server->impl->sasl_stream, "AUTH\t%u\t%s\tservice=%s\tnologin\tlip=%s\trip=%s", server->last_request_id, sasl_method, server->service, server->server_addr, server->client_addr); if (server->tls_flag) /* XXX Encapsulate for logging. */ vstream_fputs("\tsecured", server->impl->sasl_stream); if (init_response) { /* * initial response is already base64 encoded, so we can send it * directly. */ /* XXX Encapsulate for logging. */ vstream_fprintf(server->impl->sasl_stream, "\tresp=%s", init_response); } /* XXX Encapsulate for logging. */ VSTREAM_PUTC('\n', server->impl->sasl_stream); if (vstream_fflush(server->impl->sasl_stream) != VSTREAM_EOF) break; if (i == 1) { vstring_strcpy(reply, "Can't connect to authentication server"); return XSASL_AUTH_TEMP; } /* * Reconnect and try again. */ xsasl_dovecot_server_disconnect(server->impl); } return xsasl_dovecot_handle_reply(server, reply); }
static int xsasl_dovecot_server_connect(XSASL_DOVECOT_SERVER_IMPL *xp) { const char *myname = "xsasl_dovecot_server_connect"; VSTRING *line_str; VSTREAM *sasl_stream; char *line, *cmd, *mech_name; unsigned int major_version, minor_version; int fd, success, have_mech_line; int sec_props; const char *path; if (msg_verbose) msg_info("%s: Connecting", myname); /* * Not documented, but necessary for testing. */ path = xp->socket_path; if (strncmp(path, "inet:", 5) == 0) { fd = inet_connect(path + 5, BLOCKING, AUTH_TIMEOUT); } else { if (strncmp(path, "unix:", 5) == 0) path += 5; fd = unix_connect(path, BLOCKING, AUTH_TIMEOUT); } if (fd < 0) { msg_warn("SASL: Connect to %s failed: %m", xp->socket_path); return (-1); } sasl_stream = vstream_fdopen(fd, O_RDWR); vstream_control(sasl_stream, CA_VSTREAM_CTL_PATH(xp->socket_path), CA_VSTREAM_CTL_TIMEOUT(AUTH_TIMEOUT), CA_VSTREAM_CTL_END); /* XXX Encapsulate for logging. */ vstream_fprintf(sasl_stream, "VERSION\t%u\t%u\n" "CPID\t%u\n", AUTH_PROTOCOL_MAJOR_VERSION, AUTH_PROTOCOL_MINOR_VERSION, (unsigned int) getpid()); if (vstream_fflush(sasl_stream) == VSTREAM_EOF) { msg_warn("SASL: Couldn't send handshake: %m"); return (-1); } success = 0; have_mech_line = 0; line_str = vstring_alloc(256); /* XXX Encapsulate for logging. */ while (vstring_get_nonl(line_str, sasl_stream) != VSTREAM_EOF) { line = vstring_str(line_str); if (msg_verbose) msg_info("%s: auth reply: %s", myname, line); cmd = line; line = split_at(line, '\t'); if (strcmp(cmd, "VERSION") == 0) { if (sscanf(line, "%u\t%u", &major_version, &minor_version) != 2) { msg_warn("SASL: Protocol version error"); break; } if (major_version != AUTH_PROTOCOL_MAJOR_VERSION) { /* Major version is different from ours. */ msg_warn("SASL: Protocol version mismatch (%d vs. %d)", major_version, AUTH_PROTOCOL_MAJOR_VERSION); break; } } else if (strcmp(cmd, "MECH") == 0 && line != NULL) { mech_name = line; have_mech_line = 1; line = split_at(line, '\t'); if (line != 0) { sec_props = name_mask_delim_opt(myname, xsasl_dovecot_serv_sec_props, line, "\t", NAME_MASK_ANY_CASE | NAME_MASK_IGNORE); if ((sec_props & SEC_PROPS_PRIVATE) != 0) continue; } else sec_props = 0; xsasl_dovecot_server_mech_append(&xp->mechanism_list, mech_name, sec_props); } else if (strcmp(cmd, "SPID") == 0) { /* * Unfortunately the auth protocol handshake wasn't designed well * to differentiate between auth-client/userdb/master. * auth-userdb and auth-master send VERSION + SPID lines only and * nothing afterwards, while auth-client sends VERSION + MECH + * SPID + CUID + more. The simplest way that we can determine if * we've connected to the correct socket is to see if MECH line * exists or not (alternatively we'd have to have a small timeout * after SPID to see if CUID is sent or not). */ if (!have_mech_line) { msg_warn("SASL: Connected to wrong auth socket (auth-master instead of auth-client)"); break; } } else if (strcmp(cmd, "DONE") == 0) { /* Handshake finished. */ success = 1; break; } else { /* ignore any unknown commands */ } } vstring_free(line_str); if (!success) { /* handshake failed */ (void) vstream_fclose(sasl_stream); return (-1); } xp->sasl_stream = sasl_stream; return (0); }
void pcf_edit_master(int mode, int argc, char **argv) { const char *myname = "pcf_edit_master"; char *path; EDIT_FILE *ep; VSTREAM *src; VSTREAM *dst; VSTRING *line_buf = vstring_alloc(100); VSTRING *parse_buf = vstring_alloc(100); int lineno; PCF_MASTER_ENT *new_entry; VSTRING *full_entry_buf = vstring_alloc(100); char *cp; char *pattern; int service_name_type_matched; const char *err; PCF_MASTER_EDIT_REQ *edit_reqs; PCF_MASTER_EDIT_REQ *req; int num_reqs = argc; const char *edit_opts = "-Me, -Fe, -Pe, -X, or -#"; char *service_name; char *service_type; /* * Sanity check. */ if (num_reqs <= 0) msg_panic("%s: empty argument list", myname); /* * Preprocessing: split pattern=value, then split the pattern components. */ edit_reqs = (PCF_MASTER_EDIT_REQ *) mymalloc(sizeof(*edit_reqs) * num_reqs); for (req = edit_reqs; *argv != 0; req++, argv++) { req->match_count = 0; req->raw_text = *argv; cp = req->parsed_text = mystrdup(req->raw_text); if (strchr(cp, '\n') != 0) msg_fatal("%s accept no multi-line input", edit_opts); while (ISSPACE(*cp)) cp++; if (*cp == '#') msg_fatal("%s accept no comment input", edit_opts); /* Separate the pattern from the value. */ if (mode & PCF_EDIT_CONF) { if ((err = split_nameval(cp, &pattern, &req->edit_value)) != 0) msg_fatal("%s: \"%s\"", err, req->raw_text); if ((mode & PCF_MASTER_PARAM) && req->edit_value[strcspn(req->edit_value, PCF_MASTER_BLANKS)]) msg_fatal("whitespace in parameter value: \"%s\"", req->raw_text); } else if (mode & (PCF_COMMENT_OUT | PCF_EDIT_EXCL)) { if (strchr(cp, '=') != 0) msg_fatal("-X or -# requires names without value"); pattern = cp; trimblanks(pattern, 0); req->edit_value = 0; } else { msg_panic("%s: unknown mode %d", myname, mode); } #define PCF_MASTER_MASK (PCF_MASTER_ENTRY | PCF_MASTER_FLD | PCF_MASTER_PARAM) /* * Split name/type or name/type/whatever pattern into components. */ switch (mode & PCF_MASTER_MASK) { case PCF_MASTER_ENTRY: if ((req->service_pattern = pcf_parse_service_pattern(pattern, 2, 2)) == 0) msg_fatal("-Me, -MX or -M# requires service_name/type"); break; case PCF_MASTER_FLD: if ((req->service_pattern = pcf_parse_service_pattern(pattern, 3, 3)) == 0) msg_fatal("-Fe or -FX requires service_name/type/field_name"); req->field_number = pcf_parse_field_pattern(req->service_pattern->argv[2]); if (pcf_is_magic_field_pattern(req->field_number)) msg_fatal("-Fe does not accept wild-card field name"); if ((mode & PCF_EDIT_CONF) && req->field_number < PCF_MASTER_FLD_CMD && req->edit_value[strcspn(req->edit_value, PCF_MASTER_BLANKS)]) msg_fatal("-Fe does not accept whitespace in non-command field"); break; case PCF_MASTER_PARAM: if ((req->service_pattern = pcf_parse_service_pattern(pattern, 3, 3)) == 0) msg_fatal("-Pe or -PX requires service_name/type/parameter"); req->param_pattern = req->service_pattern->argv[2]; if (PCF_IS_MAGIC_PARAM_PATTERN(req->param_pattern)) msg_fatal("-Pe does not accept wild-card parameter name"); if ((mode & PCF_EDIT_CONF) && req->edit_value[strcspn(req->edit_value, PCF_MASTER_BLANKS)]) msg_fatal("-Pe does not accept whitespace in parameter value"); break; default: msg_panic("%s: unknown edit mode %d", myname, mode); } } /* * Open a temp file for the result. This uses a deterministic name so we * don't leave behind thrash with random names. */ pcf_set_config_dir(); path = concatenate(var_config_dir, "/", MASTER_CONF_FILE, (char *) 0); if ((ep = edit_file_open(path, O_CREAT | O_WRONLY, 0644)) == 0) msg_fatal("open %s%s: %m", path, EDIT_FILE_SUFFIX); dst = ep->tmp_fp; /* * Open the original file for input. */ if ((src = vstream_fopen(path, O_RDONLY, 0)) == 0) { /* OK to delete, since we control the temp file name exclusively. */ (void) unlink(ep->tmp_path); msg_fatal("open %s for reading: %m", path); } /* * Copy original file to temp file, while replacing service entries on * the fly. */ service_name_type_matched = 0; new_entry = 0; lineno = 0; while ((cp = pcf_next_cf_line(parse_buf, src, dst, &lineno)) != 0) { vstring_strcpy(line_buf, STR(parse_buf)); /* * Copy, skip or replace continued text. */ if (cp > STR(parse_buf)) { if (service_name_type_matched == 0) vstream_fputs(STR(line_buf), dst); else if (mode & PCF_COMMENT_OUT) vstream_fprintf(dst, "#%s", STR(line_buf)); } /* * Copy or replace (start of) logical line. */ else { service_name_type_matched = 0; /* * Parse out the service name and type. */ if ((service_name = mystrtok(&cp, PCF_MASTER_BLANKS)) == 0 || (service_type = mystrtok(&cp, PCF_MASTER_BLANKS)) == 0) msg_fatal("file %s: line %d: specify service name and type " "on the same line", path, lineno); if (strchr(service_name, '=')) msg_fatal("file %s: line %d: service name syntax \"%s\" is " "unsupported with %s", path, lineno, service_name, edit_opts); if (service_type[strcspn(service_type, "=/")] != 0) msg_fatal("file %s: line %d: " "service type syntax \"%s\" is unsupported with %s", path, lineno, service_type, edit_opts); /* * Match each service pattern. */ for (req = edit_reqs; req < edit_reqs + num_reqs; req++) { if (PCF_MATCH_SERVICE_PATTERN(req->service_pattern, service_name, service_type)) { service_name_type_matched = 1; /* Sticky flag */ req->match_count += 1; /* * Generate replacement master.cf entries. */ if ((mode & PCF_EDIT_CONF) || ((mode & PCF_MASTER_PARAM) && (mode & PCF_EDIT_EXCL))) { switch (mode & PCF_MASTER_MASK) { /* * Replace master.cf entry field or parameter * value. */ case PCF_MASTER_FLD: case PCF_MASTER_PARAM: if (new_entry == 0) { /* Gobble up any continuation lines. */ pcf_gobble_cf_line(full_entry_buf, line_buf, src, dst, &lineno); new_entry = (PCF_MASTER_ENT *) mymalloc(sizeof(*new_entry)); if ((err = pcf_parse_master_entry(new_entry, STR(full_entry_buf))) != 0) msg_fatal("file %s: line %d: %s", path, lineno, err); } if (mode & PCF_MASTER_FLD) { pcf_edit_master_field(new_entry, req->field_number, req->edit_value); } else { pcf_edit_master_param(new_entry, mode, req->param_pattern, req->edit_value); } break; /* * Replace entire master.cf entry. */ case PCF_MASTER_ENTRY: if (new_entry != 0) pcf_free_master_entry(new_entry); new_entry = (PCF_MASTER_ENT *) mymalloc(sizeof(*new_entry)); if ((err = pcf_parse_master_entry(new_entry, req->edit_value)) != 0) msg_fatal("%s: \"%s\"", err, req->raw_text); break; default: msg_panic("%s: unknown edit mode %d", myname, mode); } } } } /* * Pass through or replace the current input line. */ if (new_entry) { pcf_print_master_entry(dst, PCF_FOLD_LINE, new_entry); pcf_free_master_entry(new_entry); new_entry = 0; } else if (service_name_type_matched == 0) { vstream_fputs(STR(line_buf), dst); } else if (mode & PCF_COMMENT_OUT) { vstream_fprintf(dst, "#%s", STR(line_buf)); } } } /* * Postprocessing: when editing entire service entries, generate new * entries for services not found. Otherwise (editing fields or * parameters), "service not found" is a fatal error. */ for (req = edit_reqs; req < edit_reqs + num_reqs; req++) { if (req->match_count == 0) { if ((mode & PCF_MASTER_ENTRY) && (mode & PCF_EDIT_CONF)) { new_entry = (PCF_MASTER_ENT *) mymalloc(sizeof(*new_entry)); if ((err = pcf_parse_master_entry(new_entry, req->edit_value)) != 0) msg_fatal("%s: \"%s\"", err, req->raw_text); pcf_print_master_entry(dst, PCF_FOLD_LINE, new_entry); pcf_free_master_entry(new_entry); } else if ((mode & PCF_MASTER_ENTRY) == 0) { msg_warn("unmatched service_name/type: \"%s\"", req->raw_text); } } } /* * When all is well, rename the temp file to the original one. */ if (vstream_fclose(src)) msg_fatal("read %s: %m", path); if (edit_file_close(ep) != 0) msg_fatal("close %s%s: %m", path, EDIT_FILE_SUFFIX); /* * Cleanup. */ myfree(path); vstring_free(line_buf); vstring_free(parse_buf); vstring_free(full_entry_buf); for (req = edit_reqs; req < edit_reqs + num_reqs; req++) { argv_free(req->service_pattern); myfree(req->parsed_text); } myfree((char *) edit_reqs); }
static int xsasl_dovecot_server_connect(XSASL_DOVECOT_SERVER_IMPL *xp) { const char *myname = "xsasl_dovecot_server_connect"; VSTRING *line_str; VSTREAM *sasl_stream; char *line, *cmd, *mech_name; unsigned int major_version, minor_version; int fd, success; int sec_props; const char *path; if (msg_verbose) msg_info("%s: Connecting", myname); /* * Not documented, but necessary for testing. */ path = xp->socket_path; if (strncmp(path, "inet:", 5) == 0) { fd = inet_connect(path + 5, BLOCKING, AUTH_TIMEOUT); } else { if (strncmp(path, "unix:", 5) == 0) path += 5; fd = unix_connect(path, BLOCKING, AUTH_TIMEOUT); } if (fd < 0) { msg_warn("SASL: Connect to %s failed: %m", xp->socket_path); return (-1); } sasl_stream = vstream_fdopen(fd, O_RDWR); vstream_control(sasl_stream, VSTREAM_CTL_PATH, xp->socket_path, VSTREAM_CTL_TIMEOUT, AUTH_TIMEOUT, VSTREAM_CTL_END); /* XXX Encapsulate for logging. */ vstream_fprintf(sasl_stream, "VERSION\t%u\t%u\n" "CPID\t%u\n", AUTH_PROTOCOL_MAJOR_VERSION, AUTH_PROTOCOL_MINOR_VERSION, (unsigned int) getpid()); if (vstream_fflush(sasl_stream) == VSTREAM_EOF) { msg_warn("SASL: Couldn't send handshake: %m"); return (-1); } success = 0; line_str = vstring_alloc(256); /* XXX Encapsulate for logging. */ while (vstring_get_nonl(line_str, sasl_stream) != VSTREAM_EOF) { line = vstring_str(line_str); if (msg_verbose) msg_info("%s: auth reply: %s", myname, line); cmd = line; line = split_at(line, '\t'); if (strcmp(cmd, "VERSION") == 0) { if (sscanf(line, "%u\t%u", &major_version, &minor_version) != 2) { msg_warn("SASL: Protocol version error"); break; } if (major_version != AUTH_PROTOCOL_MAJOR_VERSION) { /* Major version is different from ours. */ msg_warn("SASL: Protocol version mismatch (%d vs. %d)", major_version, AUTH_PROTOCOL_MAJOR_VERSION); break; } } else if (strcmp(cmd, "MECH") == 0 && line != NULL) { mech_name = line; line = split_at(line, '\t'); if (line != 0) { sec_props = name_mask_delim_opt(myname, xsasl_dovecot_serv_sec_props, line, "\t", NAME_MASK_ANY_CASE | NAME_MASK_IGNORE); if ((sec_props & SEC_PROPS_PRIVATE) != 0) continue; } else sec_props = 0; xsasl_dovecot_server_mech_append(&xp->mechanism_list, mech_name, sec_props); } else if (strcmp(cmd, "DONE") == 0) { /* Handshake finished. */ success = 1; break; } else { /* ignore any unknown commands */ } } vstring_free(line_str); if (!success) { /* handshake failed */ (void) vstream_fclose(sasl_stream); return (-1); } xp->sasl_stream = sasl_stream; return (0); }