int do_auth (SESSION * session) { /* * !! cr4zy 0pp3rtun1ty 2 s4v3 s0m3 pr3c10uz 3n3rgy !@)#&$!@# * * g00gl3 w4z b4d but th1z 1z w0rz3 - w4zt1ng CPU cycl3z f0r * th3 s4k3 0f w4zt1ng th3m & d3l4y1ng th3 l0g1n pr0c3zz, * * 3d1t0rz r3m4rk: * sk1pp1n' th1z l4m3 puzzl3 w0u1d b3 4n 4w3z0m3 * 1Ph0n3 b4tt3ry 0pt1m1z4t10n t3kn1qu3 !! * * b4ckgr0und th30ry * http://google.com/search?q=aura-nikander-leiwo-protocols00.pdf * http://google.com/search?q=005-candolin.pdf * */ puzzle_solve (session); /* * Compute HMAC over random data, public keys, * more random data and finally some username- * related parts * * Key is part of a digest computed in key_init() * */ auth_generate_auth_hmac (session, session->auth_hmac, sizeof (session->auth_hmac)); if (send_client_auth (session)) { DSFYDEBUG("do_auth(): send_client_auth() failed\n"); return -1; } if (read_server_auth_response (session)) { DSFYDEBUG("do_auth(): read_server_auth_response() failed\n"); return -1; } if (session->init_client_packet) buf_free(session->init_client_packet); if (session->init_server_packet) buf_free(session->init_server_packet); return 0; }
int login_process(struct login_ctx *l) { int connect_error, i; int ret = 0; socklen_t len; struct dns_srv_records *svc; struct addrinfo h, *ai; fd_set wfds; struct timeval tv; l->error = SP_LOGIN_ERROR_OK; switch(l->state) { case 0: /* Lookup service records in DNS */ if(l->service_records) dns_free_list(l->service_records); l->service_records = dns_get_service_list(SPOTIFY_SRV_HOSTNAME); if(l->service_records == NULL) { l->error = SP_LOGIN_ERROR_DNS_FAILURE; DSFYDEBUG("Failed to lookup Spotify service in DNS\n"); return -1; } l->state++; break; case 1: /* Pick a host in the list we have not yet tried */ for(svc = l->service_records; svc; svc = svc->next) if(svc->tried == 0) break; if(svc == NULL) { l->state = 0; l->error = SP_LOGIN_ERROR_NO_MORE_SERVERS; DSFYDEBUG("Run out of hostnames in SRV record list\n"); return -1; } /* Lookup available address records (IPv4/IPv6) for the host */ l->server_ai_skip = 0; l->server_ai_wait = 0; if(l->server_ai) { freeaddrinfo(l->server_ai); l->server_ai = NULL; } memset(&h, 0, sizeof(h)); h.ai_family = PF_UNSPEC; h.ai_socktype = SOCK_STREAM; h.ai_protocol = IPPROTO_TCP; ret = getaddrinfo(svc->host, svc->port, &h, &l->server_ai); if(ret < 0) { l->state = 0; l->error = SP_LOGIN_ERROR_DNS_FAILURE; DSFYDEBUG("Failed to lookup addresses for %s:%s\n", svc->host, svc->port); return -1; } DSFYDEBUG("Will connect to host %s:%s\n", svc->host, svc->port); l->state++; break; case 2: /* Pick a suitable address we have not yet tried to connect to */ i = 0; for(ai = l->server_ai; ai; ai = ai->ai_next) { if(i++ != l->server_ai_skip) continue; l->server_ai_skip++; if(ai->ai_family != AF_INET && ai->ai_family != AF_INET6) continue; break; } if(ai == NULL) { /* Out of addresses for this server, try next server */ DSFYDEBUG("Run out of addresses for this host\n"); l->state--; break; } if(l->sock != -1) { DSFYDEBUG("Closing already open socket %d\n", l->sock); #ifdef _WIN32 closesocket(l->sock); #else close(l->sock); #endif } l->sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if(l->sock < 0) { l->state = 0; l->error = SP_LOGIN_ERROR_SOCKET_ERROR; return -1; } DSFYDEBUG("Initiating connect()\n"); #ifdef _WIN32 i = 1; if(ioctlsocket(l->sock, FIONBIO, &i) < 0 || (connect(l->sock, (struct sockaddr *)ai->ai_addr, ai->ai_addrlen) < 0 && WSAGetLastError() != WSAEWOULDBLOCK)) { #else if((i = fcntl(l->sock, F_GETFL, 0)) < 0 || fcntl(l->sock, F_SETFL, i | O_NONBLOCK) < 0 || (connect(l->sock, (struct sockaddr *)ai->ai_addr, ai->ai_addrlen) < 0 && errno != EINPROGRESS)) { #endif /* XXX - We'll just try with the next server */ DSFYDEBUG("fcntl() or connect() failed with errno %d, will retry with next server\n", errno); l->state--; break; } l->state++; break; case 3: FD_ZERO(&wfds); FD_SET(l->sock, &wfds); #define WAIT_MS 200 #define MAX_WAIT_MS 3000 tv.tv_sec = 0; tv.tv_usec = WAIT_MS * 1000; l->server_ai_wait += WAIT_MS; ret = select(l->sock + 1, NULL, &wfds, NULL, &tv); if(ret < 0) { l->state = 0; l->error = SP_LOGIN_ERROR_SOCKET_ERROR; break; } else if(ret == 0) { if(l->server_ai_wait >= MAX_WAIT_MS) { DSFYDEBUG("Connection timeout out (waited %dms), retrying with next server in list\n", l->server_ai_wait); /* Connect timeout */ #ifdef _WIN32 closesocket(l->sock); #else close(l->sock); #endif l->sock = -1; /* Retry with next server */ l->state--; } break; } len = sizeof(connect_error); if((ret = getsockopt(l->sock, SOL_SOCKET, SO_ERROR, (char *)&connect_error, &len)) < 0) { l->state = 0; l->error = SP_LOGIN_ERROR_SOCKET_ERROR; break; } else if(connect_error != 0) { DSFYDEBUG("Connection failed with error %d, retrying with next server in list\n", connect_error); #ifdef _WIN32 closesocket(l->sock); #else close(l->sock); #endif l->sock = -1; /* Retry with next server */ l->state--; break; } DSFYDEBUG("Connected to server\n"); l->state++; break; case 4: ret = send_client_parameters(l); if(ret < 0) { if(l->error == SP_ERROR_OTHER_TRANSIENT || l->error == SP_LOGIN_ERROR_SOCKET_ERROR) { DSFYDEBUG("Retrying with next server\n"); l->state = 2; return 0; } else { l->state = 0; return -1; } } else l->state++; DSFYDEBUG("Initial packet sent, return value was %d, login error is %d\n", ret, l->error); break; case 5: /* Receive server parameters and eventually compute session key */ ret = receive_server_parameters(l); DSFYDEBUG("Recieved initial packet, return value was %d, login error is %d\n", ret, l->error); if(ret < 0) { if(l->error == SP_ERROR_OTHER_TRANSIENT || l->error == SP_LOGIN_ERROR_SOCKET_ERROR) { DSFYDEBUG("Retrying with next server\n"); l->state = 2; return 0; } else l->state = 0; } else l->state++; break; case 6: /* Compute a session key and authenticate the client */ auth_generate_auth_hash(l); key_init(l); /* Solve the puzzle, might take some time.. */ puzzle_solve(l); /* * Compute HMAC over the initial packets, a byte representing * the length of the random data, an unknown byte, two bytes * representing the length of the puzzle (value 0x0008), * four zero bytes, the random data (if any), and finally the * puzzle solution * * Key is part of a digest computed in key_init() * */ auth_generate_auth_hmac(l); l->state++; break; case 7: /* Authenticate the client */ ret = send_client_auth_packet(l); DSFYDEBUG("Sent auth packet, return value was %d, login error is %d\n", ret, l->error); if(ret < 0) l->state = 0; else l->state++; break; case 8: /* Read the server's authentication response */ ret = receive_server_auth_response(l); DSFYDEBUG("Got auth response, return value was %d, login error is %d\n", ret, l->error); if(ret == 0) return 1; l->state = 0; return -1; break; } return ret; } void login_export_session(struct login_ctx *login, int *sock, unsigned char *key_recv, unsigned char *key_send) { *sock = login->sock; memcpy(key_recv, login->key_recv, sizeof(login->key_recv)); memcpy(key_send, login->key_send, sizeof(login->key_send)); }