2 Unix SMB/CIFS mplementation.
3 Helper functions for applying replicated objects
5 Copyright (C) Stefan Metzmacher <metze@samba.org> 2007
6 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2009
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.
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.
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/>.
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"
32 WERROR
drsuapi_decrypt_attribute_value(TALLOC_CTX
*mem_ctx
,
33 const DATA_BLOB
*gensec_skey
,
50 DATA_BLOB checked_buffer
;
52 DATA_BLOB plain_buffer
;
55 * users with rid == 0 should not exist
57 if (rid_crypt
&& rid
== 0) {
58 return WERR_DS_DRA_INVALID_PARAMETER
;
62 * the first 16 bytes at the beginning are the confounder
63 * followed by the 4 byte crc32 checksum
65 if (in
->length
< 20) {
66 return WERR_DS_DRA_INVALID_PARAMETER
;
68 confounder
= data_blob_const(in
->data
, 16);
69 enc_buffer
= data_blob_const(in
->data
+ 16, in
->length
- 16);
72 * build the encryption key md5 over the session key followed
75 * here the gensec session key is used and
76 * not the dcerpc ncacn_ip_tcp "SystemLibraryDTC" key!
78 enc_key
= data_blob_const(_enc_key
, sizeof(_enc_key
));
80 MD5Update(&md5
, gensec_skey
->data
, gensec_skey
->length
);
81 MD5Update(&md5
, confounder
.data
, confounder
.length
);
82 MD5Final(enc_key
.data
, &md5
);
85 * copy the encrypted buffer part and
86 * decrypt it using the created encryption key using arcfour
88 dec_buffer
= data_blob_const(enc_buffer
.data
, enc_buffer
.length
);
89 arcfour_crypt_blob(dec_buffer
.data
, dec_buffer
.length
, &enc_key
);
92 * the first 4 byte are the crc32 checksum
93 * of the remaining bytes
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);
99 plain_buffer
= data_blob_talloc(mem_ctx
, checked_buffer
.data
, checked_buffer
.length
);
100 W_ERROR_HAVE_NO_MEMORY(plain_buffer
.data
);
102 if (crc32_given
!= crc32_calc
) {
103 return WERR_SEC_E_DECRYPT_FAILURE
;
106 * The following rid_crypt obfuscation isn't session specific
107 * and not really needed here, because we allways know the rid of the
110 * some attributes with this 'additional encryption' include
111 * dBCSPwd, unicodePwd, ntPwdHistory, lmPwdHistory
113 * But for the rest of samba it's easier when we remove this static
117 uint32_t i
, num_hashes
;
119 if ((checked_buffer
.length
% 16) != 0) {
120 return WERR_DS_DRA_INVALID_PARAMETER
;
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);
134 WERROR
drsuapi_decrypt_attribute(TALLOC_CTX
*mem_ctx
,
135 const DATA_BLOB
*gensec_skey
,
137 struct drsuapi_DsReplicaAttribute
*attr
)
141 DATA_BLOB plain_data
;
142 bool rid_crypt
= false;
144 if (attr
->value_ctr
.num_values
== 0) {
148 switch (attr
->attid
) {
149 case DRSUAPI_ATTID_dBCSPwd
:
150 case DRSUAPI_ATTID_unicodePwd
:
151 case DRSUAPI_ATTID_ntPwdHistory
:
152 case DRSUAPI_ATTID_lmPwdHistory
:
155 case DRSUAPI_ATTID_supplementalCredentials
:
156 case DRSUAPI_ATTID_priorValue
:
157 case DRSUAPI_ATTID_currentValue
:
158 case DRSUAPI_ATTID_trustAuthOutgoing
:
159 case DRSUAPI_ATTID_trustAuthIncoming
:
160 case DRSUAPI_ATTID_initialAuthOutgoing
:
161 case DRSUAPI_ATTID_initialAuthIncoming
:
167 if (attr
->value_ctr
.num_values
> 1) {
168 return WERR_DS_DRA_INVALID_PARAMETER
;
171 if (!attr
->value_ctr
.values
[0].blob
) {
172 return WERR_DS_DRA_INVALID_PARAMETER
;
175 enc_data
= attr
->value_ctr
.values
[0].blob
;
177 status
= drsuapi_decrypt_attribute_value(mem_ctx
,
183 W_ERROR_NOT_OK_RETURN(status
);
185 talloc_free(attr
->value_ctr
.values
[0].blob
->data
);
186 *attr
->value_ctr
.values
[0].blob
= plain_data
;
191 static WERROR
drsuapi_encrypt_attribute_value(TALLOC_CTX
*mem_ctx
,
192 const DATA_BLOB
*gensec_skey
,
198 DATA_BLOB rid_crypt_out
= data_blob(NULL
, 0);
199 DATA_BLOB confounder
;
202 uint8_t _enc_key
[16];
205 DATA_BLOB enc_buffer
;
210 * users with rid == 0 should not exist
212 if (rid_crypt
&& rid
== 0) {
213 return WERR_DS_DRA_INVALID_PARAMETER
;
217 * The following rid_crypt obfuscation isn't session specific
218 * and not really needed here, because we allways know the rid of the
221 * some attributes with this 'additional encryption' include
222 * dBCSPwd, unicodePwd, ntPwdHistory, lmPwdHistory
224 * But for the rest of samba it's easier when we remove this static
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
);
232 if ((rid_crypt_out
.length
% 16) != 0) {
233 return WERR_DS_DRA_INVALID_PARAMETER
;
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);
245 * the first 16 bytes at the beginning are the confounder
246 * followed by the 4 byte crc32 checksum
249 enc_buffer
= data_blob_talloc(mem_ctx
, NULL
, in
->length
+20);
250 if (!enc_buffer
.data
) {
251 talloc_free(rid_crypt_out
.data
);
255 confounder
= data_blob_const(enc_buffer
.data
, 16);
256 generate_random_buffer(confounder
.data
, confounder
.length
);
259 * build the encryption key md5 over the session key followed
262 * here the gensec session key is used and
263 * not the dcerpc ncacn_ip_tcp "SystemLibraryDTC" key!
265 enc_key
= data_blob_const(_enc_key
, sizeof(_enc_key
));
267 MD5Update(&md5
, gensec_skey
->data
, gensec_skey
->length
);
268 MD5Update(&md5
, confounder
.data
, confounder
.length
);
269 MD5Final(enc_key
.data
, &md5
);
272 * the first 4 byte are the crc32 checksum
273 * of the remaining bytes
275 crc32_calc
= crc32_calc_buffer(in
->data
, in
->length
);
276 SIVAL(enc_buffer
.data
, 16, crc32_calc
);
279 * copy the plain buffer part and
280 * encrypt it using the created encryption key using arcfour
282 memcpy(enc_buffer
.data
+20, in
->data
, in
->length
);
283 talloc_free(rid_crypt_out
.data
);
285 arcfour_crypt_blob(enc_buffer
.data
+16, enc_buffer
.length
-16, &enc_key
);
293 encrypt a DRSUAPI attribute ready for sending over the wire
294 Only some attribute types are encrypted
296 WERROR
drsuapi_encrypt_attribute(TALLOC_CTX
*mem_ctx
,
297 const DATA_BLOB
*gensec_skey
,
299 struct drsuapi_DsReplicaAttribute
*attr
)
302 DATA_BLOB
*plain_data
;
304 bool rid_crypt
= false;
306 if (attr
->value_ctr
.num_values
== 0) {
310 switch (attr
->attid
) {
311 case DRSUAPI_ATTID_dBCSPwd
:
312 case DRSUAPI_ATTID_unicodePwd
:
313 case DRSUAPI_ATTID_ntPwdHistory
:
314 case DRSUAPI_ATTID_lmPwdHistory
:
317 case DRSUAPI_ATTID_supplementalCredentials
:
318 case DRSUAPI_ATTID_priorValue
:
319 case DRSUAPI_ATTID_currentValue
:
320 case DRSUAPI_ATTID_trustAuthOutgoing
:
321 case DRSUAPI_ATTID_trustAuthIncoming
:
322 case DRSUAPI_ATTID_initialAuthOutgoing
:
323 case DRSUAPI_ATTID_initialAuthIncoming
:
329 if (attr
->value_ctr
.num_values
> 1) {
330 return WERR_DS_DRA_INVALID_PARAMETER
;
333 if (!attr
->value_ctr
.values
[0].blob
) {
334 return WERR_DS_DRA_INVALID_PARAMETER
;
337 plain_data
= attr
->value_ctr
.values
[0].blob
;
339 status
= drsuapi_encrypt_attribute_value(mem_ctx
,
345 W_ERROR_NOT_OK_RETURN(status
);
347 talloc_free(attr
->value_ctr
.values
[0].blob
->data
);
348 *attr
->value_ctr
.values
[0].blob
= enc_data
;