Beispiel #1
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;
}
static const char *column_text_to_buf(const char *text, struct buf *buf)
{
    if (text) {
        buf_setcstr(buf, text);
        text = buf_cstring(buf);
    }

    return text;
}
Beispiel #3
0
/* Create filename corresponding to DAV DB for mailbox */
EXPORTED void dav_getpath(struct buf *fname, struct mailbox *mailbox)
{
    const char *userid;

    userid = mboxname_to_userid(mailbox->name);

    if (userid) dav_getpath_byuserid(fname, userid);
    else buf_setcstr(fname, mailbox_meta_fname(mailbox, META_DAV));
}
Beispiel #4
0
static int my_mkparentdir(const char *fname)
{
    struct buf buf = BUF_INITIALIZER;
    int r;

    buf_setcstr(&buf, fname);
    r = cyrus_mkdir(buf_cstring(&buf), 0755);
    buf_free(&buf);
    return r;
}
Beispiel #5
0
static int smtpclient_read(smtpclient_t *sm, smtp_readcb_t *cb, void *rock)
{
    char buf[513]; /* Maximum length of reply line, see RFC 5321, 4.5.3.1.5. */
    int r = IMAP_IOERROR;

    do {
        /* Read next reply line. */
        if (!prot_fgets(buf, 513, sm->backend->in)) {
            r = IMAP_IOERROR;
            return r;
        }
        buf[512] = '\0';

        /* Parse reply line. */
        if (!isdigit(buf[0]) || !isdigit(buf[1]) || !isdigit(buf[2])) {
            return IMAP_PROTOCOL_ERROR;
        }
        if (buf[3] != '-' && !isspace(buf[3])) {
            return IMAP_PROTOCOL_ERROR;
        }
        char *p = memchr(buf + 4, '\n', 508);
        if (p == NULL) {
            return IMAP_PROTOCOL_ERROR;
        }
        if (*(p-1) == '\r') {
            --p;
        }
        *p = '\0';

        /* Call callback. */
        memcpy(sm->resp.code, buf, 3);
        buf_setcstr(&sm->resp.text, buf + 4);
        sm->resp.is_last = isspace(buf[3]);
        r = cb(sm, rock);
    } while (!r && !sm->resp.is_last);

    return r;
}
Beispiel #6
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;
}
Beispiel #7
0
/*
 * Construct an iCalendar property value from XML content.
 */
