Exemple #1
0
/*
 * Add an entry to the deny DB.  Message 'msg' may be NULL, resulting
 * in a default message being used.  Service name 'service' may be NULL,
 * resulting in all services being blocked for the user.  The username
 * 'user' is a required argument.  Returns an IMAP error code or 0 on
 * success.
 */
EXPORTED int denydb_set(const char *user, const char *service, const char *msg)
{
    struct txn *txn = NULL;
    struct buf data = BUF_INITIALIZER;
    int r = 0;

    if (!denydb) {
	r = IMAP_INTERNAL;
	goto out;
    }

    if (!service)
	service = "*";

    if (!user || strchr(service, '\t')) {
	/* the service field may not contain a TAB, it's the field separator */
	r = IMAP_INVALID_IDENTIFIER;
	goto out;
    }

    /* compose the record */
    buf_printf(&data, "%u\t", USERDENY_VERSION);
    buf_appendcstr(&data, service);
    buf_putc(&data, '\t');
    buf_appendcstr(&data, (msg ? msg : default_message));

    /* write the record */
    do {
	r = cyrusdb_store(denydb,
			  user, strlen(user),
			  data.s, data.len,
			  &txn);
    } while (r == CYRUSDB_AGAIN);

    if (r) {
	syslog(LOG_ERR, "IOERROR: couldn't store denydb record for %s: %s",
			user, cyrusdb_strerror(r));
	r = IMAP_IOERROR;
    }

out:
    if (txn) {
	if (r) cyrusdb_abort(denydb, txn);
	else cyrusdb_commit(denydb, txn);
    }
    buf_free(&data);
    return r;
}
Exemple #2
0
static Xapian::Stopper *get_stopper()
{
    Xapian::Stopper *stopper = NULL;

    const char *swpath = config_getstring(IMAPOPT_SEARCH_STOPWORD_PATH);
    if (swpath) {
        // Set path to stopword file
        struct buf buf = BUF_INITIALIZER;
        buf_setcstr(&buf, swpath);
        // XXX doesn't play nice with WIN32 paths
        buf_appendcstr(&buf, "/english.list");

        // Open the stopword file
        errno = 0;
        std::ifstream inFile (buf_cstring(&buf));
        if (inFile.fail()) {
            syslog(LOG_ERR, "Xapian: could not open stopword file %s: %s",
                   buf_cstring(&buf), errno ? strerror(errno) : "unknown error");
            exit(1);
        }

        // Create the Xapian stopper
        stopper = new Xapian::SimpleStopper(
                std::istream_iterator<std::string>(inFile),
                std::istream_iterator<std::string>());

        // Clean up
        buf_free(&buf);
    }
    return stopper;
}
Exemple #3
0
static int send_notify_callback(sieve_interp_t *interp,
                                void *message_context,
                                void *script_context, notify_list_t *notify,
                                char *actions_string,
                                const char **errmsg)
{
    sieve_notify_context_t nc;
    struct buf out = BUF_INITIALIZER;
    int ret;

    assert(notify->isactive);

    if (!notify->method || !notify->options ||
        !notify->priority || !notify->message) {
        return SIEVE_RUN_ERROR;
    }

    nc.method = notify->method;
    nc.options = notify->options ? notify->options : NULL;
    nc.priority = notify->priority;

    if(nc.options && !strcmp(nc.method,"mailto"))
      if(!strcmp("$env-from$",*nc.options))
        interp->getenvelope(message_context, "From", &nc.options);

    build_notify_message(interp, notify->message, message_context,
                         &out);
    buf_appendcstr(&out, "\n\n");
    buf_appendcstr(&out, actions_string);

    nc.message = buf_cstring(&out);
    nc.fname = NULL;
    if (interp->getfname)
        interp->getfname(message_context, &nc.fname);

    ret = interp->notify(&nc,
                         interp->interp_context,
                         script_context,
                         message_context,
                         errmsg);

    buf_free(&out);

    return ret;
}
Exemple #4
0
/* Print the build information as flattened properties, prefixed by
 * the contents of buf. */
