Example #1
0
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;
}
Example #2
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));
}