static icalvalue *xml_element_to_icalvalue(xmlNodePtr xtype,
        icalvalue_kind kind)
{
    icalvalue *value = NULL;
    xmlNodePtr node;
    xmlChar *content = NULL;

    switch (kind) {

    case ICAL_GEO_VALUE: {
        struct icalgeotype geo;

        node = xmlFirstElementChild(xtype);
        if (!node) {
            syslog(LOG_WARNING, "Missing <latitude> XML element");
            break;
        }
        else if (xmlStrcmp(node->name, BAD_CAST "latitude")) {
            syslog(LOG_WARNING,
                   "Expected <latitude> XML element, received %s", node->name);
            break;
        }

        content = xmlNodeGetContent(node);
        geo.lat = atof((const char *) content);

        node = xmlNextElementSibling(node);
        if (!node) {
            syslog(LOG_WARNING, "Missing <longitude> XML element");
            break;
        }
        else if (xmlStrcmp(node->name, BAD_CAST "longitude")) {
            syslog(LOG_WARNING,
                   "Expected <longitude> XML element, received %s", node->name);
            break;
        }

        xmlFree(content);
        content = xmlNodeGetContent(node);
        geo.lon = atof((const char *) content);

        value = icalvalue_new_geo(geo);

        break;
    }

    case ICAL_PERIOD_VALUE: {
        struct icalperiodtype p;

        p.start = p.end = icaltime_null_time();
        p.duration = icaldurationtype_from_int(0);

        node = xmlFirstElementChild(xtype);
        if (!node) {
            syslog(LOG_WARNING, "Missing <start> XML element");
            break;
        }
        else if (xmlStrcmp(node->name, BAD_CAST "start")) {
            syslog(LOG_WARNING,
                   "Expected <start> XML element, received %s", node->name);
            break;
        }

        content = xmlNodeGetContent(node);
        p.start = icaltime_from_string((const char *) content);
        if (icaltime_is_null_time(p.start)) break;

        node = xmlNextElementSibling(node);
        if (!node) {
            syslog(LOG_WARNING, "Missing <end> / <duration> XML element");
            break;
        }
        else if (!xmlStrcmp(node->name, BAD_CAST "end")) {
            xmlFree(content);
            content = xmlNodeGetContent(node);
            p.end = icaltime_from_string((const char *) content);
            if (icaltime_is_null_time(p.end)) break;
        }
        else if (!xmlStrcmp(node->name, BAD_CAST "duration")) {
            xmlFree(content);
            content = xmlNodeGetContent(node);
            p.duration = icaldurationtype_from_string((const char *) content);
            if (icaldurationtype_as_int(p.duration) == 0) break;
        }
        else {
            syslog(LOG_WARNING,
                   "Expected <end> / <duration> XML element, received %s",
                   node->name);
            break;
        }

        value = icalvalue_new_period(p);

        break;
    }

    case ICAL_RECUR_VALUE: {
        struct buf rrule = BUF_INITIALIZER;
        struct hash_table byrules;
        struct icalrecurrencetype rt;
        char *sep = "";

        construct_hash_table(&byrules, 10, 1);

        /* create an iCal RRULE string from xCal <recur> sub-elements */
        for (node = xmlFirstElementChild(xtype); node;
                node = xmlNextElementSibling(node)) {

            content = xmlNodeGetContent(node);
            if (!xmlStrncmp(node->name, BAD_CAST "by", 2)) {
                /* BY* rules can have a list of values -
                   assemble them using a hash table */
                struct buf *vals =
                    hash_lookup((const char *) node->name, &byrules);

                if (vals) {
                    /* append this value to existing list */
                    buf_printf(vals, ",%s", (char *) content);
                }
                else {
                    /* create new list with this valiue */
                    vals = xzmalloc(sizeof(struct buf));
                    buf_setcstr(vals, (char *) content);
                    hash_insert((char *) node->name, vals, &byrules);
                }
            }
            else {
                /* single value rpart */
                buf_printf(&rrule, "%s%s=%s", sep,
                           ucase((char *) node->name), (char *) content);
                sep = ";";
            }

            xmlFree(content);
            content = NULL;
        }

        /* append the BY* rules to RRULE buffer */
        hash_enumerate(&byrules,
                       (void (*)(const char*, void*, void*)) &append_byrule,
                       &rrule);
        free_hash_table(&byrules, NULL);

        /* parse our iCal RRULE string */
        rt = icalrecurrencetype_from_string(buf_cstring(&rrule));
        buf_free(&rrule);

        if (rt.freq != ICAL_NO_RECURRENCE) value = icalvalue_new_recur(rt);

        break;
    }

    case ICAL_REQUESTSTATUS_VALUE: {
        struct icalreqstattype rst = { ICAL_UNKNOWN_STATUS, NULL, NULL };
        short maj, min;

        node = xmlFirstElementChild(xtype);
        if (!node) {
            syslog(LOG_WARNING, "Missing <code> XML element");
            break;
        }
        else if (xmlStrcmp(node->name, BAD_CAST "code")) {
            syslog(LOG_WARNING,
                   "Expected <code> XML element, received %s", node->name);
            break;
        }

        content = xmlNodeGetContent(node);
        if (sscanf((const char *) content, "%hd.%hd", &maj, &min) == 2) {
            rst.code = icalenum_num_to_reqstat(maj, min);
        }
        if (rst.code == ICAL_UNKNOWN_STATUS) {
            syslog(LOG_WARNING, "Unknown request-status code");
            break;
        }

        node = xmlNextElementSibling(node);
        if (!node) {
            syslog(LOG_WARNING, "Missing <description> XML element");
            break;
        }
        else if (xmlStrcmp(node->name, BAD_CAST "description")) {
            syslog(LOG_WARNING,
                   "Expected <description> XML element, received %s",
                   node->name);
            break;
        }

        xmlFree(content);
        content = xmlNodeGetContent(node);
        rst.desc = (const char *) content;

        node = xmlNextElementSibling(node);
        if (node) {
            if (xmlStrcmp(node->name, BAD_CAST "data")) {
                syslog(LOG_WARNING,
                       "Expected <data> XML element, received %s", node->name);
                break;
            }

            xmlFree(content);
            content = xmlNodeGetContent(node);
            rst.debug = (const char *) content;
        }

        value = icalvalue_new_requeststatus(rst);
        break;
    }

    case ICAL_UTCOFFSET_VALUE: {
        int n, utcoffset, hours, minutes, seconds = 0;
        char sign;

        content = xmlNodeGetContent(xtype);
        n = sscanf((const char *) content, "%c%02d:%02d:%02d",
                   &sign, &hours, &minutes, &seconds);

        if (n < 3) {
            syslog(LOG_WARNING, "Unexpected utc-offset format");
            break;
        }

        utcoffset = hours*3600 + minutes*60 + seconds;

        if (sign == '-') utcoffset = -utcoffset;

        value = icalvalue_new_utcoffset(utcoffset);
        break;
    }

    default:
        content = xmlNodeGetContent(xtype);
        value = icalvalue_new_from_string(kind, (const char *) content);
        break;
    }

    if (content) xmlFree(content);

    return value;
}
Beispiel #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;
}
Beispiel #9
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;
}
Beispiel #10
0
EXPORTED int dlist_parse(struct dlist **dlp, int parsekey,
                          struct protstream *in, const char *alt_reserve_base)
{
    struct dlist *dl = NULL;
    static struct buf kbuf;
    static struct buf vbuf;
    int c;

