diff --git a/core/io.c b/core/io.c index 1b42ca59..dd7cc16a 100644 --- a/core/io.c +++ b/core/io.c @@ -1278,6 +1278,50 @@ int uwsgi_pass_cred(int fd, char *code, size_t code_len) { #endif } +int uwsgi_pass_cred2(int fd, char *code, size_t code_len, struct sockaddr *addr, size_t addr_len) { +#ifdef SCM_CREDENTIALS + struct msghdr cr_msg; + struct cmsghdr *cmsg; + struct iovec cr_iov; + void *cr_msg_control = uwsgi_calloc(CMSG_SPACE(sizeof(struct ucred))); + + cr_iov.iov_base = code; + cr_iov.iov_len = code_len; + + cr_msg.msg_name = addr; + cr_msg.msg_namelen = addr_len; + + cr_msg.msg_iov = &cr_iov; + cr_msg.msg_iovlen = 1; + + cr_msg.msg_flags = 0; + cr_msg.msg_control = cr_msg_control; + cr_msg.msg_controllen = CMSG_SPACE(sizeof(struct ucred)); + + cmsg = CMSG_FIRSTHDR(&cr_msg); + cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_CREDENTIALS; + + struct ucred *u = (struct ucred*) CMSG_DATA(cmsg); + u->pid = getpid(); + u->uid = getuid(); + u->gid = getgid(); + + if (sendmsg(fd, &cr_msg, 0) < 0) { + uwsgi_error("uwsgi_pass_cred2()/sendmsg()"); + free(cr_msg_control); + return -1; + } + + free(cr_msg_control); + return 0; +#else + return -1; +#endif +} + + int uwsgi_recv_cred(int fd, char *code, size_t code_len, pid_t *pid, uid_t *uid, gid_t *gid) { #ifdef SCM_CREDENTIALS struct iovec iov; @@ -1313,6 +1357,8 @@ int uwsgi_recv_cred(int fd, char *code, size_t code_len, pid_t *pid, uid_t *uid, goto clear; } + if (uwsgi_strncmp(code, code_len, iov.iov_base, iov.iov_len)) goto clear; + struct ucred *u = (struct ucred *) CMSG_DATA(cmsg); *pid = u->pid; *uid = u->uid; @@ -1327,3 +1373,53 @@ clear: return -1; #endif } + +ssize_t uwsgi_recv_cred2(int fd, char *buf, size_t buf_len, pid_t *pid, uid_t *uid, gid_t *gid) { +#ifdef SCM_CREDENTIALS + struct iovec iov; + ssize_t ret = -1; + + void *msg_control = uwsgi_calloc(CMSG_SPACE(sizeof(struct ucred))); + + iov.iov_base = buf; + iov.iov_len = buf_len; + + struct msghdr msg; + memset(&msg, 0, sizeof(msg)); + + msg.msg_name = NULL; + msg.msg_namelen = 0; + + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + msg.msg_control = msg_control; + msg.msg_controllen = CMSG_SPACE(sizeof(struct ucred)); + + ssize_t len = recvmsg(fd, &msg, 0); + if (len <= 0) { + uwsgi_error("uwsgi_recv_cred2()/recvmsg()"); + goto clear; + } + + struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); + if (!cmsg) goto clear; + + if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_CREDENTIALS) { + goto clear; + } + + struct ucred *u = (struct ucred *) CMSG_DATA(cmsg); + *pid = u->pid; + *uid = u->uid; + *gid = u->gid; + ret = len; + +clear: + free(msg_control); + return ret; +#else + return -1; +#endif +} + diff --git a/core/protocol.c b/core/protocol.c index 43fd6525..fe137898 100644 --- a/core/protocol.c +++ b/core/protocol.c @@ -18,81 +18,6 @@ static size_t get_content_length(char *buf, uint16_t size) { } -ssize_t send_udp_message(uint8_t modifier1, uint8_t modifier2, char *host, char *message, uint16_t message_size) { - - int fd; - struct sockaddr_in udp_addr; - struct sockaddr_un un_addr; - char *udp_port; - ssize_t ret; - - struct uwsgi_header *uh; - - udp_port = strchr(host, ':'); - if (udp_port) { - udp_port[0] = 0; - - fd = socket(AF_INET, SOCK_DGRAM, 0); - if (fd < 0) { - uwsgi_error("socket()"); - return -1; - } - - memset(&udp_addr, 0, sizeof(struct sockaddr_in)); - udp_addr.sin_family = AF_INET; - udp_addr.sin_port = htons(atoi(udp_port + 1)); - udp_addr.sin_addr.s_addr = inet_addr(host); - } - else { - fd = socket(AF_UNIX, SOCK_DGRAM, 0); - if (fd < 0) { - uwsgi_error("socket()"); - return -1; - } - - memset(&un_addr, 0, sizeof(struct sockaddr_un)); - un_addr.sun_family = AF_UNIX; - // use 102 as the magic number - strncat(un_addr.sun_path, host, 102); - - } - - if (message) { - uh = (struct uwsgi_header *) message; - } - else { - uh = (struct uwsgi_header *) uwsgi_malloc(4); - } - - uh->modifier1 = modifier1; -#ifdef __BIG_ENDIAN__ - uh->pktsize = uwsgi_swap16(message_size); -#else - uh->pktsize = message_size; -#endif - uh->modifier2 = modifier2; - - if (udp_port) { - ret = sendto(fd, (char *) uh, message_size + 4, 0, (struct sockaddr *) &udp_addr, sizeof(udp_addr)); - udp_port[0] = ':'; - } - else { - ret = sendto(fd, (char *) uh, message_size + 4, 0, (struct sockaddr *) &un_addr, sizeof(un_addr)); - } - if (ret < 0) { - uwsgi_error("send_udp_message()/sendto()"); - } - close(fd); - - if ((char *) uh != message) { - free(uh); - } - - return ret; - -} - - int uwsgi_read_response(int fd, struct uwsgi_header *uh, int timeout, char **buf) { char *ptr = (char *) uh; diff --git a/core/socket.c b/core/socket.c index bd0577fe..4e0217c8 100644 --- a/core/socket.c +++ b/core/socket.c @@ -1928,6 +1928,19 @@ found: return up; } +int uwsgi_socket_passcred(int fd) { +#ifdef SO_PASSCRED + int optval = 1; + if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) < 0) { + uwsgi_error("uwsgi_socket_passcred()/setsockopt()"); + return -1; + } + return 0; +#else + return -1; +#endif +} + void uwsgi_protocols_register() { uwsgi_register_protocol("uwsgi", uwsgi_proto_uwsgi_setup); uwsgi_register_protocol("puwsgi", uwsgi_proto_puwsgi_setup); diff --git a/core/subscription.c b/core/subscription.c index 15869e08..3de36f71 100644 --- a/core/subscription.c +++ b/core/subscription.c @@ -49,6 +49,23 @@ static void uwsgi_subscription_sni_check(struct uwsgi_subscribe_slot *current_sl } #endif +int uwsgi_subscription_credentials_check(struct uwsgi_subscribe_slot *slot, struct uwsgi_subscribe_req *usr) { + struct uwsgi_string_list *usl = NULL; + uwsgi_foreach(usl, uwsgi.subscriptions_credentials_check_dir) { + char *filename = uwsgi_concat2n(usl->value, usl->len, slot->key, slot->keylen); + struct stat st; + int ret = stat(filename, &st); + free(filename); + if (ret != 0) continue; + uwsgi_log("%d %d %d %d\n", st.st_uid, usr->uid, st.st_gid, usr->gid); + if (st.st_uid != usr->uid) continue; + if (st.st_gid != usr->gid) continue; + // accepted... + return 1; + } + return 0; +} + struct uwsgi_subscribe_slot *uwsgi_get_subscribe_slot(struct uwsgi_subscribe_slot **slot, char *key, uint16_t keylen) { if (keylen > 0xff) @@ -393,6 +410,11 @@ struct uwsgi_subscribe_node *uwsgi_add_subscribe_node(struct uwsgi_subscribe_slo return NULL; } #endif + + if (uwsgi.subscriptions_credentials_check_dir && !uwsgi_subscription_credentials_check(current_slot, usr)) { + return NULL; + } + node = current_slot->nodes; while (node) { if (!uwsgi_strncmp(node->name, node->len, usr->address, usr->address_len)) { @@ -501,6 +523,13 @@ struct uwsgi_subscribe_node *uwsgi_add_subscribe_node(struct uwsgi_subscribe_slo #endif current_slot->keylen = usr->keylen; memcpy(current_slot->key, usr->key, usr->keylen); + if (uwsgi.subscriptions_credentials_check_dir) { + if (!uwsgi_subscription_credentials_check(current_slot, usr)) { + free(current_slot); + return NULL; + } + } + current_slot->key[usr->keylen] = 0; current_slot->hits = 0; #ifdef UWSGI_SSL @@ -556,6 +585,62 @@ struct uwsgi_subscribe_node *uwsgi_add_subscribe_node(struct uwsgi_subscribe_slo } +static void send_subscription(char *host, char *message, uint16_t message_size) { + + int fd; + struct sockaddr_in udp_addr; + struct sockaddr_un un_addr; + ssize_t ret; + + char *udp_port = strchr(host, ':'); + if (udp_port) { + udp_port[0] = 0; + + fd = socket(AF_INET, SOCK_DGRAM, 0); + if (fd < 0) { + uwsgi_error("send_subscription()/socket()"); + return; + } + + memset(&udp_addr, 0, sizeof(struct sockaddr_in)); + udp_addr.sin_family = AF_INET; + udp_addr.sin_port = htons(atoi(udp_port + 1)); + udp_addr.sin_addr.s_addr = inet_addr(host); + } + else { + fd = socket(AF_UNIX, SOCK_DGRAM, 0); + if (fd < 0) { + uwsgi_error("send_subscription()/socket()"); + return; + } + + memset(&un_addr, 0, sizeof(struct sockaddr_un)); + un_addr.sun_family = AF_UNIX; + // use 102 as the magic number + strncat(un_addr.sun_path, host, 102); + + } + + if (udp_port) { + ret = sendto(fd, message, message_size, 0, (struct sockaddr *) &udp_addr, sizeof(udp_addr)); + udp_port[0] = ':'; + } + else { + if (uwsgi.subscriptions_use_credentials) { + // could be useless as internally the socket could add them automagically + ret = uwsgi_pass_cred2(fd, message, message_size, (struct sockaddr *) &un_addr, sizeof(un_addr)); + } + else { + ret = sendto(fd, message, message_size, 0, (struct sockaddr *) &un_addr, sizeof(un_addr)); + } + } + if (ret < 0) { + uwsgi_error("send_subscription()/sendto()"); + } + + close(fd); +} + void uwsgi_send_subscription(char *udp_address, char *key, size_t keysize, uint8_t modifier1, uint8_t modifier2, uint8_t cmd, char *socket_name, char *sign, char *sni_key, char *sni_crt, char *sni_ca) { @@ -612,7 +697,10 @@ void uwsgi_send_subscription(char *udp_address, char *key, size_t keysize, uint8 if (uwsgi_buffer_append_keyval(ub, "sni_ca", 6, sni_ca, strlen(sni_ca))) goto end; } - send_udp_message(224, cmd, udp_address, ub->buf, ub->pos - 4); + // add uwsgi header + if (uwsgi_buffer_set_uh(ub, 224, cmd)) goto end; + + send_subscription(udp_address, ub->buf, ub->pos); end: uwsgi_buffer_destroy(ub); } diff --git a/core/uwsgi.c b/core/uwsgi.c index 37d4f566..d6737cc2 100644 --- a/core/uwsgi.c +++ b/core/uwsgi.c @@ -577,6 +577,8 @@ static struct uwsgi_option uwsgi_base_options[] = { {"subscriptions-sign-check", required_argument, 0, "set digest algorithm and certificate directory for secured subscription system", uwsgi_opt_scd, NULL, UWSGI_OPT_MASTER}, {"subscriptions-sign-check-tolerance", required_argument, 0, "set the maximum tolerance (in seconds) of clock skew for secured subscription system", uwsgi_opt_set_int, &uwsgi.subscriptions_sign_check_tolerance, UWSGI_OPT_MASTER}, #endif + {"subscriptions-credentials-check", required_argument, 0, "add a directory to search for subscriptions key credentials", uwsgi_opt_add_string_list, &uwsgi.subscriptions_credentials_check_dir, UWSGI_OPT_MASTER}, + {"subscriptions-use-credentials", no_argument, 0, "enable management of SCM_CREDENTIALS in subscriptions UNIX sockets", uwsgi_opt_true, &uwsgi.subscriptions_use_credentials, 0}, {"subscription-algo", required_argument, 0, "set load balancing algorithm for the subscription system", uwsgi_opt_ssa, NULL, 0}, {"subscription-dotsplit", no_argument, 0, "try to fallback to the next part (dot based) in subscription key", uwsgi_opt_true, &uwsgi.subscription_dotsplit, 0}, {"subscribe-to", required_argument, 0, "subscribe to the specified subscription server", uwsgi_opt_add_string_list, &uwsgi.subscriptions, UWSGI_OPT_MASTER}, diff --git a/plugins/corerouter/cr_common.c b/plugins/corerouter/cr_common.c index 09716951..76f7a4d7 100644 --- a/plugins/corerouter/cr_common.c +++ b/plugins/corerouter/cr_common.c @@ -4,7 +4,7 @@ common functions for various routers (fastrouter, http...) */ -#include "../../uwsgi.h" +#include extern struct uwsgi_server uwsgi; @@ -79,6 +79,11 @@ void uwsgi_corerouter_setup_sockets(struct uwsgi_corerouter *ucr) { } else { ugs->fd = bind_to_unix_dgram(ugs->name); + if (uwsgi.subscriptions_use_credentials) { + if (uwsgi_socket_passcred(ugs->fd)) { + exit(1); + } + } } uwsgi_socket_nb(ugs->fd); } @@ -113,10 +118,17 @@ void uwsgi_corerouter_manage_subscription(struct uwsgi_corerouter *ucr, int id, int i; struct uwsgi_subscribe_req usr; char bbuf[4096]; + ssize_t len = -1; - ssize_t len = recv(ugs->fd, bbuf, 4096, 0); + memset(&usr, 0, sizeof(struct uwsgi_subscribe_req)); + + if (uwsgi.subscriptions_use_credentials) { + len = uwsgi_recv_cred2(ugs->fd, bbuf, 4096, &usr.pid, &usr.uid, &usr.gid); + } + else { + len = recv(ugs->fd, bbuf, 4096, 0); + } if (len > 0) { - memset(&usr, 0, sizeof(struct uwsgi_subscribe_req)); uwsgi_hooked_parse(bbuf + 4, len - 4, corerouter_manage_subscription, &usr); if (usr.sign_len > 0) { // calc the base size diff --git a/uwsgi.h b/uwsgi.h index f2c01628..b8cc67bf 100644 --- a/uwsgi.h +++ b/uwsgi.h @@ -2145,6 +2145,9 @@ struct uwsgi_server { const EVP_MD *subscriptions_sign_check_md; #endif + struct uwsgi_string_list *subscriptions_credentials_check_dir; + int subscriptions_use_credentials; + struct uwsgi_dyn_dict *static_maps; struct uwsgi_dyn_dict *static_maps2; struct uwsgi_dyn_dict *check_static; @@ -2947,8 +2950,6 @@ uint32_t uwsgi_swap32(uint32_t); uint64_t uwsgi_swap64(uint64_t); #endif -ssize_t send_udp_message(uint8_t, uint8_t, char *, char *, uint16_t); - int uwsgi_parse_request(int, struct wsgi_request *, int); int uwsgi_parse_vars(struct wsgi_request *); @@ -3210,6 +3211,10 @@ struct uwsgi_subscribe_req { char *sni_ca; uint16_t sni_ca_len; + + pid_t pid; + uid_t uid; + gid_t gid; }; void uwsgi_nuclear_blast(); @@ -4649,7 +4654,10 @@ int uwsgi_run(void); int uwsgi_is_connected(int); int uwsgi_pass_cred(int, char *, size_t); +int uwsgi_pass_cred2(int, char *, size_t, struct sockaddr *, size_t); int uwsgi_recv_cred(int, char *, size_t, pid_t *, uid_t *, gid_t *); +ssize_t uwsgi_recv_cred2(int, char *, size_t, pid_t *, uid_t *, gid_t *); +int uwsgi_socket_passcred(int); #ifdef __cplusplus }