static ADS_STATUS ads_sasl_gensec_unwrap(struct ads_saslwrap *wrap) { struct gensec_security *gensec_security = talloc_get_type_abort(wrap->wrap_private_data, struct gensec_security); NTSTATUS nt_status; DATA_BLOB unwrapped, wrapped; TALLOC_CTX *frame = talloc_stackframe(); wrapped = data_blob_const(wrap->in.buf + 4, wrap->in.ofs - 4); nt_status = gensec_unwrap(gensec_security, frame, &wrapped, &unwrapped); if (!NT_STATUS_IS_OK(nt_status)) { TALLOC_FREE(frame); return ADS_ERROR_NT(nt_status); } if (wrapped.length < unwrapped.length) { TALLOC_FREE(frame); return ADS_ERROR_NT(NT_STATUS_INTERNAL_ERROR); } /* copy the wrapped blob to the right location */ memcpy(wrap->in.buf + 4, unwrapped.data, unwrapped.length); /* set how many bytes must be written to the underlying socket */ wrap->in.left = unwrapped.length; wrap->in.ofs = 4; TALLOC_FREE(frame); return ADS_SUCCESS; }
static NTSTATUS common_gensec_decrypt_buffer(struct gensec_security *gensec, char *buf) { NTSTATUS status; size_t buf_len = smb_len_nbt(buf) + 4; /* Don't forget the 4 length bytes. */ DATA_BLOB in_buf, out_buf; TALLOC_CTX *frame; if (buf_len < 8) { return NT_STATUS_BUFFER_TOO_SMALL; } frame = talloc_stackframe(); in_buf = data_blob_const(buf + 8, buf_len - 8); status = gensec_unwrap(gensec, frame, &in_buf, &out_buf); if (!NT_STATUS_IS_OK(status)) { DEBUG(0,("common_gensec_decrypt_buffer: gensec_unwrap failed. Error %s\n", nt_errstr(status))); TALLOC_FREE(frame); return status; } if (out_buf.length > in_buf.length) { DEBUG(0,("common_gensec_decrypt_buffer: gensec_unwrap size (%u) too large (%u) !\n", (unsigned int)out_buf.length, (unsigned int)in_buf.length )); TALLOC_FREE(frame); return NT_STATUS_INVALID_PARAMETER; } memcpy(buf + 8, out_buf.data, out_buf.length); /* Reset the length and overwrite the header. */ smb_setlen_nbt(buf, out_buf.length + 4); TALLOC_FREE(frame); return NT_STATUS_OK; }
/* * These functions are for use in the deprecated * gensec_socket code (public because SPNEGO must * use them for recursion) */ NTSTATUS gensec_unwrap_packets(struct gensec_security *gensec_security, TALLOC_CTX *mem_ctx, const DATA_BLOB *in, DATA_BLOB *out, size_t *len_processed) { if (!gensec_security->ops->unwrap_packets) { DATA_BLOB wrapped; NTSTATUS nt_status; size_t packet_size; if (in->length < 4) { /* Missing the header we already had! */ DEBUG(0, ("Asked to unwrap packet of bogus length! How did we get the short packet?!\n")); return NT_STATUS_INVALID_PARAMETER; } packet_size = RIVAL(in->data, 0); wrapped = data_blob_const(in->data + 4, packet_size); if (wrapped.length > (in->length - 4)) { DEBUG(0, ("Asked to unwrap packed of bogus length %d > %d! How did we get this?!\n", (int)wrapped.length, (int)(in->length - 4))); return NT_STATUS_INTERNAL_ERROR; } nt_status = gensec_unwrap(gensec_security, mem_ctx, &wrapped, out); if (!NT_STATUS_IS_OK(nt_status)) { return nt_status; } *len_processed = packet_size + 4; return nt_status; } return gensec_security->ops->unwrap_packets(gensec_security, mem_ctx, in, out, len_processed); }
bool kpasswdd_process(struct kdc_server *kdc, TALLOC_CTX *mem_ctx, DATA_BLOB *input, DATA_BLOB *reply, struct tsocket_address *peer_addr, struct tsocket_address *my_addr, int datagram_reply) { bool ret; const uint16_t header_len = 6; uint16_t len; uint16_t ap_req_len; uint16_t krb_priv_len; uint16_t version; NTSTATUS nt_status; DATA_BLOB ap_req, krb_priv_req; DATA_BLOB krb_priv_rep = data_blob(NULL, 0); DATA_BLOB ap_rep = data_blob(NULL, 0); DATA_BLOB kpasswd_req, kpasswd_rep; struct cli_credentials *server_credentials; struct gensec_security *gensec_security; TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); char *keytab_name; if (!tmp_ctx) { return false; } /* Be parinoid. We need to ensure we don't just let the * caller lead us into a buffer overflow */ if (input->length <= header_len) { talloc_free(tmp_ctx); return false; } len = RSVAL(input->data, 0); if (input->length != len) { talloc_free(tmp_ctx); return false; } /* There are two different versions of this protocol so far, * plus others in the standards pipe. Fortunetly they all * take a very similar framing */ version = RSVAL(input->data, 2); ap_req_len = RSVAL(input->data, 4); if ((ap_req_len >= len) || (ap_req_len + header_len) >= len) { talloc_free(tmp_ctx); return false; } krb_priv_len = len - ap_req_len; ap_req = data_blob_const(&input->data[header_len], ap_req_len); krb_priv_req = data_blob_const(&input->data[header_len + ap_req_len], krb_priv_len); server_credentials = cli_credentials_init(tmp_ctx); if (!server_credentials) { DEBUG(1, ("Failed to init server credentials\n")); return false; } /* We want the credentials subsystem to use the krb5 context * we already have, rather than a new context */ cli_credentials_set_krb5_context(server_credentials, kdc->smb_krb5_context); cli_credentials_set_conf(server_credentials, kdc->task->lp_ctx); keytab_name = talloc_asprintf(server_credentials, "HDB:samba4&%p", kdc->base_ctx); cli_credentials_set_username(server_credentials, "kadmin/changepw", CRED_SPECIFIED); ret = cli_credentials_set_keytab_name(server_credentials, kdc->task->event_ctx, kdc->task->lp_ctx, keytab_name, CRED_SPECIFIED); if (ret != 0) { ret = kpasswdd_make_unauth_error_reply(kdc, mem_ctx, KRB5_KPASSWD_HARDERROR, talloc_asprintf(mem_ctx, "Failed to obtain server credentials for kadmin/changepw: %s\n", nt_errstr(nt_status)), &krb_priv_rep); ap_rep.length = 0; if (ret) { goto reply; } talloc_free(tmp_ctx); return ret; } /* We don't strictly need to call this wrapper, and could call * gensec_server_start directly, as we have no need for NTLM * and we have a PAC, but this ensures that the wrapper can be * safely extended for other helpful things in future */ nt_status = samba_server_gensec_start(tmp_ctx, kdc->task->event_ctx, kdc->task->msg_ctx, kdc->task->lp_ctx, server_credentials, "kpasswd", &gensec_security); if (!NT_STATUS_IS_OK(nt_status)) { talloc_free(tmp_ctx); return false; } /* The kerberos PRIV packets include these addresses. MIT * clients check that they are present */ #if 0 /* Skip this part for now, it breaks with a NetAPP filer and * in any case where the client address is behind NAT. If * older MIT clients need this, we might have to insert more * complex code */ nt_status = gensec_set_local_address(gensec_security, peer_addr); if (!NT_STATUS_IS_OK(nt_status)) { talloc_free(tmp_ctx); return false; } #endif nt_status = gensec_set_local_address(gensec_security, my_addr); if (!NT_STATUS_IS_OK(nt_status)) { talloc_free(tmp_ctx); return false; } /* We want the GENSEC wrap calls to generate PRIV tokens */ gensec_want_feature(gensec_security, GENSEC_FEATURE_SEAL); nt_status = gensec_start_mech_by_name(gensec_security, "krb5"); if (!NT_STATUS_IS_OK(nt_status)) { talloc_free(tmp_ctx); return false; } /* Accept the AP-REQ and generate teh AP-REP we need for the reply */ nt_status = gensec_update(gensec_security, tmp_ctx, ap_req, &ap_rep); if (!NT_STATUS_IS_OK(nt_status) && !NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { ret = kpasswdd_make_unauth_error_reply(kdc, mem_ctx, KRB5_KPASSWD_HARDERROR, talloc_asprintf(mem_ctx, "gensec_update failed: %s", nt_errstr(nt_status)), &krb_priv_rep); ap_rep.length = 0; if (ret) { goto reply; } talloc_free(tmp_ctx); return ret; } /* Extract the data from the KRB-PRIV half of the message */ nt_status = gensec_unwrap(gensec_security, tmp_ctx, &krb_priv_req, &kpasswd_req); if (!NT_STATUS_IS_OK(nt_status)) { ret = kpasswdd_make_unauth_error_reply(kdc, mem_ctx, KRB5_KPASSWD_HARDERROR, talloc_asprintf(mem_ctx, "gensec_unwrap failed: %s", nt_errstr(nt_status)), &krb_priv_rep); ap_rep.length = 0; if (ret) { goto reply; } talloc_free(tmp_ctx); return ret; } /* Figure out something to do with it (probably changing a password...) */ ret = kpasswd_process_request(kdc, tmp_ctx, gensec_security, version, &kpasswd_req, &kpasswd_rep); if (!ret) { /* Argh! */ return false; } /* And wrap up the reply: This ensures that the error message * or success can be verified by the client */ nt_status = gensec_wrap(gensec_security, tmp_ctx, &kpasswd_rep, &krb_priv_rep); if (!NT_STATUS_IS_OK(nt_status)) { ret = kpasswdd_make_unauth_error_reply(kdc, mem_ctx, KRB5_KPASSWD_HARDERROR, talloc_asprintf(mem_ctx, "gensec_wrap failed: %s", nt_errstr(nt_status)), &krb_priv_rep); ap_rep.length = 0; if (ret) { goto reply; } talloc_free(tmp_ctx); return ret; } reply: *reply = data_blob_talloc(mem_ctx, NULL, krb_priv_rep.length + ap_rep.length + header_len); if (!reply->data) { return false; } RSSVAL(reply->data, 0, reply->length); RSSVAL(reply->data, 2, 1); /* This is a version 1 reply, MS change/set or otherwise */ RSSVAL(reply->data, 4, ap_rep.length); memcpy(reply->data + header_len, ap_rep.data, ap_rep.length); memcpy(reply->data + header_len + ap_rep.length, krb_priv_rep.data, krb_priv_rep.length); talloc_free(tmp_ctx); return ret; }