Example #1
0
// Returns decoded size; sets remain; resizes bufp/remain if needed.
// On error, returns SASL error code (negative)
int
_sasl_decode_at_offset(sasl_conn_t *ctx, char **bufp, size_t offset, int r, int *remain)
{
	const char *out;
	unsigned outlen;
	int s;
	uint32_t sasl_len;

	// this is pretty fragile. at the moment we expect to slurp exactly one
	// kerb reply in a given read(). fixing it will require adding another
	// buffer to the read code when the connection is sasl'd, which is more
	// work for now (it's not clear that hadoop supports ssf > 0 anyway).
	if (r < 4)
		abort();

	sasl_len = _be32dec(*bufp + offset);
	if ((unsigned)r - 4 != sasl_len)
		abort();

	s = sasl_decode(ctx, *bufp + offset + 4, sasl_len, &out, &outlen);
	if (s != SASL_OK)
		return s;

	if (outlen > (unsigned)*remain) {
		*remain = outlen + 4*1024;
		*bufp = realloc(*bufp, offset + *remain);
		ASSERT(*bufp);
	}

	memcpy(*bufp + offset, out, outlen);
	return outlen;
}
Example #2
0
EXPORT_SYM const char *
hdfs_namenode_authenticate_full(struct hdfs_namenode *n, const char *username,
	const char *real_user)
{
	const char *error = NULL;
	struct hdfs_object *header;
	struct hdfs_heap_buf hbuf = { 0 };
	char *inh = NULL, preamble[12];
	size_t preamble_len;

	_lock(&n->nn_lock);
	ASSERT(!n->nn_authed);
	ASSERT(n->nn_sock != -1);

	memset(preamble, 0, sizeof(preamble));
	if (n->nn_proto == HDFS_NN_v1) {
		sprintf(preamble, "hrpc\x04%c",
		    (n->nn_kerb == HDFS_NO_KERB)? 0x50 : 0x51);
		preamble_len = 6;

		header = hdfs_authheader_new_ext(n->nn_proto, username,
		    real_user, HDFS_NO_KERB);
	} else if (n->nn_proto == HDFS_NN_v2) {
		/* HDFSv2 has used both version 7 (2.0.0-2.0.2) and 8 (2.0.3+). */
		sprintf(preamble, "hrpc%c%c", 8 /* XXX Configurable? */,
		    (n->nn_kerb == HDFS_NO_KERB)? 0x50 : 0x51);
		/* There is a zero at the end: */
		preamble_len = 7;

		header = hdfs_authheader_new_ext(n->nn_proto, username,
		    real_user, n->nn_kerb);
	} else if (n->nn_proto == HDFS_NN_v2_2) {
		memcpy(preamble, "hrpc\x09", 5);
		preamble[5] = 0;
		preamble[6] = (n->nn_kerb == HDFS_NO_KERB)? 0 : -33;
		preamble_len = 7;

		header = hdfs_authheader_new_ext(n->nn_proto, username,
		    real_user, n->nn_kerb);
		_authheader_set_clientid(header, n->nn_client_id);
	} else {
		ASSERT(false);
	}

	// Serialize the connection header object (I am speaking ClientProtocol
	// and this is my username)
	hdfs_object_serialize(&hbuf, header);
	hdfs_object_free(header);

	if (n->nn_kerb == HDFS_NO_KERB) {
		// Prefix the header object with the protocol preamble
		hbuf.buf = realloc(hbuf.buf, hbuf.size + preamble_len);
		ASSERT(hbuf.buf);
		memmove(hbuf.buf + preamble_len, hbuf.buf, hbuf.used);
		memcpy(hbuf.buf, preamble, preamble_len);
		hbuf.used += preamble_len;
		hbuf.size += preamble_len;
	} else {
		/*
		 * XXX This is probably totally wrong for HDFSv2+. They start
		 * using protobufs at this point to wrap the SASL packets.
		 *
		 * To be fair, it's probably broken for HDFSv1 too :). I need
		 * to find a kerberized HDFS to test against.
		 */
		int r;
		sasl_interact_t *interactions = NULL;
		const char *out, *mechusing;
		unsigned outlen;
		struct iovec iov[3];
		uint32_t inlen;
		const uint32_t SWITCH_TO_SIMPLE_AUTH = (uint32_t)-1;

		uint8_t in[4], zero[4] = { 0 };

		do {
			r = sasl_client_start(n->nn_sasl_ctx, "GSSAPI",
			    &interactions, &out, &outlen, &mechusing);

			if (r == SASL_INTERACT)
				_sasl_interacts(interactions);
		} while (r == SASL_INTERACT);

		if (r != SASL_CONTINUE) {
			error = sasl_errstring(r, NULL, NULL);
			goto out;
		}

		// Send prefix, first auth token
		_be32enc(in, outlen);
		iov[0].iov_base = preamble;
		iov[0].iov_len = preamble_len;
		iov[1].iov_base = in;
		iov[1].iov_len = 4;
		iov[2].iov_base = __DECONST(void *, out);
		iov[2].iov_len = outlen;

		error = _writev_all(n->nn_sock, iov, 3);
		if (error)
			goto out;

		do {
			// read success / error status
			error = _read_all(n->nn_sock, in, 4);
			if (error)
				goto out;

			if (memcmp(in, zero, 4)) {
				// error. exception will be next on the wire,
				// but let's skip it.
				error = "Got error from server, bailing";
				goto out;
			}

			// read token len
			error = _read_all(n->nn_sock, in, 4);
			if (error)
				goto out;
			inlen = _be32dec(in);

			if (inlen == SWITCH_TO_SIMPLE_AUTH) {
				if (n->nn_kerb == HDFS_REQUIRE_KERB) {
					error = "Server tried to drop kerberos but "
					    "client requires it";
					goto out;
				}
				goto send_header;
			}

			// read token
			if (inh)
				free(inh);
			inh = NULL;
			if (inlen > 0) {
				inh = malloc(inlen);
				ASSERT(inh);
				error = _read_all(n->nn_sock, inh, inlen);
				if (error)
					goto out;
			}

			out = NULL;
			outlen = 0;
			r = sasl_client_step(n->nn_sasl_ctx, inh,
			    inlen, &interactions, &out, &outlen);

			if (r == SASL_INTERACT)
				_sasl_interacts(interactions);

			if (r == SASL_CONTINUE ||
			    (r == SASL_OK && out != NULL)) {
				_be32enc(in, outlen);
				iov[0].iov_base = in;
				iov[0].iov_len = 4;
				iov[1].iov_base = __DECONST(void *, out);
				iov[1].iov_len = outlen;

				error = _writev_all(n->nn_sock, iov, 2);
				if (error)
					goto out;
			}
		} while (r == SASL_INTERACT || r == SASL_CONTINUE);

		if (r != SASL_OK) {
			error = sasl_errstring(r, NULL, NULL);
			goto out;
		}

		// sasl connection established
		n->nn_sasl_ssf = _getssf(n->nn_sasl_ctx);
send_header:
		if (n->nn_sasl_ssf > 0)
			_sasl_encode_inplace(n->nn_sasl_ctx, &hbuf);
	}

	// send auth header
	error = _write_all(n->nn_sock, hbuf.buf, hbuf.used);
	n->nn_authed = true;

out:
	if (inh)
		free(inh);
	_unlock(&n->nn_lock);
	free(hbuf.buf);

	return error;
}