1/*
2   Unix SMB/CIFS mplementation.
3   Helper functions for applying replicated objects
4
5   Copyright (C) Stefan Metzmacher <metze@samba.org> 2007
6   Copyright (C) Andrew Bartlett <abartlet@samba.org> 2009
7
8   This program is free software; you can redistribute it and/or modify
9   it under the terms of the GNU General Public License as published by
10   the Free Software Foundation; either version 3 of the License, or
11   (at your option) any later version.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18   You should have received a copy of the GNU General Public License
19   along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
21*/
22
23#include "includes.h"
24#include "../lib/util/dlinklist.h"
25#include "librpc/gen_ndr/ndr_misc.h"
26#include "librpc/gen_ndr/ndr_drsuapi.h"
27#include "librpc/gen_ndr/ndr_drsblobs.h"
28#include "../lib/crypto/crypto.h"
29#include "../libcli/drsuapi/drsuapi.h"
30#include "libcli/auth/libcli_auth.h"
31
32WERROR drsuapi_decrypt_attribute_value(TALLOC_CTX *mem_ctx,
33				       const DATA_BLOB *gensec_skey,
34				       bool rid_crypt,
35				       uint32_t rid,
36				       DATA_BLOB *in,
37				       DATA_BLOB *out)
38{
39	DATA_BLOB confounder;
40	DATA_BLOB enc_buffer;
41
42	struct MD5Context md5;
43	uint8_t _enc_key[16];
44	DATA_BLOB enc_key;
45
46	DATA_BLOB dec_buffer;
47
48	uint32_t crc32_given;
49	uint32_t crc32_calc;
50	DATA_BLOB checked_buffer;
51
52	DATA_BLOB plain_buffer;
53
54	/*
55	 * users with rid == 0 should not exist
56	 */
57	if (rid_crypt && rid == 0) {
58		return WERR_DS_DRA_INVALID_PARAMETER;
59	}
60
61	/*
62	 * the first 16 bytes at the beginning are the confounder
63	 * followed by the 4 byte crc32 checksum
64	 */
65	if (in->length < 20) {
66		return WERR_DS_DRA_INVALID_PARAMETER;
67	}
68	confounder = data_blob_const(in->data, 16);
69	enc_buffer = data_blob_const(in->data + 16, in->length - 16);
70
71	/*
72	 * build the encryption key md5 over the session key followed
73	 * by the confounder
74	 *
75	 * here the gensec session key is used and
76	 * not the dcerpc ncacn_ip_tcp "SystemLibraryDTC" key!
77	 */
78	enc_key = data_blob_const(_enc_key, sizeof(_enc_key));
79	MD5Init(&md5);
80	MD5Update(&md5, gensec_skey->data, gensec_skey->length);
81	MD5Update(&md5, confounder.data, confounder.length);
82	MD5Final(enc_key.data, &md5);
83
84	/*
85	 * copy the encrypted buffer part and
86	 * decrypt it using the created encryption key using arcfour
87	 */
88	dec_buffer = data_blob_const(enc_buffer.data, enc_buffer.length);
89	arcfour_crypt_blob(dec_buffer.data, dec_buffer.length, &enc_key);
90
91	/*
92	 * the first 4 byte are the crc32 checksum
93	 * of the remaining bytes
94	 */
95	crc32_given = IVAL(dec_buffer.data, 0);
96	crc32_calc = crc32_calc_buffer(dec_buffer.data + 4 , dec_buffer.length - 4);
97	checked_buffer = data_blob_const(dec_buffer.data + 4, dec_buffer.length - 4);
98
99	plain_buffer = data_blob_talloc(mem_ctx, checked_buffer.data, checked_buffer.length);
100	W_ERROR_HAVE_NO_MEMORY(plain_buffer.data);
101
102	if (crc32_given != crc32_calc) {
103		return WERR_SEC_E_DECRYPT_FAILURE;
104	}
105	/*
106	 * The following rid_crypt obfuscation isn't session specific
107	 * and not really needed here, because we allways know the rid of the
108	 * user account.
109	 *
110	 * some attributes with this 'additional encryption' include
111	 * dBCSPwd, unicodePwd, ntPwdHistory, lmPwdHistory
112	 *
113	 * But for the rest of samba it's easier when we remove this static
114	 * obfuscation here
115	 */
116	if (rid_crypt) {
117		uint32_t i, num_hashes;
118
119		if ((checked_buffer.length % 16) != 0) {
120			return WERR_DS_DRA_INVALID_PARAMETER;
121		}
122
123		num_hashes = plain_buffer.length / 16;
124		for (i = 0; i < num_hashes; i++) {
125			uint32_t offset = i * 16;
126			sam_rid_crypt(rid, checked_buffer.data + offset, plain_buffer.data + offset, 0);
127		}
128	}
129
130	*out = plain_buffer;
131	return WERR_OK;
132}
133
134WERROR drsuapi_decrypt_attribute(TALLOC_CTX *mem_ctx,
135				 const DATA_BLOB *gensec_skey,
136				 uint32_t rid,
137				 struct drsuapi_DsReplicaAttribute *attr)
138{
139	WERROR status;
140	DATA_BLOB *enc_data;
141	DATA_BLOB plain_data;
142	bool rid_crypt = false;
143
144	if (attr->value_ctr.num_values == 0) {
145		return WERR_OK;
146	}
147
148	switch (attr->attid) {
149	case DRSUAPI_ATTRIBUTE_dBCSPwd:
150	case DRSUAPI_ATTRIBUTE_unicodePwd:
151	case DRSUAPI_ATTRIBUTE_ntPwdHistory:
152	case DRSUAPI_ATTRIBUTE_lmPwdHistory:
153		rid_crypt = true;
154		break;
155	case DRSUAPI_ATTRIBUTE_supplementalCredentials:
156	case DRSUAPI_ATTRIBUTE_priorValue:
157	case DRSUAPI_ATTRIBUTE_currentValue:
158	case DRSUAPI_ATTRIBUTE_trustAuthOutgoing:
159	case DRSUAPI_ATTRIBUTE_trustAuthIncoming:
160	case DRSUAPI_ATTRIBUTE_initialAuthOutgoing:
161	case DRSUAPI_ATTRIBUTE_initialAuthIncoming:
162		break;
163	default:
164		return WERR_OK;
165	}
166
167	if (attr->value_ctr.num_values > 1) {
168		return WERR_DS_DRA_INVALID_PARAMETER;
169	}
170
171	if (!attr->value_ctr.values[0].blob) {
172		return WERR_DS_DRA_INVALID_PARAMETER;
173	}
174
175	enc_data	= attr->value_ctr.values[0].blob;
176
177	status = drsuapi_decrypt_attribute_value(mem_ctx,
178						 gensec_skey,
179						 rid_crypt,
180						 rid,
181						 enc_data,
182						 &plain_data);
183	W_ERROR_NOT_OK_RETURN(status);
184
185	talloc_free(attr->value_ctr.values[0].blob->data);
186	*attr->value_ctr.values[0].blob = plain_data;
187
188	return WERR_OK;
189}
190
191static WERROR drsuapi_encrypt_attribute_value(TALLOC_CTX *mem_ctx,
192					      const DATA_BLOB *gensec_skey,
193					      bool rid_crypt,
194					      uint32_t rid,
195					      DATA_BLOB *in,
196					      DATA_BLOB *out)
197{
198	DATA_BLOB rid_crypt_out = data_blob(NULL, 0);
199	DATA_BLOB confounder;
200
201	struct MD5Context md5;
202	uint8_t _enc_key[16];
203	DATA_BLOB enc_key;
204
205	DATA_BLOB enc_buffer;
206
207	uint32_t crc32_calc;
208
209	/*
210	 * users with rid == 0 should not exist
211	 */
212	if (rid_crypt && rid == 0) {
213		return WERR_DS_DRA_INVALID_PARAMETER;
214	}
215
216	/*
217	 * The following rid_crypt obfuscation isn't session specific
218	 * and not really needed here, because we allways know the rid of the
219	 * user account.
220	 *
221	 * some attributes with this 'additional encryption' include
222	 * dBCSPwd, unicodePwd, ntPwdHistory, lmPwdHistory
223	 *
224	 * But for the rest of samba it's easier when we remove this static
225	 * obfuscation here
226	 */
227	if (rid_crypt) {
228		uint32_t i, num_hashes;
229		rid_crypt_out = data_blob_talloc(mem_ctx, in->data, in->length);
230		W_ERROR_HAVE_NO_MEMORY(rid_crypt_out.data);
231
232		if ((rid_crypt_out.length % 16) != 0) {
233			return WERR_DS_DRA_INVALID_PARAMETER;
234		}
235
236		num_hashes = rid_crypt_out.length / 16;
237		for (i = 0; i < num_hashes; i++) {
238			uint32_t offset = i * 16;
239			sam_rid_crypt(rid, in->data + offset, rid_crypt_out.data + offset, 1);
240		}
241		in = &rid_crypt_out;
242	}
243
244	/*
245	 * the first 16 bytes at the beginning are the confounder
246	 * followed by the 4 byte crc32 checksum
247	 */
248
249	enc_buffer = data_blob_talloc(mem_ctx, NULL, in->length+20);
250	if (!enc_buffer.data) {
251		talloc_free(rid_crypt_out.data);
252		return WERR_NOMEM;
253	};
254
255	confounder = data_blob_const(enc_buffer.data, 16);
256	generate_random_buffer(confounder.data, confounder.length);
257
258	/*
259	 * build the encryption key md5 over the session key followed
260	 * by the confounder
261	 *
262	 * here the gensec session key is used and
263	 * not the dcerpc ncacn_ip_tcp "SystemLibraryDTC" key!
264	 */
265	enc_key = data_blob_const(_enc_key, sizeof(_enc_key));
266	MD5Init(&md5);
267	MD5Update(&md5, gensec_skey->data, gensec_skey->length);
268	MD5Update(&md5, confounder.data, confounder.length);
269	MD5Final(enc_key.data, &md5);
270
271	/*
272	 * the first 4 byte are the crc32 checksum
273	 * of the remaining bytes
274	 */
275	crc32_calc = crc32_calc_buffer(in->data, in->length);
276	SIVAL(enc_buffer.data, 16, crc32_calc);
277
278	/*
279	 * copy the plain buffer part and
280	 * encrypt it using the created encryption key using arcfour
281	 */
282	memcpy(enc_buffer.data+20, in->data, in->length);
283	talloc_free(rid_crypt_out.data);
284
285	arcfour_crypt_blob(enc_buffer.data+16, enc_buffer.length-16, &enc_key);
286
287	*out = enc_buffer;
288
289	return WERR_OK;
290}
291
292/*
293  encrypt a DRSUAPI attribute ready for sending over the wire
294  Only some attribute types are encrypted
295 */
296WERROR drsuapi_encrypt_attribute(TALLOC_CTX *mem_ctx,
297				 const DATA_BLOB *gensec_skey,
298				 uint32_t rid,
299				 struct drsuapi_DsReplicaAttribute *attr)
300{
301	WERROR status;
302	DATA_BLOB *plain_data;
303	DATA_BLOB enc_data;
304	bool rid_crypt = false;
305
306	if (attr->value_ctr.num_values == 0) {
307		return WERR_OK;
308	}
309
310	switch (attr->attid) {
311	case DRSUAPI_ATTRIBUTE_dBCSPwd:
312	case DRSUAPI_ATTRIBUTE_unicodePwd:
313	case DRSUAPI_ATTRIBUTE_ntPwdHistory:
314	case DRSUAPI_ATTRIBUTE_lmPwdHistory:
315		rid_crypt = true;
316		break;
317	case DRSUAPI_ATTRIBUTE_supplementalCredentials:
318	case DRSUAPI_ATTRIBUTE_priorValue:
319	case DRSUAPI_ATTRIBUTE_currentValue:
320	case DRSUAPI_ATTRIBUTE_trustAuthOutgoing:
321	case DRSUAPI_ATTRIBUTE_trustAuthIncoming:
322	case DRSUAPI_ATTRIBUTE_initialAuthOutgoing:
323	case DRSUAPI_ATTRIBUTE_initialAuthIncoming:
324		break;
325	default:
326		return WERR_OK;
327	}
328
329	if (attr->value_ctr.num_values > 1) {
330		return WERR_DS_DRA_INVALID_PARAMETER;
331	}
332
333	if (!attr->value_ctr.values[0].blob) {
334		return WERR_DS_DRA_INVALID_PARAMETER;
335	}
336
337	plain_data	= attr->value_ctr.values[0].blob;
338
339	status = drsuapi_encrypt_attribute_value(mem_ctx,
340						 gensec_skey,
341						 rid_crypt,
342						 rid,
343						 plain_data,
344						 &enc_data);
345	W_ERROR_NOT_OK_RETURN(status);
346
347	talloc_free(attr->value_ctr.values[0].blob->data);
348	*attr->value_ctr.values[0].blob = enc_data;
349
350	return WERR_OK;
351}
352
353