static void format_flat(json_t *buildinfo, struct buf *buf)
{
    const char *key;
    json_t *val;

    json_object_foreach(buildinfo, key, val) {
        buf_appendcstr(buf, key);
        if (json_typeof(val) == JSON_OBJECT) {
            buf_appendcstr(buf, ".");
            format_flat(val, buf);
            buf_truncate(buf, -1);
        } else {
            char *jval = json_dumps(val, JSON_ENCODE_ANY);
            buf_appendcstr(buf, "=");
            buf_appendcstr(buf, jval);
            printf("%s\n", buf_cstring(buf));
            buf_truncate(buf, -strlen(jval)-1);
            free(jval);
        }
        buf_truncate(buf, -strlen(key));
    }
Exemple #5
0
static void buf_appendjson(struct buf *buf, json_t *jvalue)
{
    switch (json_typeof(jvalue)) {
    case JSON_ARRAY: {
        size_t i, n = json_array_size(jvalue);
        const char *sep = "";

        for (i = 0; i < n; i++, sep = ",") {
            buf_appendcstr(buf, sep);
            buf_appendjson(buf, json_array_get(jvalue, i));
        }
        break;
    }

    case JSON_STRING:
        buf_appendcstr(buf, json_string_value(jvalue));
        break;

    case JSON_INTEGER:
        buf_printf(buf, "%" JSON_INTEGER_FORMAT, json_integer_value(jvalue));
        break;

    case JSON_REAL:
        buf_printf(buf, "%f", json_real_value(jvalue));
        break;

    case JSON_TRUE:
    case JSON_FALSE:
        buf_printf(buf, "%d", json_boolean_value(jvalue));
        break;

    default:
        /* Shouldn't get here - ignore object */
        break;
    }
}
Exemple #6
0
static void add_header(sieve_interp_t *i, int isenv, char *header,
                       void *message_context, struct buf *out)
{
    const char **h;
    char *decoded_header;
    /* get header value */
    if (isenv)
        i->getenvelope(message_context, header, &h);
    else
        i->getheader(message_context, header, &h);

    if (!h || !h[0])
        return;

    decoded_header = charset_parse_mimeheader(h[0]);
    buf_appendcstr(out, decoded_header);
    free(decoded_header);
}
Exemple #7
0
/*
 * Construct an iCalendar property from a XML element.
 */
static icalproperty *xml_element_to_icalproperty(xmlNodePtr xprop)
{
    const char *propname, *typestr;
    icalproperty_kind kind;
    icalproperty *prop = NULL;
    icalvalue_kind valkind;
    icalvalue *value;
    xmlNodePtr node;

    /* Get the property type */
    propname = ucase(icalmemory_tmp_copy((const char *) xprop->name));
    kind = icalenum_string_to_property_kind(propname);
    if (kind == ICAL_NO_PROPERTY) {
        syslog(LOG_WARNING, "Unknown xCal property type: %s", propname);
        return NULL;
    }

    /* Create new property */
    prop = icalproperty_new(kind);
    if (!prop) {
        syslog(LOG_ERR, "Creation of new %s property failed", propname);
        return NULL;
    }
    if (kind == ICAL_X_PROPERTY) icalproperty_set_x_name(prop, propname);


    /* Add parameters */
    node = xmlFirstElementChild(xprop);
    if (node && !xmlStrcmp(node->name, BAD_CAST "parameters")) {
        xmlNodePtr xparam;

        for (xparam = xmlFirstElementChild(node); xparam;
                xparam = xmlNextElementSibling(xparam)) {
            char *paramname =
                ucase(icalmemory_tmp_copy((const char *) xparam->name));
            xmlChar *paramval = xmlNodeGetContent(xmlFirstElementChild(xparam));

            /* XXX  Need to handle multi-valued parameters */
            icalproperty_set_parameter_from_string(prop, paramname,
                                                   (const char *) paramval);

            xmlFree(paramval);
        }

        node = xmlNextElementSibling(node);
    }

    /* Get the value type */
    if (!node) {
        syslog(LOG_WARNING, "Missing xCal value for %s property",
               propname);
        return NULL;
    }
    typestr = ucase(icalmemory_tmp_copy((const char *) node->name));
    valkind = !strcmp(typestr, "UNKNOWN") ? ICAL_X_VALUE :
              icalenum_string_to_value_kind(typestr);
    if (valkind == ICAL_NO_VALUE) {
        syslog(LOG_WARNING, "Unknown xCal value type for %s property: %s",
               propname, typestr);
        return NULL;
    }
    else if (valkind == ICAL_TEXT_VALUE) {
        /* "text" also includes enumerated types - grab type from property */
        valkind = icalproperty_kind_to_value_kind(kind);
    }


    /* Add value */
    switch (kind) {
    case ICAL_CATEGORIES_PROPERTY:
    case ICAL_RESOURCES_PROPERTY:
    case ICAL_POLLPROPERTIES_PROPERTY:
        if (valkind == ICAL_TEXT_VALUE) {
            /* Handle multi-valued properties */
            struct buf buf = BUF_INITIALIZER;
            xmlChar *content = NULL;

            content = xmlNodeGetContent(node);
            buf_setcstr(&buf, (const char *) content);
            free(content);

            while ((node = xmlNextElementSibling(node))) {
                buf_putc(&buf, ',');
                content = xmlNodeGetContent(node);
                buf_appendcstr(&buf, (const char *) content);
                free(content);
            }

            value = icalvalue_new_from_string(valkind, buf_cstring(&buf));
            buf_free(&buf);
            break;
        }

    default:
        value = xml_element_to_icalvalue(node, valkind);
        if (!value) {
            syslog(LOG_ERR, "Parsing %s property value failed", propname);
            goto error;
        }
    }

    icalproperty_set_value(prop, value);


    /* Sanity check */
    if ((node = xmlNextElementSibling(node))) {
        syslog(LOG_WARNING,
               "Unexpected XML element in property: %s", node->name);
        goto error;
    }

    return prop;

error:
    icalproperty_free(prop);
    return NULL;
}
Exemple #8
0
int main(int argc, char **argv)
{
    int opt, r = 0;
    char *alt_config = NULL, *pub = NULL, *ver = NULL, *winfile = NULL;
    char prefix[2048];
    enum { REBUILD, WINZONES, NONE } op = NONE;

    if ((geteuid()) == 0 && (become_cyrus(/*ismaster*/0) != 0)) {
	fatal("must run as the Cyrus user", EC_USAGE);
    }

    while ((opt = getopt(argc, argv, "C:r:vw:")) != EOF) {
	switch (opt) {
	case 'C': /* alt config file */
	    alt_config = optarg;
	    break;

	case 'r':
	    if (op == NONE) {
		op = REBUILD;
		pub = optarg;
		ver = strchr(optarg, ':');
		if (ver) *ver++ = '\0';
		else usage();
	    }
	    else usage();
	    break;

	case 'v':
	    verbose = 1;
	    break;

	case 'w':
	    if (op == NONE) {
		op = WINZONES;
		winfile = optarg;
	    }
	    else usage();
	    break;

	default:
	    usage();
	}
    }

    cyrus_init(alt_config, "ctl_zoneinfo", 0, 0);

    signals_set_shutdown(&shut_down);
    signals_add_handlers(0);

    snprintf(prefix, sizeof(prefix), "%s%s", config_dir, FNAME_ZONEINFODIR);

    switch (op) {
    case REBUILD: {
	struct hash_table tzentries;
	struct zoneinfo *info;
	struct txn *tid = NULL;
	char buf[1024];
	FILE *fp;

	construct_hash_table(&tzentries, 500, 1);

	/* Add INFO record (overall lastmod and TZ DB source version) */
	info = xzmalloc(sizeof(struct zoneinfo));
	info->type = ZI_INFO;
	appendstrlist(&info->data, pub);
	appendstrlist(&info->data, ver);
	hash_insert(INFO_TZID, info, &tzentries);

	/* Add LEAP record (last updated and hash) */
	snprintf(buf, sizeof(buf), "%s%s", prefix, FNAME_LEAPSECFILE);
	if (verbose) printf("Processing leap seconds file %s\n", buf);
	if (!(fp = fopen(buf, "r"))) {
	    fprintf(stderr, "Could not open leap seconds file %s\n", buf);
	}
	else {
	    struct zoneinfo *leap = xzmalloc(sizeof(struct zoneinfo));
	    leap->type = ZI_INFO;

	    while(fgets(buf, sizeof(buf), fp)) {
		if (buf[0] == '#') {
		    /* comment line */

		    if (buf[1] == '$') {
			/* last updated */
			unsigned long last;

			sscanf(buf+2, "\t%lu", &last);
			leap->dtstamp = last - NIST_EPOCH_OFFSET;
		    }
		    else if (buf[1] == 'h') {
			/* hash */
			char *p, *hash = buf+3 /* skip "#h\t" */;

			/* trim trailing whitespace */
			for (p = hash + strlen(hash); isspace(*--p); *p = '\0');
			appendstrlist(&leap->data, hash);
		    }
		}
	    }
	    fclose(fp);

	    hash_insert(LEAP_TZID, leap, &tzentries);
	    info->dtstamp = leap->dtstamp;
	}

	/* Add ZONE/LINK records */
	do_zonedir(prefix, &tzentries, info);

	zoneinfo_open(NULL);

	/* Store records */
	hash_enumerate(&tzentries, &store_zoneinfo, &tid);

	zoneinfo_close(tid);

	free_hash_table(&tzentries, &free_zoneinfo);
	break;
    }

    case WINZONES: {
	xmlParserCtxtPtr ctxt;
	xmlDocPtr doc;
	xmlNodePtr node;
	struct buf tzidbuf = BUF_INITIALIZER;
	struct buf aliasbuf = BUF_INITIALIZER;

	if (verbose) printf("Processing Windows Zone file %s\n", winfile);

	/* Parse the XML file */
	ctxt = xmlNewParserCtxt();
	if (!ctxt) {
	    fprintf(stderr, "Failed to create XML parser context\n");
	    break;
	}

	doc = xmlCtxtReadFile(ctxt, winfile, NULL, 0);
	xmlFreeParserCtxt(ctxt);
	if (!doc) {
	    fprintf(stderr, "Failed to parse XML document\n");
	    break;
	}

	node = xmlDocGetRootElement(doc);
	if (!node || xmlStrcmp(node->name, BAD_CAST "supplementalData")) {
	    fprintf(stderr, "Incorrect root node\n");
	    goto done;
	}

	for (node = xmlFirstElementChild(node);
	     node && xmlStrcmp(node->name, BAD_CAST "windowsZones");
	     node = xmlNextElementSibling(node));
	if (!node) {
	    fprintf(stderr, "Missing windowsZones node\n");
	    goto done;
	}

	node = xmlFirstElementChild(node);
	if (!node || xmlStrcmp(node->name, BAD_CAST "mapTimezones")) {
	    fprintf(stderr, "Missing mapTimezones node\n");
	    goto done;
	}

	if (chdir(prefix)) {
	    fprintf(stderr, "chdir(%s) failed\n", prefix);
	    goto done;
	}

	for (node = xmlFirstElementChild(node);
	     node;
	     node = xmlNextElementSibling(node)) {
	    if (!xmlStrcmp(node->name, BAD_CAST "mapZone") &&
		!xmlStrcmp(xmlGetProp(node, BAD_CAST "territory"),
			   BAD_CAST "001")) {
		const char *tzid, *alias;

		buf_setcstr(&tzidbuf,
			    (const char *) xmlGetProp(node, BAD_CAST "type"));
		buf_appendcstr(&tzidbuf, ".ics");
		tzid = buf_cstring(&tzidbuf);
		buf_setcstr(&aliasbuf,
			    (const char *) xmlGetProp(node, BAD_CAST "other"));
		buf_appendcstr(&aliasbuf, ".ics");
		alias = buf_cstring(&aliasbuf);

		if (verbose) printf("\tLINK: %s -> %s\n", alias, tzid);

		if (symlink(tzid, alias)) {
		    if (errno == EEXIST) {
			struct stat sbuf;

			if (stat(alias, &sbuf)) {
			    fprintf(stderr, "stat(%s) failed: %s\n",
				    alias, strerror(errno));
			    errno = EEXIST;
			}
			else if (sbuf.st_mode & S_IFLNK) {
			    char link[MAX_MAILBOX_PATH+1];
			    int n = readlink(alias, link, MAX_MAILBOX_PATH);

			    if (n == -1) {
				fprintf(stderr, "readlink(%s) failed: %s\n",
					alias, strerror(errno));
				errno = EEXIST;
			    }
			    else if (n == (int) strlen(tzid) &&
				     !strncmp(tzid, link, n)) {
				errno = 0;
			    }
			}
		    }

		    if (errno) {
			fprintf(stderr, "symlink(%s, %s) failed: %s\n",
				tzid, alias, strerror(errno));
		    }
		}
	    }
	}

  done:
	buf_free(&aliasbuf);
	buf_free(&tzidbuf);
	xmlFreeDoc(doc);
	break;
    }

    case NONE:
	r = 2;
	usage();
	break;
    }

    cyrus_done();

    return r;
}
Exemple #9
0
/* This handles piping of the LSUB command, because we have to figure out
 * what mailboxes actually exist before passing them to the end user.
 *
 * It is also needed if we are doing a FIND MAILBOXES, for that we do an
 * LSUB on the backend anyway, because the semantics of FIND do not allow
 * it to return nonexistant mailboxes (RFC1176), but we need to really dumb
 * down the response when this is the case.
 */
int pipe_lsub(struct backend *s, const char *userid, const char *tag,
              int force_notfatal, const char *resp)
{
    int taglen = strlen(tag);
    int c;
    int r = PROXY_OK;
    int exist_r;
    static struct buf tagb, cmd, sep, name;
    struct buf flags = BUF_INITIALIZER;

    const char *end_strip_flags[] = { " \\NonExistent)", "\\NonExistent)",
                                      " \\Noselect)", "\\Noselect)",
                                      NULL };
    const char *mid_strip_flags[] = { "\\NonExistent ",
                                      "\\Noselect ",
                                      NULL
                                    };

    assert(s);
    assert(s->timeout);

    s->timeout->mark = time(NULL) + IDLE_TIMEOUT;

    while(1) {
        c = getword(s->in, &tagb);

        if(c == EOF) {
            if(s == backend_current && !force_notfatal)
                fatal("Lost connection to selected backend", EC_UNAVAILABLE);
            proxy_downserver(s);
            r = PROXY_NOCONNECTION;
            goto out;
        }

        if(!strncmp(tag, tagb.s, taglen)) {
            char buf[2048];
            if(!prot_fgets(buf, sizeof(buf), s->in)) {
                if(s == backend_current && !force_notfatal)
                    fatal("Lost connection to selected backend",
                          EC_UNAVAILABLE);
                proxy_downserver(s);
                r = PROXY_NOCONNECTION;
                goto out;
            }
            /* Got the end of the response */
            buf_appendcstr(&s->last_result, buf);
            buf_cstring(&s->last_result);

            switch (buf[0]) {
            case 'O': case 'o':
                r = PROXY_OK;
                break;
            case 'N': case 'n':
                r = PROXY_NO;
                break;
            case 'B': case 'b':
                r = PROXY_BAD;
                break;
            default: /* huh? no result? */
                if(s == backend_current && !force_notfatal)
                    fatal("Lost connection to selected backend",
                          EC_UNAVAILABLE);
                proxy_downserver(s);
                r = PROXY_NOCONNECTION;
                break;
            }
            break; /* we're done */
        }

        c = getword(s->in, &cmd);

        if(c == EOF) {
            if(s == backend_current && !force_notfatal)
                fatal("Lost connection to selected backend", EC_UNAVAILABLE);
            proxy_downserver(s);
            r = PROXY_NOCONNECTION;
            goto out;
        }

        if(strncasecmp("LSUB", cmd.s, 4) && strncasecmp("LIST", cmd.s, 4)) {
            prot_printf(imapd_out, "%s %s ", tagb.s, cmd.s);
            r = pipe_to_end_of_response(s, force_notfatal);
            if (r != PROXY_OK)
                goto out;
        } else {
            /* build up the response bit by bit */
            int i;

            buf_reset(&flags);
            c = prot_getc(s->in);
            while(c != ')' && c != EOF) {
                buf_putc(&flags, c);
                c = prot_getc(s->in);
            }

            if(c != EOF) {
                /* terminate string */
                buf_putc(&flags, ')');
                buf_cstring(&flags);

                /* get the next character */
                c = prot_getc(s->in);
            }

            if(c != ' ') {
                if(s == backend_current && !force_notfatal)
                    fatal("Bad LSUB response from selected backend",
                          EC_UNAVAILABLE);
                proxy_downserver(s);
                r = PROXY_NOCONNECTION;
                goto out;
            }

            /* Check for flags that we should remove
             * (e.g. Noselect, NonExistent) */
            for(i=0; end_strip_flags[i]; i++)
                buf_replace_all(&flags, end_strip_flags[i], ")");

            for (i=0; mid_strip_flags[i]; i++)
                buf_replace_all(&flags, mid_strip_flags[i], NULL);

            /* Get separator */
            c = getastring(s->in, s->out, &sep);

            if(c != ' ') {
                if(s == backend_current && !force_notfatal)
                    fatal("Bad LSUB response from selected backend",
                          EC_UNAVAILABLE);
                proxy_downserver(s);
                r = PROXY_NOCONNECTION;
                goto out;
            }

            /* Get name */
            c = getastring(s->in, s->out, &name);

            if(c == '\r') c = prot_getc(s->in);
            if(c != '\n') {
                if(s == backend_current && !force_notfatal)
                    fatal("Bad LSUB response from selected backend",
                          EC_UNAVAILABLE);
                proxy_downserver(s);
                r = PROXY_NOCONNECTION;
                goto out;
            }

            /* lookup name */
            exist_r = 1;
            char *intname = mboxname_from_external(name.s, &imapd_namespace, userid);
            mbentry_t *mbentry = NULL;
            exist_r = mboxlist_lookup(intname, &mbentry, NULL);
            free(intname);
            if(!exist_r && (mbentry->mbtype & MBTYPE_RESERVE))
                exist_r = IMAP_MAILBOX_RESERVED;
            mboxlist_entry_free(&mbentry);

            /* send our response */
            /* we need to set \Noselect if it's not in our mailboxes.db */
            if (resp[0] == 'L') {
                if(!exist_r) {
                    prot_printf(imapd_out, "* %s %s \"%s\" ",
                                resp, flags.s, sep.s);
                } else {
                    prot_printf(imapd_out, "* %s (\\Noselect) \"%s\" ",
                                resp, sep.s);
                }

                prot_printstring(imapd_out, name.s);
                prot_printf(imapd_out, "\r\n");

            } else if(resp[0] == 'M' && !exist_r) {
                /* Note that it has to exist for a find response */
                prot_printf(imapd_out, "* %s ", resp);
                prot_printastring(imapd_out, name.s);
                prot_printf(imapd_out, "\r\n");
            }
        }
    } /* while(1) */

out:
    buf_free(&flags);
    return r;
}
Exemple #10
0
/* pipe_response() reads from 's->in' until either the tagged response
   starting with 'tag' appears, or if 'tag' is NULL, to the end of the
   current line.  If 'include_last' is set, the last/tagged line is included
   in the output, otherwise the last/tagged line is stored in 's->last_result'.
   In either case, the result of the tagged command is returned.

   's->last_result' assumes that tagged responses don't contain literals.
   Unfortunately, the IMAP grammar allows them

   force_notfatal says to not fatal() if we lose connection to backend_current
   even though it is in 95% of the cases, a good idea...
*/
static int pipe_response(struct backend *s, const char *tag, int include_last,
                         int force_notfatal)
{
    char buf[2048];
    char eol[128];
    unsigned sl;
    int cont = 0, last = !tag, r = PROXY_OK;
    size_t taglen = 0;

    s->timeout->mark = time(NULL) + IDLE_TIMEOUT;

    if (tag) {
        taglen = strlen(tag);
        if(taglen >= sizeof(buf) + 1) {
            fatal("tag too large",EC_TEMPFAIL);
        }
    }

    buf_reset(&s->last_result);

    /* the only complication here are literals */
    do {
        /* if 'cont' is set, we're looking at the continuation to a very
           long line.
           if 'last' is set, we've seen the tag we're looking for, we're
           just reading the end of the line. */
        if (!cont) eol[0] = '\0';

        if (!prot_fgets(buf, sizeof(buf), s->in)) {
            /* uh oh */
            if(s == backend_current && !force_notfatal)
                fatal("Lost connection to selected backend", EC_UNAVAILABLE);
            proxy_downserver(s);
            return PROXY_NOCONNECTION;
        }

        sl = strlen(buf);

        if (tag) {
            /* Check for the tagged line */
            if (!cont && buf[taglen] == ' ' && !strncmp(tag, buf, taglen)) {

                switch (buf[taglen + 1]) {
                case 'O': case 'o':
                    r = PROXY_OK;
                    break;
                case 'N': case 'n':
                    r = PROXY_NO;
                    break;
                case 'B': case 'b':
                    r = PROXY_BAD;
                    break;
                default: /* huh? no result? */
                    if(s == backend_current && !force_notfatal)
                        fatal("Lost connection to selected backend",
                              EC_UNAVAILABLE);
                    proxy_downserver(s);
                    r = PROXY_NOCONNECTION;
                    break;
                }

                last = 1;
            }

            if (last && !include_last) {
                /* Store the tagged line */
                buf_appendcstr(&s->last_result, buf+taglen+1);
                buf_cstring(&s->last_result);
            }
        }

        if (sl == (sizeof(buf) - 1) && buf[sl-1] != '\n') {
            /* only got part of a line */
            /* we save the last 64 characters in case it has important
               literal information */
            strcpy(eol, buf + sl - 64);

            /* write out this part, but we have to keep reading until we
               hit the end of the line */
            if (!last || include_last) prot_write(imapd_out, buf, sl);
            cont = 1;
            continue;
        } else {                /* we got the end of the line */
            int i;
            int litlen = 0, islit = 0;

            if (!last || include_last) prot_write(imapd_out, buf, sl);

            /* now we have to see if this line ends with a literal */
            if (sl < 64) {
                strcat(eol, buf);
            } else {
                strcat(eol, buf + sl - 63);
            }

            /* eol now contains the last characters from the line; we want
               to see if we've hit a literal */
            i = strlen(eol);
            if (i >= 4 &&
                eol[i-1] == '\n' && eol[i-2] == '\r' && eol[i-3] == '}') {
                /* possible literal */
                i -= 4;
                while (i > 0 && eol[i] != '{' && Uisdigit(eol[i])) {
                    i--;
                }
                if (eol[i] == '{') {
                    islit = 1;
                    litlen = atoi(eol + i + 1);
                }
            }

            /* copy the literal over */
            if (islit) {
                while (litlen > 0) {
                    int j = (litlen > (int) sizeof(buf) ?
                             (int) sizeof(buf) : litlen);

                    j = prot_read(s->in, buf, j);
                    if(!j) {
                        /* EOF or other error */
                        return -1;
                    }
                    if (!last || include_last) prot_write(imapd_out, buf, j);
                    litlen -= j;
                }

                /* none of our saved information has any relevance now */
                eol[0] = '\0';

                /* have to keep going for the end of the line */
                cont = 1;
                continue;
            }
        }

        /* ok, let's read another line */
        cont = 0;

    } while (!last || cont);

    return r;
}
Exemple #11
0
/* Returns TRUE if we are done */
int parser(struct protstream *sieved_out, struct protstream *sieved_in)
{
  int token = EOL;
  const char *error_msg = "Generic Error";

  struct buf mechanism_name = BUF_INITIALIZER;
  struct buf initial_challenge = BUF_INITIALIZER;
  struct buf sieve_name = BUF_INITIALIZER;
  struct buf sieve_data = BUF_INITIALIZER;
  unsigned long num;
  int ret = FALSE;

  /* get one token from the lexer */
  while(token == EOL) 
      token = timlex(NULL, NULL, sieved_in);

  if (!authenticated && (token > 255) && (token!=AUTHENTICATE) &&
      (token!=LOGOUT) && (token!=CAPABILITY) &&
      (token!=NOOP) && (token!=CHECKSCRIPT) &&
      (!tls_enabled() || (token!=STARTTLS)))
  {
    error_msg = "Authenticate first";
    if (token!=EOL)
      lex_setrecovering();

    goto error;
  }

  if (verify_only && (token > 255) && (token!=CHECKSCRIPT)
    && (token!=PUTSCRIPT) && (token!=LOGOUT))
  {
    error_msg = "Script verification only";
    if (token!=EOL)
      lex_setrecovering();

    goto error;
  }

  switch (token)
  {
  case EOF:
      /* timlex() will return EOF when the remote disconnects badly */
      syslog(LOG_WARNING, "Lost connection to client -- exiting");
      prot_printf(sieved_out, "BYE \"Shutdown TCP timeout\"\r\n");
      ret = TRUE;
      goto done;
      break;

  case AUTHENTICATE:
    if (sieved_tls_required) {
      error_msg = "AUTHENTICATE only available under a layer";
      goto error;
    }
    if (timlex(NULL, NULL, sieved_in)!=SPACE)
    {
      error_msg = "SPACE must occur after AUTHENTICATE";
      goto error;
    }

    if (timlex(&mechanism_name, NULL, sieved_in)!=STRING)
    {
      error_msg = "Did not specify mechanism name";
      goto error;
    }

    token = timlex(NULL, NULL, sieved_in);

    if (token != EOL)
    {
      /* optional client first challenge */
      if (token!=SPACE)
      {
	error_msg = "Expected SPACE";
	goto error;
      }

      if (timlex(&initial_challenge, NULL, sieved_in)!=STRING)
      {
	error_msg = "Expected string";
	goto error;
      }

      token = timlex(NULL, NULL, sieved_in);      
    }

    if (token != EOL)
    {
      error_msg = "Expected EOL";
      goto error;
    }

    if (authenticated)
	prot_printf(sieved_out, "NO \"Already authenticated\"\r\n");
    else if (cmd_authenticate(sieved_out, sieved_in, mechanism_name.s,
			      &initial_challenge, &error_msg)==FALSE)
    {
	error_msg = "Authentication Error";
	goto error;
    }

#if 0 /* XXX - not implemented in sieveshell*/
    /* referral_host is non-null only once we are authenticated */
    if(referral_host)
	goto do_referral;
#endif
    break;

  case CAPABILITY:
      if (timlex(NULL, NULL, sieved_in)!=EOL)
      {
	  error_msg = "Expected EOL";
	  goto error;
      }

      if(referral_host)
	  goto do_referral;

      capabilities(sieved_out, sieved_saslconn, starttls_done, authenticated,
		   sasl_ssf);
      break;

  case CHECKSCRIPT:
      if (timlex(NULL, NULL, sieved_in)!=SPACE)
      {
          error_msg = "SPACE must occur after CHECKSCRIPT";
          goto error;
      }

      if (timlex(&sieve_data, NULL, sieved_in)!=STRING)
      {
          error_msg = "Expected script content as second parameter";
          goto error;
      }

      if (timlex(NULL, NULL, sieved_in)!=EOL)
      {
        error_msg = "Expected EOL";
        goto error;
      }

      /* f stands for "f"aked name, it could be any valid script name */
      buf_reset(&sieve_name);
      buf_appendcstr(&sieve_name, "f");
      putscript(sieved_out, &sieve_name, &sieve_data, /* verify_only */ 1);
      break;

  case HAVESPACE:
      if (timlex(NULL, NULL, sieved_in)!=SPACE)
      {
	  error_msg = "SPACE must occur after HAVESPACE";
	  goto error;
      }
      
      if (timlex(&sieve_name, NULL, sieved_in)!=STRING)
      {
	  error_msg = "Did not specify script name";
	  goto error;
      }
      
      if (timlex(NULL, NULL, sieved_in)!=SPACE)
      {
	  error_msg = "Expected SPACE after SCRIPTNAME";
	  goto error;
      }
      
      if (timlex(NULL, &num, sieved_in)!=NUMBER)
      {
	  error_msg = "Expected Number";
	  goto error;
      }

      if (timlex(NULL, NULL, sieved_in)!=EOL)
      {
	  error_msg = "Expected EOL";
	  goto error;
      }

      if(referral_host)
	  goto do_referral;

      cmd_havespace(sieved_out, &sieve_name, num);

      break;

  case LOGOUT:
      token = timlex(NULL, NULL, sieved_in);
      
      /* timlex() will return LOGOUT when the remote disconnects badly */
      if (token!=EOL && token!=EOF && token!=LOGOUT)
      {
	  error_msg = "Garbage after logout command";
	  goto error;
      }

      /* no referral for logout */

      cmd_logout(sieved_out, sieved_in);
      
      ret = TRUE;
      goto done;
      break;

  case GETSCRIPT:
    if (timlex(NULL, NULL, sieved_in)!=SPACE)
    {
      error_msg = "SPACE must occur after GETSCRIPT";
      goto error;
    }

    if (timlex(&sieve_name, NULL, sieved_in)!=STRING)
    {
      error_msg = "Did not specify script name";
      goto error;
    }

    if (timlex(NULL, NULL, sieved_in)!=EOL)
    {
      error_msg = "Expected EOL";
      goto error;
    }

    if(referral_host)
	goto do_referral;

    getscript(sieved_out, &sieve_name);
    
    break;


  case PUTSCRIPT:
    if (timlex(NULL, NULL, sieved_in)!=SPACE)
    {
      error_msg = "SPACE must occur after PUTSCRIPT";
      goto error;
    }

    if (timlex(&sieve_name, NULL, sieved_in)!=STRING)
    {
      error_msg = "Did not specify script name";
      goto error;
    }

    if (timlex(NULL, NULL, sieved_in)!=SPACE)
    {
      error_msg = "Expected SPACE";
      goto error;
    }

    if (timlex(&sieve_data, NULL, sieved_in)!=STRING)
    {
      error_msg = "Did not specify legal script data length";
      goto error;
    }

    if (timlex(NULL, NULL, sieved_in)!=EOL)
    {
      error_msg = "Expected EOL";
      goto error;
    }

    if(referral_host)
	goto do_referral;

    putscript(sieved_out, &sieve_name, &sieve_data, verify_only);
    
    break;

  case SETACTIVE:
    if (timlex(NULL, NULL, sieved_in)!=SPACE)
    {
      error_msg = "SPACE must occur after SETACTIVE";
      goto error;
    }

    if (timlex(&sieve_name, NULL, sieved_in)!=STRING)
    {
      error_msg = "Did not specify script name";
      goto error;
    }

    if (timlex(NULL, NULL, sieved_in)!=EOL)
    {
      error_msg = "Expected EOL";
      goto error;
    }

    if(referral_host)
	goto do_referral;

    setactive(sieved_out, &sieve_name);
    
    break;

  case DELETESCRIPT:
    if (timlex(NULL, NULL, sieved_in)!=SPACE)
    {
      error_msg = "SPACE must occur after DELETESCRIPT";
      goto error;
    }

    if (timlex(&sieve_name, NULL, sieved_in)!=STRING)
    {
      error_msg = "Did not specify script name";
      goto error;
    }

    if (timlex(NULL, NULL, sieved_in)!=EOL)
    {
      error_msg = "Expected EOL";
      goto error;
    }

    if(referral_host)
	goto do_referral;

    deletescript(sieved_out, &sieve_name);
    
    break;

  case LISTSCRIPTS:

    if (timlex(NULL, NULL, sieved_in)!=EOL)
    {
      error_msg = "Expected EOL";
      goto error;
    }

    if(referral_host)
	goto do_referral;
    
    listscripts(sieved_out);
    
    break;

  case STARTTLS:

    if (timlex(NULL, NULL, sieved_in)!=EOL)
    {
      error_msg = "Expected EOL";
      goto error;
    }

    /* XXX  discard any input pipelined after STARTTLS */
    prot_flush(sieved_in);

    if(referral_host)
	goto do_referral;

    cmd_starttls(sieved_out, sieved_in);
    
    break;

  case NOOP:

    token = timlex(NULL, NULL, sieved_in);
    if (token != EOL)
    {
      /* optional string parameter */
      if (token!=SPACE)
      {
	error_msg = "Expected SPACE";
	goto error;
      }

      if (timlex(&sieve_name, NULL, sieved_in)!=STRING)
      {
	error_msg = "Expected string";
	goto error;
      }

      token = timlex(NULL, NULL, sieved_in);      
    }

    if (token != EOL)
    {
      error_msg = "Expected EOL";
      goto error;
    }

    if (sieve_name.len) {
      prot_printf(sieved_out, "OK (TAG ");
      prot_printliteral(sieved_out, sieve_name.s, sieve_name.len);
      prot_printf(sieved_out, ") \"Done\"\r\n");
    } else
      prot_printf(sieved_out, "OK \"Done\"\r\n");
    break;

  case UNAUTHENTICATE:
      if (timlex(NULL, NULL, sieved_in)!=EOL)
      {
	  error_msg = "Expected EOL";
	  goto error;
      }
      cmd_unauthenticate(sieved_out, sieved_in);
      break;

  default:
    error_msg="Expected a command. Got something else.";
    goto error;
    break;

  }

 done: 
  /* free memory */
  buf_free(&mechanism_name);
  buf_free(&initial_challenge);
  buf_free(&sieve_name);
  buf_free(&sieve_data);
 
  prot_flush(sieved_out);

  return ret;

 error:

  /* free memory */
  buf_free(&mechanism_name);
  buf_free(&initial_challenge);
  buf_free(&sieve_name);
  buf_free(&sieve_data);

  prot_printf(sieved_out, "NO \"%s\"\r\n",error_msg);
  prot_flush(sieved_out);

  return FALSE;

 do_referral:
  {
      char buf[4096];
      char *c;

      /* Truncate the hostname if necessary */
      strlcpy(buf, referral_host, sizeof(buf));
      c = strchr(buf, '!');
      if(c) *c = '\0';
      
      prot_printf(sieved_out, "BYE (REFERRAL \"sieve://%s\") \"Try Remote.\"\r\n",
		  buf);
      ret = TRUE;
      goto done;
  }

}
Exemple #12
0
HIDDEN int ws_start_channel(struct transaction_t *txn, const char *protocol,
                            int (*data_cb)(struct buf *inbuf, struct buf *outbuf,
                                           struct buf *logbuf, void **rock))
{
    int r;
    const char **hdr, *accept = NULL;
    wslay_event_context_ptr ev;
    struct ws_context *ctx;
    struct wslay_event_callbacks callbacks = {
        recv_cb,
        send_cb,
        NULL,
        NULL,
        NULL,
        NULL,
        on_msg_recv_cb
    };

    /* Check for supported WebSocket version */
    hdr = spool_getheader(txn->req_hdrs, "Sec-WebSocket-Version");
    if (!hdr) {
        txn->error.desc = "Missing WebSocket version";
        return HTTP_BAD_REQUEST;
    }
    else if (hdr[1]) {
        txn->error.desc = "Multiple WebSocket versions";
        return HTTP_BAD_REQUEST;
    }
    else if (strcmp(hdr[0], WS_VERSION)) {
        txn->error.desc = "Unsupported WebSocket version";
        return HTTP_UPGRADE;
    }

    if (protocol) {
        /* Check for supported WebSocket subprotocol */
        int i, found = 0;

        hdr = spool_getheader(txn->req_hdrs, "Sec-WebSocket-Protocol");
        if (!hdr) {
            txn->error.desc = "Missing WebSocket protocol";
            return HTTP_BAD_REQUEST;
        }

        for (i = 0; !found && hdr[i]; i++) {
            tok_t tok = TOK_INITIALIZER(hdr[i], ",", TOK_TRIMLEFT|TOK_TRIMRIGHT);
            char *token;

            while ((token = tok_next(&tok))) {
                if (!strcmp(token, protocol)) {
                    found = 1;
                    break;
                }
            }
            tok_fini(&tok);
        }
        if (!found) {
            txn->error.desc = "Unsupported WebSocket protocol";
            return HTTP_BAD_REQUEST;
        }
    }

    if (txn->flags.ver == VER_1_1) {
        unsigned char sha1buf[SHA1_DIGEST_LENGTH];

        /* Check for WebSocket client key */
        hdr = spool_getheader(txn->req_hdrs, "Sec-WebSocket-Key");
        if (!hdr) {
            txn->error.desc = "Missing WebSocket client key";
            return HTTP_BAD_REQUEST;
        }
        else if (hdr[1]) {
            txn->error.desc = "Multiple WebSocket client keys";
            return HTTP_BAD_REQUEST;
        }
        else if (strlen(hdr[0]) != WS_CKEY_LEN) {
            txn->error.desc = "Invalid WebSocket client key";
            return HTTP_BAD_REQUEST;
        }

        /* Create WebSocket accept key */
        buf_setcstr(&txn->buf, hdr[0]);
        buf_appendcstr(&txn->buf, WS_GUID);
        xsha1((u_char *) buf_base(&txn->buf), buf_len(&txn->buf), sha1buf);

        buf_ensure(&txn->buf, WS_AKEY_LEN+1);
        accept = buf_base(&txn->buf);

        r = sasl_encode64((char *) sha1buf, SHA1_DIGEST_LENGTH,
                          (char *) accept, WS_AKEY_LEN+1, NULL);
        if (r != SASL_OK) syslog(LOG_WARNING, "sasl_encode64: %d", r);
    }

    /* Create server context */
    r = wslay_event_context_server_init(&ev, &callbacks, txn);
    if (r) {
        syslog(LOG_WARNING,
               "wslay_event_context_init: %s", wslay_strerror(r));
        return HTTP_SERVER_ERROR;
    }

    /* Create channel context */
    ctx = xzmalloc(sizeof(struct ws_context));
    ctx->event = ev;
    ctx->accept = accept;
    ctx->protocol = protocol;
    ctx->data_cb = data_cb;
    txn->ws_ctx = ctx;

    /* Check for supported WebSocket extensions */
    parse_extensions(txn);

    /* Prepare log buffer */

    /* Add client data */
    buf_printf(&ctx->log, "%s", txn->conn->clienthost);
    if (httpd_userid) buf_printf(&ctx->log, " as \"%s\"", httpd_userid);
    if ((hdr = spool_getheader(txn->req_hdrs, "User-Agent"))) {
        buf_printf(&ctx->log, " with \"%s\"", hdr[0]);
        if ((hdr = spool_getheader(txn->req_hdrs, "X-Client")))
            buf_printf(&ctx->log, " by \"%s\"", hdr[0]);
        else if ((hdr = spool_getheader(txn->req_hdrs, "X-Requested-With")))
            buf_printf(&ctx->log, " by \"%s\"", hdr[0]);
    }

    /* Add request-line */
    buf_printf(&ctx->log, "; \"WebSocket/%s via %s\"",
               protocol ? protocol : "echo" , txn->req_line.ver);
    ctx->log_tail = buf_len(&ctx->log);

    /* Tell client that WebSocket negotiation has succeeded */
    if (txn->conn->sess_ctx) {
        /* Treat WS data as chunked response */
        txn->flags.te = TE_CHUNKED;

        response_header(HTTP_OK, txn);

        /* Force the response to the client immediately */
        prot_flush(httpd_out);
    }
    else response_header(HTTP_SWITCH_PROT, txn);

    /* Set connection as non-blocking */
    prot_NONBLOCK(txn->conn->pin);

    /* Don't do telemetry logging in prot layer */
    prot_setlog(txn->conn->pin, PROT_NO_FD);
    prot_setlog(txn->conn->pout, PROT_NO_FD);

    return 0;
}
Exemple #13
0
static int login(struct backend *s, const char *userid,
                 sasl_callback_t *cb, const char **status,
                 int noauth __attribute__((unused)))
{
    int r = 0;
    socklen_t addrsize;
    struct sockaddr_storage saddr_l, saddr_r;
    char remoteip[60], localip[60];
    static struct buf buf = BUF_INITIALIZER;
    sasl_security_properties_t secprops =
        { 0, 0xFF, PROT_BUFSIZE, 0, NULL, NULL }; /* default secprops */
    const char *mech_conf, *pass, *clientout = NULL;
    struct auth_scheme_t *scheme = NULL;
    unsigned need_tls = 0, tls_done = 0, auth_done = 0, clientoutlen;
    hdrcache_t hdrs = NULL;

    if (status) *status = NULL;

    /* set the IP addresses */
    addrsize = sizeof(struct sockaddr_storage);
    if (getpeername(s->sock, (struct sockaddr *) &saddr_r, &addrsize) ||
        iptostring((struct sockaddr *) &saddr_r, addrsize, remoteip, 60)) {
        if (status) *status = "Failed to get remote IP address";
        return SASL_FAIL;
    }

    addrsize = sizeof(struct sockaddr_storage);
    if (getsockname(s->sock, (struct sockaddr *) &saddr_l, &addrsize) ||
        iptostring((struct sockaddr *) &saddr_l, addrsize, localip, 60)) {
        if (status) *status = "Failed to get local IP address";
        return SASL_FAIL;
    }

    /* Create callbacks, if necessary */
    if (!cb) {
        buf_setmap(&buf, s->hostname, strcspn(s->hostname, "."));
        buf_appendcstr(&buf, "_password");
        pass = config_getoverflowstring(buf_cstring(&buf), NULL);
        if (!pass) pass = config_getstring(IMAPOPT_PROXY_PASSWORD);
        cb = mysasl_callbacks(NULL, /* userid */
                              config_getstring(IMAPOPT_PROXY_AUTHNAME),
                              config_getstring(IMAPOPT_PROXY_REALM),
                              pass);
        s->sasl_cb = cb;
    }

    /* Create SASL context */
    r = sasl_client_new(s->prot->sasl_service, s->hostname,
                        localip, remoteip, cb, SASL_USAGE_FLAGS, &s->saslconn);
    if (r != SASL_OK) goto done;

    r = sasl_setprop(s->saslconn, SASL_SEC_PROPS, &secprops);
    if (r != SASL_OK) goto done;

    /* Get SASL mechanism list.  We can force a particular
       mechanism using a <shorthost>_mechs option */
    buf_setmap(&buf, s->hostname, strcspn(s->hostname, "."));
    buf_appendcstr(&buf, "_mechs");
    if (!(mech_conf = config_getoverflowstring(buf_cstring(&buf), NULL))) {
        mech_conf = config_getstring(IMAPOPT_FORCE_SASL_CLIENT_MECH);
    }

    do {
        unsigned code;
        const char **hdr, *errstr, *serverin;
        char base64[BASE64_BUF_SIZE+1];
        unsigned int serverinlen;
        struct body_t resp_body;
#ifdef SASL_HTTP_REQUEST
        sasl_http_request_t httpreq = { "OPTIONS",      /* Method */
                                        "*",            /* URI */
                                        (u_char *) "",  /* Empty body */
                                        0,              /* Zero-length body */
                                        0 };            /* Persistent cxn? */
#endif

        /* Base64 encode any client response, if necessary */
        if (clientout && scheme && (scheme->flags & AUTH_BASE64)) {
            r = sasl_encode64(clientout, clientoutlen,
                              base64, BASE64_BUF_SIZE, &clientoutlen);
            if (r != SASL_OK) break;

            clientout = base64;
        }

        /* Send Authorization and/or Upgrade request to server */
        prot_puts(s->out, "OPTIONS * HTTP/1.1\r\n");
        prot_printf(s->out, "Host: %s\r\n", s->hostname);
        prot_printf(s->out, "User-Agent: %s\r\n", buf_cstring(&serverinfo));
        if (scheme) {
            prot_printf(s->out, "Authorization: %s %s\r\n",
                        scheme->name, clientout ? clientout : "");
            prot_printf(s->out, "Authorize-As: %s\r\n",
                        userid ? userid : "anonymous");
        }
        else {
            prot_printf(s->out, "Upgrade: %s\r\n", TLS_VERSION);
            if (need_tls) {
                prot_puts(s->out, "Connection: Upgrade\r\n");
                need_tls = 0;
            }
            prot_puts(s->out, "Authorization: \r\n");
        }
        prot_puts(s->out, "\r\n");
        prot_flush(s->out);

        serverin = clientout = NULL;
        serverinlen = clientoutlen = 0;

        /* Read response(s) from backend until final response or error */
        do {
            resp_body.flags = BODY_DISCARD;
            r = http_read_response(s, METH_OPTIONS, &code, NULL,
                                   &hdrs, &resp_body, &errstr);
            if (r) {
                if (status) *status = errstr;
                break;
            }

            if (code == 101) {  /* Switching Protocols */
                if (tls_done) {
                    r = HTTP_BAD_GATEWAY;
                    if (status) *status = "TLS already active";
                    break;
                }
                else if (backend_starttls(s, NULL, NULL, NULL)) {
                    r = HTTP_SERVER_ERROR;
                    if (status) *status = "Unable to start TLS";
                    break;
                }
                else tls_done = 1;
            }
        } while (code < 200);

        switch (code) {
        default: /* Failure */
            if (!r) {
                r = HTTP_BAD_GATEWAY;
                if (status) {
                    buf_reset(&buf);
                    buf_printf(&buf,
                               "Unexpected status code from backend: %u", code);
                    *status = buf_cstring(&buf);
                }
            }
            break;

        case 426: /* Upgrade Required */
            if (tls_done) {
                r = HTTP_BAD_GATEWAY;
                if (status) *status = "TLS already active";
            }
            else need_tls = 1;
            break;

        case 200: /* OK */
            if (scheme->recv_success &&
                (serverin = scheme->recv_success(hdrs))) {
                /* Process success data */
                serverinlen = strlen(serverin);
                goto challenge;
            }
            break;

        case 401: /* Unauthorized */
            if (auth_done) {
                r = SASL_BADAUTH;
                break;
            }

            if (!serverin) {
                int i = 0;

                hdr = spool_getheader(hdrs, "WWW-Authenticate");

                if (!scheme) {
                    unsigned avail_auth_schemes = 0;
                    const char *mech = NULL;
                    size_t len;

                    /* Compare authentication schemes offered in
                     * WWW-Authenticate header(s) to what we support */
                    buf_reset(&buf);
                    for (i = 0; hdr && hdr[i]; i++) {
                        len = strcspn(hdr[i], " ");

                        for (scheme = auth_schemes; scheme->name; scheme++) {
                            if (!strncmp(scheme->name, hdr[i], len) &&
                                !((scheme->flags & AUTH_NEED_PERSIST) &&
                                  (resp_body.flags & BODY_CLOSE))) {
                                /* Tag the scheme as available */
                                avail_auth_schemes |= (1 << scheme->idx);

                                /* Add SASL-based schemes to SASL mech list */
                                if (scheme->saslmech) {
                                    if (buf_len(&buf)) buf_putc(&buf, ' ');
                                    buf_appendcstr(&buf, scheme->saslmech);
                                }
                                break;
                            }
                        }
                    }

                    /* If we have a mech_conf, use it */
                    if (mech_conf && buf_len(&buf)) {
                        char *conf = xstrdup(mech_conf);
                        char *newmechlist =
                            intersect_mechlists(conf,
                                                (char *) buf_cstring(&buf));

                        if (newmechlist) {
                            buf_setcstr(&buf, newmechlist);
                            free(newmechlist);
                        }
                        else {
                            syslog(LOG_DEBUG, "%s did not offer %s",
                                   s->hostname, mech_conf);
                            buf_reset(&buf);
                        }
                        free(conf);
                    }

#ifdef SASL_HTTP_REQUEST
                    /* Set HTTP request as specified above (REQUIRED) */
                    httpreq.non_persist = (resp_body.flags & BODY_CLOSE);
                    sasl_setprop(s->saslconn, SASL_HTTP_REQUEST, &httpreq);
#endif

                    /* Try to start SASL exchange using available mechs */
                    r = sasl_client_start(s->saslconn, buf_cstring(&buf),
                                          NULL,         /* no prompts */
                                          NULL, NULL,   /* no initial resp */
                                          &mech);

                    if (mech) {
                        /* Find auth scheme associated with chosen SASL mech */
                        for (scheme = auth_schemes; scheme->name; scheme++) {
                            if (scheme->saslmech &&
                                !strcmp(scheme->saslmech, mech)) break;
                        }
                    }
                    else {
                        /* No matching SASL mechs - try Basic */
                        scheme = &auth_schemes[AUTH_BASIC];
                        if (!(avail_auth_schemes & (1 << scheme->idx))) {
                            need_tls = !tls_done;
                            break;  /* case 401 */
                        }
                    }

                    /* Find the associated WWW-Authenticate header */
                    for (i = 0; hdr && hdr[i]; i++) {
                        len = strcspn(hdr[i], " ");
                        if (!strncmp(scheme->name, hdr[i], len)) break;
                    }
                }

                /* Get server challenge, if any */
                if (hdr) {
                    const char *p = strchr(hdr[i], ' ');
                    serverin = p ? ++p : "";
                    serverinlen = strlen(serverin);
                }
            }

        challenge:
            if (serverin) {
                /* Perform the next step in the auth exchange */

                if (scheme->idx == AUTH_BASIC) {
                    /* Don't care about "realm" in server challenge */
                    const char *authid =
                        callback_getdata(s->saslconn, cb, SASL_CB_AUTHNAME);
                    pass = callback_getdata(s->saslconn, cb, SASL_CB_PASS);

                    buf_reset(&buf);
                    buf_printf(&buf, "%s:%s", authid, pass);
                    clientout = buf_cstring(&buf);
                    clientoutlen = buf_len(&buf);
                    auth_done = 1;
                }
                else {
                    /* Base64 decode any server challenge, if necessary */
                    if (serverin && (scheme->flags & AUTH_BASE64)) {
                        r = sasl_decode64(serverin, serverinlen,
                                          base64, BASE64_BUF_SIZE, &serverinlen);
                        if (r != SASL_OK) break;  /* case 401 */

                        serverin = base64;
                    }

                    /* SASL mech (Digest, Negotiate, NTLM) */
                    r = sasl_client_step(s->saslconn, serverin, serverinlen,
                                         NULL,          /* no prompts */
                                         &clientout, &clientoutlen);
                    if (r == SASL_OK) auth_done = 1;
                }
            }
            break;  /* case 401 */
        }

    } while (need_tls || clientout);

  done:
    if (hdrs) spool_free_hdrcache(hdrs);

    if (r && status && !*status) *status = sasl_errstring(r, NULL, NULL);

    return r;
}
Exemple #14
0
static void _endline(struct vparse_target *tgt)
{
    buf_appendcstr(tgt->buf, "\r\n");
    tgt->last = buf_len(tgt->buf);
}
Exemple #15
0
EXPORTED const char *dlist_print_iter_step(struct dlist_print_iter *iter, struct buf *outbuf)
{
    /* already finished */
    if (!iter->next) return NULL;

    buf_reset(outbuf);

    /* Bundle short steps together to minimise call overhead.
     * Note that outbuf can grow significantly longer than this limit, if a
     * single item in the dlist is very long (e.g. a message), but then it
     * won't bundle up more than that.
     */
    while (iter->next != NULL && buf_len(outbuf) < 1024) {
        const struct dlist *curr = iter->next;
        struct dlist_stack_node *parent = NULL;
        int descend = 0;

        /* output */
        switch (curr->type) {
        case DL_KVLIST:
        case DL_ATOMLIST:
            // XXX should use equiv to "prot_printastring" for curr->name
            if (iter->printkeys)
                buf_printf(outbuf, "%s ", curr->name);

            buf_appendcstr(outbuf, curr->type == DL_KVLIST ? "%(" : "(");

            if (curr->head) {
                descend = 1;
            }
            else {
                buf_putc(outbuf, ')');
                if (curr->next)
                    buf_putc(outbuf, ' ');
            }
            break;

        default:
            dlist_printbuf(curr, iter->printkeys, outbuf);
            if (curr->next)
                buf_putc(outbuf, ' ');
            break;
        }

        /* increment */
        if (descend) {
            parent = xmalloc(sizeof *parent);
            parent->printkeys = iter->printkeys;
            parent->dl = curr;
            parent->next = iter->parent;
            iter->parent = parent;
            iter->next = curr->head;
            // XXX can this always be 1? we know an atom list here is non-empty
            iter->printkeys = curr->type == DL_KVLIST ? 1 : curr->nval;
        }
        else if (curr->next) {
            iter->next = curr->next;
        }
        else if (iter->parent) {
            /* multiple parents might be ending at the same point
             * don't mistake one parent ending for end of entire tree
             */
            do {
                buf_putc(outbuf, ')');

                parent = iter->parent;

                iter->parent = iter->parent->next;
                iter->next = parent->dl->next;
                iter->printkeys = parent->printkeys;

                free(parent);

                if (iter->next) {
                    /* found an unfinished dlist, stop closing parents */
                    buf_putc(outbuf, ' ');
                    break;
                }
            } while (iter->parent);
        }
        else {
            iter->next = NULL;
        }
    }

    /* and return */
    return buf_cstringnull(outbuf);
}
Exemple #16
0
static void test_printstring(void)
{
    PROLOG;
    struct protstream *p;
    int len;
    struct buf b = BUF_INITIALIZER;
    int i;
    char str[2600];

    p = prot_new(_fd, 1);
    CU_ASSERT_PTR_NOT_NULL_FATAL(p);

    /* NULL string */
    BEGIN;
    prot_printstring(p, NULL);
    prot_flush(p);
    END(str, len);
    CU_ASSERT_EQUAL(len, 3);
    CU_ASSERT_STRING_EQUAL(str, "NIL");

    /* Zero length string */
    BEGIN;
    prot_printstring(p, "");
    prot_flush(p);
    END(str, len);
    CU_ASSERT_EQUAL(len, 2);
    CU_ASSERT_STRING_EQUAL(str, "\"\"");

    /* Boring string */
    BEGIN;
    prot_printstring(p, "Hello");
    prot_flush(p);
    END(str, len);
    CU_ASSERT_EQUAL(len, 7);
    CU_ASSERT_STRING_EQUAL(str, "\"Hello\"");

    /* String with non-dangerous whitespace */
    BEGIN;
    prot_printstring(p, "Hello World\tagain");
    prot_flush(p);
    END(str, len);
    CU_ASSERT_EQUAL(len, 19);
    CU_ASSERT_STRING_EQUAL(str, "\"Hello World\tagain\"");

    /* String with dangerous whitespace */
    BEGIN;
    prot_printstring(p, "Good\rBye\nEarth");
    prot_flush(p);
    END(str, len);
    CU_ASSERT_EQUAL(len, 20);
    CU_ASSERT_STRING_EQUAL(str, "{14}\r\nGood\rBye\nEarth");

    /* String with embedded dquote */
    BEGIN;
    prot_printstring(p, "Quot\"able");
    prot_flush(p);
    END(str, len);
    CU_ASSERT_EQUAL(len, 14);
    CU_ASSERT_STRING_EQUAL(str, "{9}\r\nQuot\"able");

    /* String with embedded percent */
    BEGIN;
    prot_printstring(p, "per%ent");
    prot_flush(p);
    END(str, len);
    CU_ASSERT_EQUAL(len, 12);
    CU_ASSERT(!strcmp(str, "{7}\r\nper%ent"));

    /* String with embedded backslash */
    BEGIN;
    prot_printstring(p, "slash\\dot");
    prot_flush(p);
    END(str, len);
    CU_ASSERT_EQUAL(len, 14);
    CU_ASSERT_STRING_EQUAL(str, "{9}\r\nslash\\dot");

    /* String with embedded 8-bit chars */
    BEGIN;
    prot_printstring(p, "Hi I'm \330l\345f");
    prot_flush(p);
    END(str, len);
    CU_ASSERT_EQUAL(len, 17);
    CU_ASSERT_STRING_EQUAL(str, "{11}\r\nHi I'm \330l\345f");

    /* Boring but overly long string */
    for (i = 0 ; i<500 ; i++)
	buf_appendcstr(&b, "blah ");
    buf_cstring(&b);
    BEGIN;
    prot_printstring(p, b.s);
    prot_flush(p);
    END(str, len);
    CU_ASSERT_EQUAL(len, b.len+8);
    CU_ASSERT_STRING_EQUAL(str+8, b.s);
    str[8] = '\0';
    CU_ASSERT_STRING_EQUAL(str, "{2500}\r\n");

    buf_free(&b);
    prot_free(p);
    EPILOG;
}
Exemple #17
0
static void on_msg_recv_cb(wslay_event_context_ptr ev,
                           const struct wslay_event_on_msg_recv_arg *arg,
                           void *user_data)
{
    struct transaction_t *txn = (struct transaction_t *) user_data;
    struct ws_context *ctx = (struct ws_context *) txn->ws_ctx;
    struct buf inbuf = BUF_INITIALIZER, outbuf = BUF_INITIALIZER;
    struct wslay_event_msg msgarg = { arg->opcode, NULL, 0 };
    uint8_t rsv = WSLAY_RSV_NONE;
    double cmdtime, nettime;
    const char *err_msg;
    int r, err_code = 0;

    /* Place client request into a buf */
    buf_init_ro(&inbuf, (const char *) arg->msg, arg->msg_length);

    /* Decompress request, if necessary */
    if (wslay_get_rsv1(arg->rsv)) {
        /* Add trailing 4 bytes */
        buf_appendmap(&inbuf, "\x00\x00\xff\xff", 4);

        r = zlib_decompress(txn, buf_base(&inbuf), buf_len(&inbuf));
        if (r) {
            syslog(LOG_ERR, "on_msg_recv_cb(): zlib_decompress() failed");

            err_code = WSLAY_CODE_PROTOCOL_ERROR;
            err_msg = DECOMP_FAILED_ERR;
            goto err;
        }

        buf_move(&inbuf, &txn->zbuf);
    }

    /* Log the uncompressed client request */
    buf_truncate(&ctx->log, ctx->log_tail);
    buf_appendcstr(&ctx->log, " (");
    if (txn->strm_ctx) {
        buf_printf(&ctx->log, "stream-id=%d; ",
                   http2_get_streamid(txn->strm_ctx));
    }
    buf_printf(&ctx->log, "opcode=%s; rsv=0x%x; length=%ld",
               wslay_str_opcode(arg->opcode), arg->rsv, arg->msg_length);

    switch (arg->opcode) {
    case WSLAY_CONNECTION_CLOSE:
        buf_printf(&ctx->log, "; status=%d; msg='%s'", arg->status_code,
                   buf_len(&inbuf) ? buf_cstring(&inbuf)+2 : "");
        txn->flags.conn = CONN_CLOSE;
        break;

    case WSLAY_TEXT_FRAME:
    case WSLAY_BINARY_FRAME:
        if (txn->conn->logfd != -1) {
            /* Telemetry logging */
            struct iovec iov[2];
            int niov = 0;

            assert(!buf_len(&txn->buf));
            buf_printf(&txn->buf, "<%ld<", time(NULL));  /* timestamp */
            WRITEV_ADD_TO_IOVEC(iov, niov,
                                buf_base(&txn->buf), buf_len(&txn->buf));
            WRITEV_ADD_TO_IOVEC(iov, niov, buf_base(&inbuf), buf_len(&inbuf));
            writev(txn->conn->logfd, iov, niov);
            buf_reset(&txn->buf);
        }

        /* Process the request */
        r = ctx->data_cb(&inbuf, &outbuf, &ctx->log, &ctx->cb_rock);
        if (r) {

            err_code = (r == HTTP_SERVER_ERROR ?
                        WSLAY_CODE_INTERNAL_SERVER_ERROR :
                        WSLAY_CODE_INVALID_FRAME_PAYLOAD_DATA);
            err_msg = error_message(r);
            goto err;
        }

        if (txn->conn->logfd != -1) {
            /* Telemetry logging */
            struct iovec iov[2];
            int niov = 0;

            assert(!buf_len(&txn->buf));
            buf_printf(&txn->buf, ">%ld>", time(NULL));  /* timestamp */
            WRITEV_ADD_TO_IOVEC(iov, niov,
                                buf_base(&txn->buf), buf_len(&txn->buf));
            WRITEV_ADD_TO_IOVEC(iov, niov, buf_base(&outbuf), buf_len(&outbuf));
            writev(txn->conn->logfd, iov, niov);
            buf_reset(&txn->buf);
        }

        /* Compress the server response, if supported by the client */
        if (ctx->ext & EXT_PMCE_DEFLATE) {
            r = zlib_compress(txn,
                              ctx->pmce.deflate.no_context ? COMPRESS_START : 0,
                              buf_base(&outbuf), buf_len(&outbuf));
            if (r) {
                syslog(LOG_ERR, "on_msg_recv_cb(): zlib_compress() failed");

                err_code = WSLAY_CODE_INTERNAL_SERVER_ERROR;
                err_msg = COMP_FAILED_ERR;
                goto err;
            }

            /* Trim the trailing 4 bytes */
            buf_truncate(&txn->zbuf, buf_len(&txn->zbuf) - 4);
            buf_move(&outbuf, &txn->zbuf);

            rsv |= WSLAY_RSV1_BIT;
        }

        /* Queue the server response */
        msgarg.msg = (const uint8_t *) buf_base(&outbuf);
        msgarg.msg_length = buf_len(&outbuf);
        wslay_event_queue_msg_ex(ev, &msgarg, rsv);

        /* Log the server response */
        buf_printf(&ctx->log,
                   ") => \"Success\" (opcode=%s; rsv=0x%x; length=%ld",
                   wslay_str_opcode(msgarg.opcode), rsv, msgarg.msg_length);
        break;
    }

  err:
    if (err_code) {
        size_t err_msg_len = strlen(err_msg);

        syslog(LOG_DEBUG, "wslay_event_queue_close()");
        wslay_event_queue_close(ev, err_code, (uint8_t *) err_msg, err_msg_len);

        /* Log the server response */
        buf_printf(&ctx->log,
                   ") => \"Fail\" (opcode=%s; rsv=0x%x; length=%ld"
                   "; status=%d; msg='%s'",
                   wslay_str_opcode(WSLAY_CONNECTION_CLOSE), rsv, err_msg_len,
                   err_code, err_msg);
    }

    /* Add timing stats */
    cmdtime_endtimer(&cmdtime, &nettime);
    buf_printf(&ctx->log, ") [timing: cmd=%f net=%f total=%f]",
               cmdtime, nettime, cmdtime + nettime);

    syslog(LOG_INFO, "%s", buf_cstring(&ctx->log));

    buf_free(&inbuf);
    buf_free(&outbuf);
}