diff --git a/common.h b/common.h index 2172e287..6cabfba5 100644 --- a/common.h +++ b/common.h @@ -299,8 +299,7 @@ typedef struct { uint32_t airplay_statusflags; char *airplay_device_id; // for the Bonjour advertisement and the GETINFO PList char *airplay_pin; // non-NULL, 4 char PIN, if required for pairing - char *airplay_pk; // "pk" string in the Bonjour advertisement - char *airplay_pi; // UUID in the Bonjour advertisement and the GETINFO Plist + char *airplay_pi; // UUID in the Bonjour advertisement and the GETINFO Plist char *airplay_gid; // UUID in the Bonjour advertisement -- initially the same as the pi #endif } shairport_cfg; diff --git a/pair_ap b/pair_ap index 1b9954cb..1a83fd5c 160000 --- a/pair_ap +++ b/pair_ap @@ -1 +1 @@ -Subproject commit 1b9954cba18631c9671595fca85565308e8b0972 +Subproject commit 1a83fd5c501bbf8390b2b34bfb94c59f9889a98a diff --git a/rtsp.c b/rtsp.c index 74a1249a..067749a8 100644 --- a/rtsp.c +++ b/rtsp.c @@ -886,6 +886,19 @@ static ssize_t write_encrypted(rtsp_conn_info *conn, const void *buf, size_t cou free(encrypted); return count; } + +static void pkString_make(char *str, size_t str_size, const char *device_id) { + uint8_t public_key[32]; + if (str_size < 2 * sizeof(public_key) + 1) { + warn("Insufficient string size"); + str[0] = '\0'; + return; + } + pair_public_key_get(PAIR_SERVER_HOMEKIT, public_key, device_id); + char *ptr = str; + for (size_t i = 0; i < sizeof(public_key); i++) + ptr += sprintf(ptr, "%02x", public_key[i]); +} #endif ssize_t read_from_rtsp_connection(rtsp_conn_info *conn, void *buf, size_t count) { @@ -1238,11 +1251,14 @@ void handle_get_info(__attribute((unused)) rtsp_conn_info *conn, rtsp_message *r plist_dict_set_item(response_plist, "name", plist_new_string(config.service_name)); char *vs = get_version_string(); plist_dict_set_item(response_plist, "model", plist_new_string(vs)); + free(vs); + char pkString[128]; + pkString_make(pkString, sizeof(pkString), config.airplay_device_id); + plist_dict_set_item(response_plist, "pk", plist_new_string(pkString)); plist_to_bin(response_plist, &resp->content, &resp->contentlength); if (resp->contentlength == 0) debug(1, "GET /info Stage 1: response bplist not created!"); plist_free(response_plist); - free(vs); } msg_add_header(resp, "Content-Type", "application/x-apple-binary-plist"); debug_log_rtsp_message(2, "GET /info Stage 1 Response:", resp); @@ -1264,6 +1280,9 @@ void handle_get_info(__attribute((unused)) rtsp_conn_info *conn, rtsp_message *r char *vs = get_version_string(); plist_dict_set_item(response_plist, "model", plist_new_string(vs)); free(vs); + char pkString[128]; + pkString_make(pkString, sizeof(pkString), config.airplay_device_id); + plist_dict_set_item(response_plist, "pk", plist_new_string(pkString)); plist_to_bin(response_plist, &resp->content, &resp->contentlength); plist_free(response_plist); msg_add_header(resp, "Content-Type", "application/x-apple-binary-plist"); @@ -1420,8 +1439,8 @@ void handle_get(__attribute((unused)) rtsp_conn_info *conn, __attribute((unused) #ifdef CONFIG_AIRPLAY_2 void handle_pair_verify(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) { int ret; - uint8_t *body; - size_t body_len; + uint8_t *body = NULL; + size_t body_len = 0; struct pair_result *result; debug(2, "Connection %d: pair-verify Content-Length %d", conn->connection_number, req->contentlength); @@ -1430,12 +1449,16 @@ void handle_pair_verify(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *r conn->ap2_control_pairing.verify_ctx = pair_verify_new(PAIR_SERVER_HOMEKIT, NULL, NULL, NULL, config.airplay_device_id); if (!conn->ap2_control_pairing.verify_ctx) { debug(1, "Error creating verify context"); + resp->respcode = 500; // Internal Server Error + goto out; } } ret = pair_verify(&body, &body_len, conn->ap2_control_pairing.verify_ctx, (const uint8_t *)req->content, req->contentlength); if (ret < 0) { debug(1, pair_verify_errmsg(conn->ap2_control_pairing.verify_ctx)); + resp->respcode = 470; // Connection Authorization Required + goto out; } ret = pair_verify_result(&result, conn->ap2_control_pairing.verify_ctx); @@ -1443,19 +1466,22 @@ void handle_pair_verify(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *r conn->ap2_control_pairing.cipher_ctx = pair_cipher_new(PAIR_SERVER_HOMEKIT, 2, result->shared_secret, result->shared_secret_len); if (!conn->ap2_control_pairing.cipher_ctx) { debug(1, "Error setting up rtsp control channel ciphering\n"); + goto out; } } + out: resp->content = (char *)body; // these will be freed when the data is sent resp->contentlength = body_len; - msg_add_header(resp, "Content-Type", "application/octet-stream"); + if (body) + msg_add_header(resp, "Content-Type", "application/octet-stream"); debug_log_rtsp_message(2, "pair-verify response", resp); } void handle_pair_setup(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *resp) { int ret; - uint8_t *body; - size_t body_len; + uint8_t *body = NULL; + size_t body_len = 0; struct pair_result *result; debug(2, "Connection %d: pair-setup Content-Length %d", conn->connection_number, req->contentlength); @@ -1464,16 +1490,16 @@ void handle_pair_setup(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *re conn->ap2_control_pairing.setup_ctx = pair_setup_new(PAIR_SERVER_HOMEKIT, config.airplay_pin, NULL, NULL, config.airplay_device_id); if (!conn->ap2_control_pairing.setup_ctx) { debug(1, "Error creating setup context"); - resp->respcode = 451; - return; + resp->respcode = 500; // Internal Server Error + goto out; } } ret = pair_setup(&body, &body_len, conn->ap2_control_pairing.setup_ctx, (const uint8_t *)req->content, req->contentlength); if (ret < 0) { debug(1, pair_setup_errmsg(conn->ap2_control_pairing.setup_ctx)); - resp->respcode = 451; - return; + resp->respcode = 470; // Connection Authorization Required + goto out; } ret = pair_setup_result(NULL, &result, conn->ap2_control_pairing.setup_ctx); @@ -1483,14 +1509,15 @@ void handle_pair_setup(rtsp_conn_info *conn, rtsp_message *req, rtsp_message *re conn->ap2_control_pairing.cipher_ctx = pair_cipher_new(PAIR_SERVER_HOMEKIT, 2, result->shared_secret, result->shared_secret_len); if (!conn->ap2_control_pairing.cipher_ctx) { debug(1, "Error setting up rtsp control channel ciphering\n"); - resp->respcode = 451; - return; + goto out; } } + out: resp->content = (char *)body; // these will be freed when the data is sent resp->contentlength = body_len; - msg_add_header(resp, "Content-Type", "application/octet-stream"); + if (body) + msg_add_header(resp, "Content-Type", "application/octet-stream"); debug_log_rtsp_message(2, "pair-setup response", resp); } @@ -4098,7 +4125,7 @@ void *rtsp_listen_loop(__attribute((unused)) void *arg) { #ifdef CONFIG_AIRPLAY_2 *p++ = "srcvers=366.0"; char deviceIdString[64]; - snprintf(deviceIdString, sizeof(deviceIdString) - 1, "deviceid=%s", config.airplay_device_id); + snprintf(deviceIdString, sizeof(deviceIdString), "deviceid=%s", config.airplay_device_id); *p++ = deviceIdString; // features is a 64 bit number, least significant 32 bits char featuresString[64]; @@ -4106,8 +4133,7 @@ void *rtsp_listen_loop(__attribute((unused)) void *arg) { features_hi = (features_hi >> 32) & 0xffffffff; uint64_t features_lo = config.airplay_features; features_lo = features_lo & 0xffffffff; - // @mikebrady I don't think the -1 here (and other places) is necessary - snprintf(featuresString, sizeof(featuresString) - 1, "features=0x%" PRIx64 ",0x%" PRIx64 "", + snprintf(featuresString, sizeof(featuresString), "features=0x%" PRIx64 ",0x%" PRIx64 "", features_lo, features_hi); *p++ = featuresString; char statusflagsString[32]; @@ -4119,14 +4145,15 @@ void *rtsp_listen_loop(__attribute((unused)) void *arg) { *p++ = "fv=p20.78000.12"; *p++ = "model=SPS"; char piString[64]; - snprintf(piString, sizeof(piString) - 1, "pi=%s", config.airplay_pi); + snprintf(piString, sizeof(piString), "pi=%s", config.airplay_pi); *p++ = piString; char gidString[64]; - snprintf(gidString, sizeof(gidString) - 1, "gid=%s", config.airplay_gid); + snprintf(gidString, sizeof(gidString), "gid=%s", config.airplay_gid); *p++ = gidString; *p++ = "gcgl=0"; char pkString[128]; - snprintf(pkString, sizeof(pkString) - 1, "pk=%s", config.airplay_pk); + snprintf(pkString, sizeof(pkString), "pk="); + pkString_make(pkString + strlen("pk="), sizeof(pkString) - strlen("pk="), config.airplay_device_id); *p++ = pkString; *p++ = NULL; #else diff --git a/shairport.c b/shairport.c index 303a282a..afe9ab4a 100644 --- a/shairport.c +++ b/shairport.c @@ -1591,21 +1591,6 @@ int main(int argc, char **argv) { } else debug(1, "Started in Airplay 2 mode!"); - // now make up a 32-byte "pk" string from random numbers - const ssize_t pk_size = 32; - char pk_bytes[pk_size]; - char pk_string[pk_size * 2 + 1]; - randombytes_buf(pk_bytes, pk_size); // using libsodium - char *obfp = pk_string; - int obfc; - for (obfc = 0; obfc < pk_size; obfc++) { - snprintf(obfp, 3, "%02X", pk_bytes[obfc]); - obfp += 2; - }; - *obfp = 0; - debug(1, "pk string: \"%s\"", pk_string); - config.airplay_pk = strdup(pk_string); - // now generate a UUID // from https://stackoverflow.com/questions/51053568/generating-a-random-uuid-in-c // with thanks