void _sasl_encode_inplace(sasl_conn_t *ctx, struct hdfs_heap_buf *b) { const char *out; unsigned outlen; int r; r = sasl_encode(ctx, b->buf, b->used, &out, &outlen); if (r != SASL_OK) { fprintf(stderr, "sasl_encode: %s\n", sasl_errstring(r, NULL, NULL)); abort(); } free(b->buf); b->buf = malloc(4 + outlen); ASSERT(b->buf); // copy in length-prefixed encoded bits _be32enc(b->buf, outlen); memcpy(b->buf + 4, out, outlen); b->used = b->size = 4 + outlen; }
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; }