    /* handle the key if wanted */
    if (parsekey) {
        c = getastring(in, NULL, &kbuf);
        c = next_nonspace(in, c);
    }
    else {
        buf_setcstr(&kbuf, "");
        c = prot_getc(in);
    }

    /* connection dropped? */
    if (c == EOF) goto fail;

    /* check what sort of value we have */
    if (c == '(') {
        dl = dlist_newlist(NULL, kbuf.s);
        c = next_nonspace(in, ' ');
        while (c != ')') {
            struct dlist *di = NULL;
            prot_ungetc(c, in);
            c = dlist_parse(&di, 0, in, alt_reserve_base);
            if (di) dlist_stitch(dl, di);
            c = next_nonspace(in, c);
            if (c == EOF) goto fail;
        }
        c = prot_getc(in);
    }
    else if (c == '%') {
        /* no whitespace allowed here */
        c = prot_getc(in);
        if (c == '(') {
            dl = dlist_newkvlist(NULL, kbuf.s);
            c = next_nonspace(in, ' ');
            while (c != ')') {
                struct dlist *di = NULL;
                prot_ungetc(c, in);
                c = dlist_parse(&di, 1, in, alt_reserve_base);
                if (di) dlist_stitch(dl, di);
                c = next_nonspace(in, c);
                if (c == EOF) goto fail;
            }
        }
        else if (c == '{') {
            struct message_guid tmp_guid;
            static struct buf pbuf, gbuf;
            unsigned size = 0;
            const char *fname;
            const char *part;
            c = getastring(in, NULL, &pbuf);
            if (c != ' ') goto fail;
            c = getastring(in, NULL, &gbuf);
            if (c != ' ') goto fail;
            c = getuint32(in, &size);
            if (c != '}') goto fail;
            c = prot_getc(in);
            if (c == '\r') c = prot_getc(in);
            if (c != '\n') goto fail;
            if (!message_guid_decode(&tmp_guid, gbuf.s)) goto fail;
            part = alt_reserve_base ? alt_reserve_base : pbuf.s;
            if (reservefile(in, part, &tmp_guid, size, &fname)) goto fail;
            dl = dlist_setfile(NULL, kbuf.s, pbuf.s, &tmp_guid, size, fname);
            /* file literal */
        }
        else {
            /* unknown percent type */
            goto fail;
        }
        c = prot_getc(in);
    }
    else if (c == '{') {
        prot_ungetc(c, in);
        /* could be binary in a literal */
        c = getbastring(in, NULL, &vbuf);
        dl = dlist_setmap(NULL, kbuf.s, vbuf.s, vbuf.len);
    }
    else if (c == '\\') { /* special case for flags */
        prot_ungetc(c, in);
        c = getastring(in, NULL, &vbuf);
        dl = dlist_setflag(NULL, kbuf.s, vbuf.s);
    }
    else {
        prot_ungetc(c, in);
        c = getnastring(in, NULL, &vbuf);
        dl = dlist_setatom(NULL, kbuf.s, vbuf.s);
    }

    /* success */
    *dlp = dl;
    return c;

fail:
    dlist_free(&dl);
    return EOF;
}
Beispiel #11
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;
}