static void auth_generate_auth_hmac(struct login_ctx *l) { struct buf* buf = buf_new(); buf_append_data(buf, l->client_parameters->ptr, l->client_parameters->len); buf_append_data(buf, l->server_parameters->ptr, l->server_parameters->len); buf_append_u8(buf, 0); /* random data length */ buf_append_u8(buf, 0); /* unknown */ buf_append_u16(buf, 8); /* puzzle solution length */ buf_append_u32(buf, 0); /* unknown */ /* <-- random data would go here */ buf_append_data(buf, l->puzzle_solution, 8); #ifdef DEBUG_LOGIN hexdump8x32 ("auth_generate_auth_hmac, HMAC message", buf->ptr, buf->len); hexdump8x32 ("auth_generate_auth_hmac, HMAC key", l->key_hmac, sizeof (l->key_hmac)); #endif sha1_hmac(l->key_hmac, sizeof(l->key_hmac), buf->ptr, buf->len, l->auth_hmac); #ifdef DEBUG_LOGIN hexdump8x32 ("auth_generate_auth_hmac, HMAC digest", l->auth_hmac, sizeof(l->auth_hmac)); #endif buf_free(buf); }
void auth_generate_auth_hmac (SESSION * session, unsigned char *auth_hmac, unsigned int mac_len) { (void)mac_len; struct buf* buf = buf_new(); buf_append_data(buf, session->init_client_packet->ptr, session->init_client_packet->len); buf_append_data(buf, session->init_server_packet->ptr, session->init_server_packet->len); buf_append_u8(buf, 0); /* random data length */ buf_append_u8(buf, 0); /* unknown */ buf_append_u16(buf, 8); /* puzzle solution length */ buf_append_u32(buf, 0); /* unknown */ /* <-- random data would go here */ buf_append_data(buf, session->puzzle_solution, 8); #ifdef DEBUG_LOGIN hexdump8x32 ("auth_generate_auth_hmac, HMAC message", buf->ptr, buf->len); hexdump8x32 ("auth_generate_auth_hmac, HMAC key", session->key_hmac, sizeof (session->key_hmac)); #endif sha1_hmac ( session->key_hmac, sizeof (session->key_hmac), buf->ptr, buf->len, auth_hmac); #ifdef DEBUG_LOGIN hexdump8x32 ("auth_generate_auth_hmac, HMAC digest", auth_hmac, mac_len); #endif buf_free(buf); }
int send_client_auth (SESSION * session) { int ret; struct buf* buf = buf_new(); buf_append_data(buf, session->auth_hmac, 20); buf_append_u8(buf, 0); /* random data length */ buf_append_u8(buf, 0); /* unknown */ buf_append_u16(buf, 8); /* puzzle solution length */ buf_append_u32(buf, 0); /* <-- random data would go here */ buf_append_data (buf, session->puzzle_solution, 8); #ifdef DEBUG_LOGIN hexdump8x32 ("send_client_auth, second client packet", buf->ptr, buf->len); #endif ret = send(session->ap_sock, buf->ptr, buf->len, 0); if (ret <= 0) { DSFYDEBUG("send_client_auth(): connection lost\n"); buf_free(buf); return -1; } else if (ret != buf->len) { DSFYDEBUG("send_client_auth(): only wrote %d of %d bytes\n", ret, buf->len); buf_free(buf); return -1; } buf_free(buf); return 0; }
int send_client_initial_packet (SESSION * session) { int ret; unsigned int len_idx; struct buf* b = buf_new(); buf_append_u16 (b, 3); /* protocol version */ len_idx = b->len; buf_append_u16(b, 0); /* packet length - updated later */ buf_append_u32(b, 0x00000300); /* unknown */ buf_append_u32(b, 0x00030c00); /* unknown */ buf_append_u32(b, session->client_revision); buf_append_u32(b, 0); /* unknown */ buf_append_u32(b, 0x01000000); /* unknown */ buf_append_data(b, session->client_id, 4); buf_append_u32(b, 0); /* unknown */ buf_append_data (b, session->client_random_16, 16); buf_append_data (b, session->my_pub_key, 96); BN_bn2bin (session->rsa->n, session->rsa_pub_exp); buf_append_data (b, session->rsa_pub_exp, sizeof(session->rsa_pub_exp)); buf_append_u8 (b, 0); /* length of random data */ buf_append_u8 (b, session->username_len); buf_append_u16(b, 0x0100); /* unknown */ /* <-- random data would go here */ buf_append_data (b, (unsigned char *) session->username, session->username_len); buf_append_u8 (b, 0x40); /* unknown */ /* * Update length bytes * */ b->ptr[len_idx] = (b->len >> 8) & 0xff; b->ptr[len_idx+1] = b->len & 0xff; #ifdef DEBUG_LOGIN hexdump8x32 ("initial client packet", b->ptr, b->len); #endif ret = send (session->ap_sock, b->ptr, b->len, 0); if (ret <= 0) { DSFYDEBUG("connection lost\n"); buf_free(b); return -1; } else if (ret != b->len) { DSFYDEBUG("only wrote %d of %d bytes\n", ret, b->len); buf_free(b); return -1; } /* save initial server packet for auth hmac generation */ session->init_client_packet = b; return 0; }
static int osfy_image_callback(CHANNEL *ch, unsigned char *payload, unsigned short len) { struct image_ctx *image_ctx = (struct image_ctx *)ch->private; switch(ch->state) { case CHANNEL_DATA: buf_append_data(image_ctx->image->data, payload, len); break; case CHANNEL_ERROR: DSFYDEBUG("Got a channel ERROR, retrying within %d seconds\n", IMAGE_RETRY_TIMEOUT); buf_free(image_ctx->image->data); image_ctx->image->data = NULL; /* Reset timeout so the request can be retried */ image_ctx->req->next_timeout = get_millisecs() + IMAGE_RETRY_TIMEOUT*1000; break; case CHANNEL_END: /* We simply assume we're always getting a JPEG image back */ image_ctx->image->format = SP_IMAGE_FORMAT_JPEG; image_ctx->image->is_loaded = 1; image_ctx->image->error = SP_ERROR_OK; request_set_result(image_ctx->session, image_ctx->req, SP_ERROR_OK, image_ctx->image); free(image_ctx); break; default: break; } return 0; }
static int http_complete_login (RESTSESSION * r) { struct buf *response; if (r->client->state != CLIENT_STATE_IDLE_CONNECTED) return http_reply_need_auth (r); response = buf_new (); buf_append_data (response, "logged in!\n", 11); return http_reply (r, 200, response); }
int cmd_send_cache_hash (SESSION * session) { int ret; struct buf* buf = buf_new(); buf_append_data(buf, session->cache_hash, sizeof (session->cache_hash)); ret = packet_write (session, 0x0f, buf->ptr, buf->len); DSFYDEBUG ("packet_write() returned %d\n", ret); buf_free(buf); return ret; }
static int http_reply_need_auth (RESTSESSION * r) { struct buf *b; int ret; char buf[256]; b = buf_new(); strcpy (buf, "HTTP/1.1 401\r\n"); buf_append_data (b, buf, strlen (buf)); strcpy (buf, "WWW-Authenticate: Basic realm=\"Spotify\"\r\n"); buf_append_data (b, buf, strlen (buf)); buf_append_data (b, content_type, strlen (content_type)); buf_append_data (b, connection_close, strlen (connection_close)); strcpy (buf, "Content-Length: 0\r\n"); buf_append_data (b, buf, strlen (buf)); buf_append_data (b, "\r\n", 2); ret = 0; if (send (r->socket, b->ptr, b->len, 0) != b->len) ret = -1; buf_free (b); http_cleanup (r); return ret; }
static int http_reply (RESTSESSION * r, int status, struct buf * response) { struct buf *b; int ret; char respcode[100]; char content_len[256]; b = buf_new (); sprintf (respcode, "HTTP/1.1 %03d\r\n", status); buf_append_data (b, respcode, strlen (respcode)); buf_append_data (b, content_type, strlen (content_type)); buf_append_data (b, connection_close, strlen (connection_close)); sprintf (content_len, "Content-Length: %d\r\n", response->len); buf_append_data (b, content_len, strlen (content_len)); buf_append_data (b, "\r\n", 2); buf_append_data (b, response->ptr, response->len); ret = 0; if (send (r->socket, b->ptr, b->len, 0) != b->len) ret = -1; buf_free (b); buf_free (response); http_cleanup (r); return ret; }
static int send_client_auth_packet(struct login_ctx *l) { int ret; struct buf* buf = buf_new(); buf_append_data(buf, l->auth_hmac, 20); buf_append_u8(buf, 0); /* random data length */ buf_append_u8(buf, 0); /* unknown */ buf_append_u16(buf, 8); /* puzzle solution length */ buf_append_u32(buf, 0); /* <-- random data would go here */ buf_append_data (buf, l->puzzle_solution, 8); #ifdef DEBUG_LOGIN hexdump8x32("send_client_auth_packet, second client packet", buf->ptr, buf->len); #endif ret = send(l->sock, buf->ptr, buf->len, 0); if (ret <= 0) { DSFYDEBUG("Connection was reset\n"); buf_free(buf); l->error = SP_LOGIN_ERROR_SOCKET_ERROR; return -1; } else if (ret != buf->len) { DSFYDEBUG("Only wrote %d of %d bytes\n", ret, buf->len); buf_free(buf); l->error = SP_LOGIN_ERROR_SOCKET_ERROR; return -1; } buf_free(buf); return 0; }
int gw_getplaylist (SPOTIFYSESSION * s, char *playlist_hex_id) { unsigned char id[17]; hex_ascii_to_bytes (playlist_hex_id, id, 17); s->output = buf_new (); s->output_len = 0; buf_append_data (s->output, "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n<playlist>\n", 51); return cmd_getplaylist (s->session, id, PLAYLIST_CURRENT, gw_getplaylist_result_callback, (void *) s); }
static int receive_server_parameters(struct login_ctx *l) { char buf[512]; unsigned char padlen, username_len; unsigned short chalen[4]; int normalize; int ret; struct buf* save = buf_new(); /* read 2 status bytes */ ret = block_read(l->sock, l->server_random_16, 2); if(ret < 2) { DSFYDEBUG("Failed to read status bytes, return value was %d, errno is %d\n", ret, errno); l->error = SP_LOGIN_ERROR_SOCKET_ERROR; return -1; } if (l->server_random_16[0] != 0) { DSFYDEBUG("Bad response: %#02x %#02x\n", l->server_random_16[0], l->server_random_16[1]); switch (l->server_random_16[1]) { case 1: /* client upgrade required */ l->error = SP_LOGIN_ERROR_UPGRADE_REQUIRED; return -1; case 3: /* user not found */ l->error = SP_LOGIN_ERROR_USER_NOT_FOUND; return -1; case 4: /* account has been disabled */ l->error = SP_LOGIN_ERROR_USER_BANNED; return -1; case 6: /* you need to complete your account details */ l->error = SP_LOGIN_ERROR_USER_NEED_TO_COMPLETE_DETAILS; return -1; case 9: /* country mismatch */ l->error = SP_LOGIN_ERROR_USER_COUNTRY_MISMATCH; return -1; default: /* unknown error */ l->error = SP_LOGIN_ERROR_OTHER_PERMANENT; return -1; } } /* read remaining 14 random bytes */ ret = block_read(l->sock, l->server_random_16 + 2, 14); if(ret < 14) { DSFYDEBUG("Failed to read server random\n"); l->error = SP_LOGIN_ERROR_SOCKET_ERROR; return -1; } buf_append_data(save, l->server_random_16, 16); /* read public key */ ret = block_read(l->sock, l->remote_pub_key, 96); if (ret != 96) { DSFYDEBUG("Failed to read 'remote_pub_key'\n"); l->error = SP_LOGIN_ERROR_SOCKET_ERROR; return -1; } buf_append_data(save, l->remote_pub_key, 96); /* read server blob */ ret = block_read(l->sock, buf, 256); if (ret != 256) { DSFYDEBUG("Failed to read 'random_256', got %d of 256 bytes\n", ret); l->error = SP_LOGIN_ERROR_SOCKET_ERROR; return -1; } buf_append_data(save, buf, 256); /* read salt */ ret = block_read(l->sock, l->salt, 10); if (ret != 10) { DSFYDEBUG("Failed to read 'salt'\n"); l->error = SP_LOGIN_ERROR_SOCKET_ERROR; return -1; } buf_append_data(save, l->salt, 10); /* read padding length */ ret = block_read(l->sock, &padlen, 1); if (ret != 1) { DSFYDEBUG("Failed to read 'padding length'\n"); l->error = SP_LOGIN_ERROR_SOCKET_ERROR; return -1; } assert (padlen > 0); buf_append_u8(save, padlen); /* read username length */ ret = block_read(l->sock, &username_len, 1); if (ret != 1) { DSFYDEBUG("Failed to read 'username_len'\n"); l->error = SP_LOGIN_ERROR_SOCKET_ERROR; return -1; } buf_append_u8(save, username_len); /* read challenge lengths */ ret = block_read(l->sock, chalen, 8); if (ret != 8) { DSFYDEBUG("Failed to read challenge lengths\n"); l->error = SP_LOGIN_ERROR_SOCKET_ERROR; return -1; } buf_append_data(save, chalen, 8); /* read packet padding */ ret = block_read(l->sock, buf, padlen); if (ret != padlen) { DSFYDEBUG("Failed to read 'padding'\n"); l->error = SP_LOGIN_ERROR_SOCKET_ERROR; return -1; } buf_append_data(save, buf, padlen); /* read username */ ret = block_read(l->sock, l->username, username_len); if (ret != username_len) { DSFYDEBUG("Failed to read 'username'\n"); l->error = SP_LOGIN_ERROR_SOCKET_ERROR; return -1; } buf_append_data(save, l->username, username_len); l->username[username_len] = 0; /* read puzzle challenge */ { int puzzle_len = ntohs(chalen[0]); int len1 = ntohs(chalen[1]); int len2 = ntohs(chalen[2]); int len3 = ntohs(chalen[3]); int totlen = puzzle_len + len1 + len2 + len3; struct buf* b = buf_new(); buf_extend(b, totlen); DSFYDEBUG("Reading a total of %d bytes puzzle challenge\n", totlen); ret = block_read(l->sock, b->ptr, totlen); if (ret != totlen) { DSFYDEBUG("Failed to read puzzle\n"); buf_free(b); l->error = SP_LOGIN_ERROR_SOCKET_ERROR; return -1; } buf_append_data(save, b->ptr, totlen); if (b->ptr[0] == 1) { l->puzzle_denominator = b->ptr[1]; memcpy(&normalize, b->ptr + 2, sizeof(int)); l->puzzle_magic = ntohl(normalize); } else { DSFYDEBUG("Unexpected puzzle challenge with first byte 0x%02x\n", b->ptr[0]); hexdump8x32("receive_server_parameters, puzzle", b->ptr, totlen); l->error = SP_LOGIN_ERROR_OTHER_PERMANENT; buf_free(b); return -1; } buf_free(b); } l->server_parameters = save; return 0; }
static int send_client_parameters(struct login_ctx *l) { int ret; unsigned char client_pub_key[96]; unsigned char rsa_pub_exp[128]; unsigned int len_idx; unsigned char bytevalue; struct buf* b = buf_new(); buf_append_u16 (b, 3); /* protocol version */ len_idx = b->len; buf_append_u16(b, 0); /* packet length - updated later */ buf_append_u32(b, 0); /* unknown */ buf_append_u32(b, 0x00030c00); /* unknown */ buf_append_u32(b, 99999); /* revision */ buf_append_u32(b, 0); /* unknown */ buf_append_u32(b, 0x01000000); /* unknown */ buf_append_data(b, "\x01\x04\x01\x01", 4); /* client ID */ buf_append_u32(b, 0); /* unknown */ /* Random bytes(?) */ RAND_bytes(l->client_random_16, 16); buf_append_data (b, l->client_random_16, 16); BN_bn2bin (l->dh->pub_key, client_pub_key); buf_append_data (b, client_pub_key, sizeof(client_pub_key)); BN_bn2bin (l->rsa->n, rsa_pub_exp); buf_append_data (b, rsa_pub_exp, sizeof(rsa_pub_exp)); buf_append_u8 (b, 0); /* length of random data */ bytevalue = strlen(l->username); buf_append_u8 (b, bytevalue); buf_append_u16(b, 0x0100); /* unknown */ /* <-- random data would go here */ DSFYDEBUG("Sending username '%s'\n", l->username); buf_append_data (b, (unsigned char *) l->username, strlen(l->username)); buf_append_u8 (b, 0x40); /* unknown */ /* * Update length bytes * */ b->ptr[len_idx] = (b->len >> 8) & 0xff; b->ptr[len_idx+1] = b->len & 0xff; ret = send(l->sock, b->ptr, b->len, 0); if (ret <= 0) { DSFYDEBUG("connection lost\n"); buf_free(b); l->error = SP_LOGIN_ERROR_SOCKET_ERROR; return -1; } else if (ret != b->len) { DSFYDEBUG("only wrote %d of %d bytes\n", ret, b->len); buf_free(b); l->error = SP_LOGIN_ERROR_SOCKET_ERROR; return -1; } /* save initial server packet for auth hmac generation */ l->client_parameters = b; return 0; }
int read_server_initial_packet (SESSION * session) { char buf[512]; unsigned char padlen; int ret; struct buf* save = buf_new(); /* read 2 status bytes */ ret = block_read(session->ap_sock, session->server_random_16, 2); if (ret < 2) { DSFYDEBUG("Failed to read status bytes\n"); DSFYDEBUG("Remote host was %s:%d\n", session->server_host, session->server_port); if (ret > 0) hexdump8x32 ("read_server_initial_packet, server_random_16", session->server_random_16, ret); return -90; } #ifdef DEBUG_LOGIN hexdump8x32 ("read_server_initial_packet, server_random_16", session->server_random_16, ret); #endif if (session->server_random_16[0] != 0) { DSFYDEBUG("Bad response: %#02x %#02x\n", session->server_random_16[0], session->server_random_16[1]); switch (session->server_random_16[1]) { case 1: /* client upgrade required */ return -11; case 3: /* user not found */ return -13; case 4: /* account has been disabled */ return -14; case 6: /* you need to complete your account details */ return -16; case 9: /* country mismatch */ return -19; default: /* unknown error */ return -91; } } /* read remaining 14 random bytes */ ret = block_read(session->ap_sock, session->server_random_16 + 2, 14); if (ret < 14) { DSFYDEBUG("Failed to read server random\n"); DSFYDEBUG("Remote host was %s:%d\n", session->server_host, session->server_port); if (ret > 0) hexdump8x32("read_server_initial_packet, server_random_16", session->server_random_16+2, ret); return -92; } buf_append_data(save, session->server_random_16, 16); /* read public key */ ret = block_read(session->ap_sock, session->remote_pub_key, 96); if (ret != 96) { DSFYDEBUG("Failed to read 'remote_pub_key'\n"); return -93; } buf_append_data(save, session->remote_pub_key, 96); #ifdef DEBUG_LOGIN hexdump8x32 ("read_server_initial_packet, server pub key", session->remote_pub_key, 96); #endif /* read server blob */ ret = block_read(session->ap_sock, session->random_256, 256); if (ret != 256) { DSFYDEBUG("Failed to read 'random_256', got %d of 256 bytes\n", ret); return -94; } buf_append_data(save, session->random_256, 256); #ifdef DEBUG_LOGIN hexdump8x32 ("read_server_initial_packet, random_256", session->random_256, 256); #endif /* read salt */ ret = block_read(session->ap_sock, session->salt, 10); if (ret != 10) { DSFYDEBUG("Failed to read 'salt'\n"); return -95; } buf_append_data(save, session->salt, 10); #ifdef DEBUG_LOGIN hexdump8x32 ("read_server_initial_packet, salt", session->salt, 10); #endif /* read padding length */ ret = block_read(session->ap_sock, &padlen, 1); if (ret != 1) { DSFYDEBUG("Failed to read 'padding length'\n"); return -96; } assert (padlen > 0); buf_append_u8(save, padlen); /* read username length */ ret = block_read(session->ap_sock, &session->username_len, 1); if (ret != 1) { DSFYDEBUG("Failed to read 'username_len'\n"); return -97; } buf_append_u8(save, session->username_len); /* read challenge lengths */ unsigned short chalen[4]; ret = block_read(session->ap_sock, chalen, 8); if (ret != 8) { DSFYDEBUG("Failed to read challenge lengths\n"); return -98; } buf_append_data(save, chalen, 8); /* read packet padding */ ret = block_read(session->ap_sock, buf, padlen); if (ret != padlen) { DSFYDEBUG("Failed to read 'padding'\n"); return -99; } buf_append_data(save, buf, padlen); #ifdef DEBUG_LOGIN hexdump8x32 ("read_server_initial_packet, padding", buf, padlen); #endif /* read username */ ret = block_read(session->ap_sock, session->username, session->username_len); if (ret != session->username_len) { DSFYDEBUG("Failed to read 'username'\n"); return -100; } buf_append_data(save, session->username, session->username_len); session->username[session->username_len] = 0; #ifdef DEBUG_LOGIN hexdump8x32 ("read_server_initial_packet, username", session->username, session->username_len); #endif /* read puzzle challenge */ { int puzzle_len = ntohs(chalen[0]); int len1 = ntohs(chalen[1]); int len2 = ntohs(chalen[2]); int len3 = ntohs(chalen[3]); int totlen = puzzle_len + len1 + len2 + len3; int normalize = 0; struct buf* b = buf_new(); buf_extend(b, totlen); ret = block_read(session->ap_sock, b->ptr, totlen); if (ret != totlen) { DSFYDEBUG("Failed to read puzzle\n"); buf_free(b); return -101; } buf_append_data(save, b->ptr, totlen); #ifdef DEBUG_LOGIN hexdump8x32("read_server_initial_packet, puzzle", b->ptr, totlen); #endif if (b->ptr[0] == 1) { session->puzzle_denominator = b->ptr[1]; memcpy(&normalize, b->ptr+2, sizeof(int)); session->puzzle_magic = ntohl(normalize); } else { DSFYDEBUG("Unexpected puzzle challenge\n"); hexdump8x32("read_server_initial_packet, puzzle", b->ptr, totlen); buf_free(b); return -102; } buf_free(b); } session->init_server_packet = save; return 0; }
int http_handle_request (RESTSESSION * r) { struct buf *b; char buf[512]; char *p; SPOTIFYSESSION *client; /* * r->httpreq->url has path that was requested * r->httpreq->authheader MIGHT be non-NULL and * have username:password in base64 * */ /* Default to process next command */ r->state = REST_STATE_LOAD_COMMAND; if ((client = spotify_find_http_client ()) == NULL) { /* Force auth if not sent or likely invalid */ if (!r->httpreq->authheader || strlen (r->httpreq->authheader) > 100) return http_reply_need_auth (r); memset (buf, 0, sizeof (buf)); b64decode (r->httpreq->authheader, buf); if ((p = strchr (buf, ':')) == NULL) { printf ("b64 decode failed '%s'\n", buf); return http_reply_need_auth (r); } *p++ = 0; strcpy (r->username, buf); strcpy (r->password, p); spotify_client_allocate (r); spotify_client_mark_for_http (r->client); if (r->client->state == CLIENT_STATE_IDLE_CONNECTED) return http_complete_login (r); r->state = REST_STATE_WAITING; r->httpreq->callback = http_complete_login; return 0; } if (0) { } else { b = buf_new (); sprintf (buf, "You're logged in as '%s' with password '%s'<br />\n", r->username, r->password); if (client) buf_append_data(b, buf, strlen (buf)); sprintf (buf, "The requested URL '%s' was not found!\n", r->httpreq->url); buf_append_data(b, buf, strlen (buf)); return http_reply (r, 404, b); } return 0; }