static int ws_compute_handshake(struct http_client *c, unsigned char *out) { char buffer[16]; md5_state_t ctx; // websocket handshake uint32_t number_1 = ws_read_key(client_get_header(c, "Sec-WebSocket-Key1")); uint32_t number_2 = ws_read_key(client_get_header(c, "Sec-WebSocket-Key2")); if(c->body_sz < 8) { /* we need at least 8 bytes */ return -1; } /* copy number_1, number_2, and last 8 bytes of the body. */ memcpy(buffer, &number_1, 4); memcpy(buffer + 4, &number_2, 4); memcpy(buffer + 8, c->body + c->body_sz - 8, 8); /* hash that buffer, that creates the handshake signature. */ md5_init(&ctx); md5_append(&ctx, (const md5_byte_t *)buffer, sizeof(buffer)); md5_finish(&ctx, out); return 0; }
int acl_match_client(struct acl *a, struct http_client *client, in_addr_t *ip) { /* check HTTP Basic Auth */ const char *auth; auth = client_get_header(client, "Authorization"); if(a->http_basic_auth) { if(auth && strncasecmp(auth, "Basic ", 6) == 0) { /* sent auth */ if(strcmp(auth + 6, a->http_basic_auth) != 0) { /* bad password */ return 0; } } else { /* no auth sent, required to match this ACL */ return 0; } } /* CIDR check. */ if(a->cidr.enabled == 0) { /* none given, all match */ return 1; } if(((*ip) & a->cidr.mask) == (a->cidr.subnet & a->cidr.mask)) { return 1; } return 0; }
static int ws_compute_handshake(struct http_client *c, char *out, size_t *out_sz) { unsigned char *buffer, sha1_output[20]; char magic[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; SHA1Context ctx; base64_encodestate b64_ctx; int pos, i; // websocket handshake const char *key = client_get_header(c, "Sec-WebSocket-Key"); size_t key_sz = key?strlen(key):0, buffer_sz = key_sz + sizeof(magic) - 1; buffer = calloc(buffer_sz, 1); // concatenate key and guid in buffer memcpy(buffer, key, key_sz); memcpy(buffer+key_sz, magic, sizeof(magic)-1); // compute sha-1 SHA1Reset(&ctx); SHA1Input(&ctx, buffer, buffer_sz); SHA1Result(&ctx); for(i = 0; i < 5; ++i) { // put in correct byte order before memcpy. ctx.Message_Digest[i] = ntohl(ctx.Message_Digest[i]); } memcpy(sha1_output, (unsigned char*)ctx.Message_Digest, 20); // encode `sha1_output' in base 64, into `out'. base64_init_encodestate(&b64_ctx); pos = base64_encode_block((const char*)sha1_output, 20, out, &b64_ctx); base64_encode_blockend(out + pos, &b64_ctx); // compute length, without \n *out_sz = strlen(out); if(out[*out_sz-1] == '\n') (*out_sz)--; free(buffer); return 0; }
int ws_handshake_reply(struct http_client *c) { int ret; unsigned char md5_handshake[16]; char *buffer = NULL, *p; const char *origin = NULL, *host = NULL; size_t origin_sz = 0, host_sz = 0, sz; char template0[] = "HTTP/1.1 101 Websocket Protocol Handshake\r\n" "Upgrade: WebSocket\r\n" "Connection: Upgrade\r\n" "Sec-WebSocket-Origin: "; /* %s */ char template1[] = "\r\n" "Sec-WebSocket-Location: ws://"; /* %s%s */ char template2[] = "\r\n" "Origin: http://"; /* %s */ char template3[] = "\r\n\r\n"; if((origin = client_get_header(c, "Origin"))) { origin_sz = strlen(origin); } if((host = client_get_header(c, "Host"))) { host_sz = strlen(host); } /* need those headers */ if(!origin || !origin_sz || !host || !host_sz || !c->path || !c->path_sz) { return -1; } if(ws_compute_handshake(c, &md5_handshake[0]) != 0) { /* failed to compute handshake. */ return -1; } sz = sizeof(template0)-1 + origin_sz + sizeof(template1)-1 + host_sz + c->path_sz + sizeof(template2)-1 + host_sz + sizeof(template3)-1 + sizeof(md5_handshake); p = buffer = malloc(sz); /* Concat all */ /* template0 */ memcpy(p, template0, sizeof(template0)-1); p += sizeof(template0)-1; memcpy(p, origin, origin_sz); p += origin_sz; /* template1 */ memcpy(p, template1, sizeof(template1)-1); p += sizeof(template1)-1; memcpy(p, host, host_sz); p += host_sz; memcpy(p, c->path, c->path_sz); p += c->path_sz; /* template2 */ memcpy(p, template2, sizeof(template2)-1); p += sizeof(template2)-1; memcpy(p, host, host_sz); p += host_sz; /* template3 */ memcpy(p, template3, sizeof(template3)-1); p += sizeof(template3)-1; memcpy(p, &md5_handshake[0], sizeof(md5_handshake)); ret = write(c->fd, buffer, sz); (void)ret; free(buffer); return 0; }