Пример #1
0
void handle_request(void) {
    const char *param;
    char *argp;
    unsigned int plen;

    msg_new_id();
    verb = VERB_UNSUP;
    param = FCGX_GetParam("REQUEST_METHOD", envp);
    if(param) {
	plen = strlen(param);
	switch(plen) {
	case 3:
	    if(!memcmp(param, "GET", 4))
		verb = VERB_GET;
	    else if(!memcmp(param, "PUT", 4))
		verb = VERB_PUT;
	    break;
	case 4:
	    if(!memcmp(param, "HEAD", 5))
		verb = VERB_HEAD;
	    else if(!memcmp(param, "POST", 5))
		verb = VERB_POST;
	    break;
	case 6:
	    if(!memcmp(param, "DELETE", 7))
		verb = VERB_DELETE;
	    break;
	case 7:
	    if(!memcmp(param, "OPTIONS", 8)) {
		CGI_PUTS("Allow: GET,HEAD,OPTIONS,PUT,DELETE\r\nContent-Length: 0\r\n\r\n");
		return;
	    }
	    break;
	}
    }
    if(verb == VERB_UNSUP)
	quit_errmsg(405, "Method Not Allowed");

    if(content_len()<0 || (verb != VERB_PUT && content_len()))
	quit_errmsg(400, "Invalid Content-Length: must be positive and method must be PUT");

    param = FCGX_GetParam("REQUEST_URI", envp);
    if(!param)
	quit_errmsg(400, "No URI provided");
    plen = strlen(param);
    if(*param != '/')
	quit_errmsg(400, "URI must start with /");
    if(plen > sizeof(reqbuf) - 1)
	quit_errmsg(414, "URL too long: request line must be <8k");

    do {
	param++;
	plen--;
    } while(*param == '/');

    memcpy(reqbuf, param, plen+1);
    argp = memchr(reqbuf, '?', plen);
    nargs = 0;
    if(argp) {
	unsigned int argslen = plen - (argp - reqbuf);
	plen = argp - reqbuf;
	do {
	    *argp = '\0';
	    argp++;
	    argslen--;
	} while(*argp == '?');
	if(!argslen)
	    argp = NULL;
	else {
	    do {
		char *nextarg;
		if(nargs >= MAX_ARGS)
		    quit_errmsg(414, "Too many parameters");
		nextarg = memchr(argp, '&', argslen);
		if(nextarg) {
		    do {
			*nextarg = '\0';
			nextarg++;
		    } while(*nextarg == '&');
		}
		if(*argp) {
		    if(!(args[nargs] = inplace_urldecode(argp, 0, 0, NULL)))
			quit_errmsg(400, "Invalid URL encoding");
		    if(utf8_validate_len(args[nargs]) < 0)
			quit_errmsg(400, "Parameters with invalid utf-8 encoding");
		    nargs++;
		}
		argslen -= nextarg - argp;
		argp = nextarg;
	    } while (argp);
	}
    }

    while(plen && reqbuf[plen-1] == '/') {
	plen--;
	reqbuf[plen] = '\0';
    }

    path = memchr(reqbuf, '/', plen);
    if(path) {
	do {
	    *path = '\0';
	    path ++;
	} while(*path == '/');
	if(!*path)
	    path = NULL;
    }
    volume = *reqbuf ? reqbuf : NULL;

    int forbidden = 0;
    if((volume && !inplace_urldecode(volume, '/', 0, &forbidden)) || (path && !inplace_urldecode(path, '/', '/', &forbidden))) {
        if (forbidden)
            quit_errmsg(400, "Volume or path with forbidden %2f or %00");
        else
            quit_errmsg(400, "Invalid URL encoding");
    }

    int vlen = volume ? utf8_validate_len(volume) : 0;
    int flen = path ? utf8_validate_len(path) : 0;

    if (vlen < 0 || flen < 0)
       quit_errmsg(400, "URL with invalid utf-8 encoding");

    if (is_reserved()) {
        /* No UTF8 used on reserved volumes, allow higher limit.
         * Otherwise we hit the 512 limit with batch requests already */
        if (path && strlen(path) > SXLIMIT_MAX_FILENAME_LEN * 12) {
            msg_set_reason("Path too long: filename must be <%d characters (%ld)",
                           SXLIMIT_MAX_FILENAME_LEN*12+ 1, strlen(path));
            quit_errmsg(414, msg_get_reason());
        }
    } else {
        if (flen > SXLIMIT_MAX_FILENAME_LEN) {
            msg_set_reason("Path too long: filename must be <%d UTF8 characters (%d)",
                           SXLIMIT_MAX_FILENAME_LEN + 1, flen);
            quit_errmsg(414, msg_get_reason());
        }
    }

    if (volume && strlen(volume) > SXLIMIT_MAX_VOLNAME_LEN) {
        msg_set_reason("Volume name too long: must be <= %d bytes", SXLIMIT_MAX_VOLNAME_LEN);
        quit_errmsg(414, msg_get_reason());
    }

    if(!EVP_DigestInit(&body_ctx, EVP_sha1()))
	quit_errmsg(500, "Failed to initialize crypto engine");
    HMAC_CTX_init(&hmac_ctx);

    authed = AUTH_NOTAUTH;
    role = PRIV_NONE;
    auth_begin();

    if(has_priv(PRIV_CLUSTER) && sx_hashfs_uses_secure_proto(hashfs) != is_https() &&
       !sx_storage_is_bare(hashfs)) {
        /* programmed nodes: must obey cluster SSL mode
         * unprogrammed nodes: can use SSL instead of non-SSL,
         *  it is the cluster's responsibility to initiate programming via SSL,
         *  as the unprogrammed node would accept both         *
         * */
        WARN("hashfs use-ssl: %d, https: %d, is_bare: %d",
              sx_hashfs_uses_secure_proto(hashfs), is_https(),
              sx_storage_is_bare(hashfs));
	quit_errmsg(403, sx_hashfs_uses_secure_proto(hashfs) ? "Cluster operations require SECURE mode" : "Cluster operations require INSECURE mode");
    }

    int dc = sx_hashfs_distcheck(hashfs);
    if(dc < 0) {
	CRIT("Failed to reload distribution");
	/* MODHDIST: should die here */
    }

    if(!volume)
	cluster_ops();
    else if(!path)
	volume_ops();
    else
	file_ops();

    if(authed == AUTH_BODYCHECKING)
	WARN("FIXME: Security fail");

    HMAC_CTX_cleanup(&hmac_ctx);
    EVP_MD_CTX_cleanup(&body_ctx);
}
Пример #2
0
void handle_request(worker_type_t wtype) {
    const char *param, *p_method, *p_uri;
    char *argp;
    unsigned int plen;
    int cluster_readonly = 0, s2sreq = 0;

    if(sx_hashfs_cluster_get_mode(hashfs, &cluster_readonly)) {
        CRIT("Failed to get cluster operating mode");
        quit_errmsg(500, "Internal error: failed to check cluster operating mode");
    }

    if(sx_hashfs_distcheck(hashfs) < 0) {
	CRIT("Failed to reload distribution");
	quit_errmsg(503, "Internal error: failed to load distribution");
    }

    if(sx_hashfs_is_orphan(hashfs))
	quit_errmsg(410, "This node is no longer a cluster member");

    msg_new_id();
    verb = VERB_UNSUP;
    p_method = FCGX_GetParam("REQUEST_METHOD", envp);
    if(p_method) {
	plen = strlen(p_method);
	switch(plen) {
	case 3:
	    if(!memcmp(p_method, "GET", 4))
		verb = VERB_GET;
	    else if(!memcmp(p_method, "PUT", 4))
		verb = VERB_PUT;
	    break;
	case 4:
	    if(!memcmp(p_method, "HEAD", 5))
		verb = VERB_HEAD;
	    else if(!memcmp(p_method, "POST", 5))
		verb = VERB_POST;
	    break;
	case 6:
	    if(!memcmp(p_method, "DELETE", 7))
		verb = VERB_DELETE;
	    break;
	case 7:
	    if(!memcmp(p_method, "OPTIONS", 8)) {
		CGI_PUTS("Allow: GET,HEAD,OPTIONS,PUT,DELETE\r\nContent-Length: 0\r\n\r\n");
		return;
	    }
	    break;
	}
    }
    if(verb == VERB_UNSUP)
	quit_errmsg(405, "Method Not Allowed");

    if(content_len()<0 || (verb != VERB_PUT && content_len()))
	quit_errmsg(400, "Invalid Content-Length: must be positive and method must be PUT");

    p_uri = param = FCGX_GetParam("REQUEST_URI", envp);
    if(!p_uri)
	quit_errmsg(400, "No URI provided");
    plen = strlen(p_uri);
    if(*p_uri != '/')
	quit_errmsg(400, "URI must start with /");
    if(plen > sizeof(reqbuf) - 1)
	quit_errmsg(414, "URL too long: request line must be <8k");

    do {
	param++;
	plen--;
    } while(*param == '/');

    if(!strncmp(param, ".s2s/", lenof(".s2s/"))) {
	param += lenof(".s2s/");
	plen -= lenof(".s2s/");
	while(*param == '/') {
	    param++;
	    plen--;
	}
	s2sreq = 1;
    }
    if(wtype == WORKER_S2S && !s2sreq)
	WARN("Misconfiguration detected. Please make sure your restricted-socket config option is properly set.");
    /* FIXME: we could detect the opposite kind of mismatch
     * at the cost of extra complications in the wtype definition
     * I prefer to privilege simplicity at this point */

    memcpy(reqbuf, param, plen+1);
    argp = memchr(reqbuf, '?', plen);
    nargs = 0;
    if(argp) {
	unsigned int argslen = plen - (argp - reqbuf);
	plen = argp - reqbuf;
	do {
	    *argp = '\0';
	    argp++;
	    argslen--;
	} while(*argp == '?');
	if(!argslen)
	    argp = NULL;
	else {
	    do {
		char *nextarg;
		if(nargs >= MAX_ARGS)
		    quit_errmsg(414, "Too many parameters");
		nextarg = memchr(argp, '&', argslen);
		if(nextarg) {
		    do {
			*nextarg = '\0';
			nextarg++;
		    } while(*nextarg == '&');
		}
		if(*argp) {
		    if(!(args[nargs] = inplace_urldecode(argp, 0, 0, NULL, 1)))
			quit_errmsg(400, "Invalid URL encoding");
		    if(sxi_utf8_validate_len(args[nargs]) < 0)
			quit_errmsg(400, "Parameters with invalid utf-8 encoding");
		    nargs++;
		}
		argslen -= nextarg - argp;
		argp = nextarg;
	    } while (argp);
	}
    }

    while(plen && reqbuf[plen-1] == '/') {
	plen--;
	reqbuf[plen] = '\0';
    }

    path = memchr(reqbuf, '/', plen);
    if(path) {
	do {
	    *path = '\0';
	    path ++;
	} while(*path == '/');
	if(!*path)
	    path = NULL;
    }
    volume = *reqbuf ? reqbuf : NULL;

    int forbidden = 0;
    if((volume && !inplace_urldecode(volume, '/', 0, &forbidden, 0)) || (path && !inplace_urldecode(path, '/', '/', &forbidden, 0))) {
        if (forbidden)
            quit_errmsg(400, "Volume or path with forbidden %2f or %00");
        else
            quit_errmsg(400, "Invalid URL encoding");
    }

    int vlen = volume ? sxi_utf8_validate_len(volume) : 0;
    int flen = path ? strlen(path) : 0;

    if (vlen < 0 || flen < 0)
       quit_errmsg(400, "URL with invalid utf-8 encoding");

    if (is_reserved()) {
        /* No UTF8/url-encoding used on reserved volumes, allow higher limit.
         * Otherwise we hit the 1024 limit with batch requests already */
        if (path && strlen(path) > SXLIMIT_MAX_FILENAME_LEN * 3) {
            msg_set_reason("Path too long: filename must be <%d bytes (%ld)",
                           SXLIMIT_MAX_FILENAME_LEN*3+ 1, strlen(path));
            quit_errmsg(414, msg_get_reason());
        }
    } else {
        if (flen > SXLIMIT_MAX_FILENAME_LEN) {
            msg_set_reason("Path too long: filename must be <%d bytes (%d)",
                           SXLIMIT_MAX_FILENAME_LEN + 1, flen);
            quit_errmsg(414, msg_get_reason());
        }
    }

    if (volume && strlen(volume) > SXLIMIT_MAX_VOLNAME_LEN) {
        msg_set_reason("Volume name too long: must be <= %d bytes", SXLIMIT_MAX_VOLNAME_LEN);
        quit_errmsg(414, msg_get_reason());
    }

    body_ctx = sxi_md_init();
    if (!body_ctx || !sxi_sha1_init(body_ctx))
	quit_errmsg(500, "Failed to initialize crypto engine");
    hmac_ctx = sxi_hmac_sha1_init();
    if (!hmac_ctx)
        quit_errmsg(503, "Cannot initialize crypto library");

    authed = AUTH_NOTAUTH;
    role = PRIV_NONE;


    /* Begin auth check */
    uint8_t buf[AUTHTOK_BIN_LEN], key[AUTH_KEY_LEN];
    unsigned int blen = sizeof(buf);
    time_t reqdate, now;

    param = FCGX_GetParam("HTTP_AUTHORIZATION", envp);
    if(!param || strlen(param) != lenof("SKY ") + AUTHTOK_ASCII_LEN || strncmp(param, "SKY ", 4)) {
	if(volume) {
	    send_authreq();
	    return;
	}
	quit_home();
    }

    if(sxi_b64_dec_core(param+4, buf, &blen) || blen != sizeof(buf)) {
	send_authreq();
	return;
    }

    memcpy(user, buf, sizeof(user));
    memcpy(rhmac, buf+20, sizeof(rhmac));

    if(sx_hashfs_get_user_info(hashfs, user, &uid, key, &role, NULL, &user_quota) != OK) /* no such user */ {
	DEBUG("No such user: %s", param+4);
	send_authreq();
	return;
    }
    DEBUG("Request from uid %lld", (long long)uid);
    if(cluster_readonly && (verb == VERB_PUT || verb == VERB_DELETE) && !has_priv(PRIV_CLUSTER) && !has_priv(PRIV_ADMIN))
        quit_errmsg(503, "Cluster is in read-only mode");

    if(s2sreq && !has_priv(PRIV_CLUSTER)) {
	send_authreq();
	return;
    }

    if(!sxi_hmac_sha1_init_ex(hmac_ctx, key, sizeof(key))) {
	WARN("hmac_init failed");
	quit_errmsg(500, "Failed to initialize crypto engine");
    }

    if(!sxi_hmac_sha1_update_str(hmac_ctx, p_method))
	quit_errmsg(500, "Crypto error authenticating the request");

    if(!sxi_hmac_sha1_update_str(hmac_ctx, p_uri+1))
	quit_errmsg(500, "Crypto error authenticating the request");

    param = FCGX_GetParam("HTTP_DATE", envp);
    if(!param)
	quit_errmsg(400, "Missing Date: header");
    if(httpdate_to_time_t(param, &reqdate))
	quit_errmsg(400, "Date header in wrong format");
    now = time(NULL);
    if(reqdate < now - MAX_CLOCK_DRIFT * 60 || reqdate > now + MAX_CLOCK_DRIFT * 60) {
	CGI_PUTS("WWW-Authenticate: SKY realm=\"SXCLOCK\"\r\n");
	quit_errmsg(401, "Client clock drifted more than "STRIFY(MAX_CLOCK_DRIFT)" minutes");
    }
    if(!sxi_hmac_sha1_update_str(hmac_ctx, param))
	quit_errmsg(500, "Crypto error authenticating the request");

    if(!content_len()) {
	/* If no body is present, complete authentication now */
	uint8_t chmac[20];
	unsigned int chmac_len = 20;
	if(!sxi_hmac_sha1_update_str(hmac_ctx, "da39a3ee5e6b4b0d3255bfef95601890afd80709"))
	    quit_errmsg(500, "Crypto error authenticating the request");
	if(!sxi_hmac_sha1_final(hmac_ctx, chmac, &chmac_len))
	    quit_errmsg(500, "Crypto error authenticating the request");
	if(!hmac_compare(chmac, rhmac, sizeof(rhmac))) {
	    authed = AUTH_OK;
	} else {
	    /* WARN("auth mismatch"); */
	    send_authreq();
	    return;
	}
    } else /* Otherwise set it as pending */
	authed = AUTH_BODYCHECK;

    if(has_priv(PRIV_CLUSTER) && sx_hashfs_uses_secure_proto(hashfs) != is_https() &&
       !sx_storage_is_bare(hashfs)) {
        /* programmed nodes: must obey cluster SSL mode
         * unprogrammed nodes: can use SSL instead of non-SSL,
         *  it is the cluster's responsibility to initiate programming via SSL,
         *  as the unprogrammed node would accept both         *
         * */
        WARN("hashfs use-ssl: %d, https: %d, is_bare: %d",
              sx_hashfs_uses_secure_proto(hashfs), is_https(),
              sx_storage_is_bare(hashfs));
	quit_errmsg(403, sx_hashfs_uses_secure_proto(hashfs) ? "Cluster operations require SECURE mode" : "Cluster operations require INSECURE mode");
    }

    if(!volume)
	cluster_ops();
    else if(!path)
	volume_ops();
    else
	file_ops();

    if(authed == AUTH_BODYCHECKING)
	DEBUG("Bad request signature");

    sxi_hmac_sha1_cleanup(&hmac_ctx);
    sxi_md_cleanup(&body_ctx);
}
Пример #3
0
static void auth_begin(void) {
    const char *param = FCGX_GetParam("HTTP_AUTHORIZATION", envp);
    uint8_t buf[AUTHTOK_BIN_LEN], key[AUTH_KEY_LEN];
    unsigned int blen = sizeof(buf);
    time_t reqdate, now;

    if(!param || strlen(param) != lenof("SKY ") + AUTHTOK_ASCII_LEN || strncmp(param, "SKY ", 4))
	return;

    if(sxi_b64_dec_core(param+4, buf, &blen) || blen != sizeof(buf))
	return;
    memcpy(user, buf, sizeof(user));
    memcpy(rhmac, buf+20, sizeof(rhmac));

    if(sx_hashfs_get_user_info(hashfs, user, &uid, key, &role) != OK) /* no such user */ {
        WARN("No such user: %s", param+4);
       return;
    }
    DEBUG("Request from uid %lld", (long long)uid);

    if(!sxi_hmac_init_ex(&hmac_ctx, key, sizeof(key), EVP_sha1(), NULL)) {
        WARN("hmac_init failed");
        quit_errmsg(500, "Failed to initialize crypto engine");
    }

    param = FCGX_GetParam("REQUEST_METHOD", envp);
    if(!param)
	return;
    if(!hmac_update_str(&hmac_ctx, param))
        quit_errmsg(500, "Failed to initialize crypto engine");

    param = FCGX_GetParam("REQUEST_URI", envp);
    if(!param)
	return;
    if(!hmac_update_str(&hmac_ctx, param+1))
        quit_errmsg(500, "Failed to initialize crypto engine");

    param = FCGX_GetParam("HTTP_DATE", envp);
    if(!param)
        quit_errmsg(400, "Missing Date: header");
    if(httpdate_to_time_t(param, &reqdate))
        quit_errmsg(400, "Date header in wrong format");
    now = time(NULL);
    if(reqdate < now - MAX_CLOCK_DRIFT * 60 || reqdate > now + MAX_CLOCK_DRIFT * 60)
        quit_errmsg(400, "Client clock drifted more than "STRIFY(MAX_CLOCK_DRIFT)" minutes");
    if(!hmac_update_str(&hmac_ctx, param))
        quit_errmsg(500, "Failed to initialize crypto engine");

    if(!content_len()) {
	uint8_t chmac[20];
	unsigned int chmac_len = 20;
	if(!hmac_update_str(&hmac_ctx, "da39a3ee5e6b4b0d3255bfef95601890afd80709"))
            quit_errmsg(500, "Failed to initialize crypto engine");
	if(!sxi_hmac_final(&hmac_ctx, chmac, &chmac_len))
            quit_errmsg(500, "Failed to initialize crypto engine");
	if(!hmac_compare(chmac, rhmac, sizeof(rhmac))) {
	    authed = AUTH_OK;
	} else {
	    /* WARN("auth mismatch"); */
	}
	return;
    } else
	authed = AUTH_BODYCHECK;
}