2 Unix SMB/CIFS implementation.
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"
31 #include "dsdb/samdb/samdb.h"
33 WERROR
drsuapi_decrypt_attribute_value(TALLOC_CTX
*mem_ctx
,
34 const DATA_BLOB
*gensec_skey
,
51 DATA_BLOB checked_buffer
;
53 DATA_BLOB plain_buffer
;
56 * users with rid == 0 should not exist
58 if (rid_crypt
&& rid
== 0) {
59 return WERR_DS_DRA_INVALID_PARAMETER
;
63 * the first 16 bytes at the beginning are the confounder
64 * followed by the 4 byte crc32 checksum
66 if (in
->length
< 20) {
67 return WERR_DS_DRA_INVALID_PARAMETER
;
69 confounder
= data_blob_const(in
->data
, 16);
70 enc_buffer
= data_blob_const(in
->data
+ 16, in
->length
- 16);
73 * build the encryption key md5 over the session key followed
76 * here the gensec session key is used and
77 * not the dcerpc ncacn_ip_tcp "SystemLibraryDTC" key!
79 enc_key
= data_blob_const(_enc_key
, sizeof(_enc_key
));
81 MD5Update(&md5
, gensec_skey
->data
, gensec_skey
->length
);
82 MD5Update(&md5
, confounder
.data
, confounder
.length
);
83 MD5Final(enc_key
.data
, &md5
);
86 * copy the encrypted buffer part and
87 * decrypt it using the created encryption key using arcfour
89 dec_buffer
= data_blob_const(enc_buffer
.data
, enc_buffer
.length
);
90 arcfour_crypt_blob(dec_buffer
.data
, dec_buffer
.length
, &enc_key
);
93 * the first 4 byte are the crc32 checksum
94 * of the remaining bytes
96 crc32_given
= IVAL(dec_buffer
.data
, 0);
97 crc32_calc
= crc32_calc_buffer(dec_buffer
.data
+ 4 , dec_buffer
.length
- 4);
98 checked_buffer
= data_blob_const(dec_buffer
.data
+ 4, dec_buffer
.length
- 4);
100 plain_buffer
= data_blob_talloc(mem_ctx
, checked_buffer
.data
, checked_buffer
.length
);
101 W_ERROR_HAVE_NO_MEMORY(plain_buffer
.data
);
103 if (crc32_given
!= crc32_calc
) {
104 return WERR_SEC_E_DECRYPT_FAILURE
;
107 * The following rid_crypt obfuscation isn't session specific
108 * and not really needed here, because we allways know the rid of the
111 * some attributes with this 'additional encryption' include
112 * dBCSPwd, unicodePwd, ntPwdHistory, lmPwdHistory
114 * But for the rest of samba it's easier when we remove this static
118 uint32_t i
, num_hashes
;
120 if ((checked_buffer
.length
% 16) != 0) {
121 return WERR_DS_DRA_INVALID_PARAMETER
;
124 num_hashes
= plain_buffer
.length
/ 16;
125 for (i
= 0; i
< num_hashes
; i
++) {
126 uint32_t offset
= i
* 16;
127 sam_rid_crypt(rid
, checked_buffer
.data
+ offset
, plain_buffer
.data
+ offset
, 0);
135 WERROR
drsuapi_decrypt_attribute(TALLOC_CTX
*mem_ctx
,
136 const DATA_BLOB
*gensec_skey
,
138 uint32_t dsdb_repl_flags
,
139 struct drsuapi_DsReplicaAttribute
*attr
)
143 DATA_BLOB plain_data
;
144 bool rid_crypt
= false;
146 if (attr
->value_ctr
.num_values
== 0) {
150 switch (attr
->attid
) {
151 case DRSUAPI_ATTID_dBCSPwd
:
152 case DRSUAPI_ATTID_unicodePwd
:
153 case DRSUAPI_ATTID_ntPwdHistory
:
154 case DRSUAPI_ATTID_lmPwdHistory
:
157 case DRSUAPI_ATTID_supplementalCredentials
:
158 case DRSUAPI_ATTID_priorValue
:
159 case DRSUAPI_ATTID_currentValue
:
160 case DRSUAPI_ATTID_trustAuthOutgoing
:
161 case DRSUAPI_ATTID_trustAuthIncoming
:
162 case DRSUAPI_ATTID_initialAuthOutgoing
:
163 case DRSUAPI_ATTID_initialAuthIncoming
:
169 if (dsdb_repl_flags
& DSDB_REPL_FLAG_EXPECT_NO_SECRETS
) {
170 return WERR_TOO_MANY_SECRETS
;
173 if (attr
->value_ctr
.num_values
> 1) {
174 return WERR_DS_DRA_INVALID_PARAMETER
;
177 if (!attr
->value_ctr
.values
[0].blob
) {
178 return WERR_DS_DRA_INVALID_PARAMETER
;
181 enc_data
= attr
->value_ctr
.values
[0].blob
;
183 status
= drsuapi_decrypt_attribute_value(mem_ctx
,
189 W_ERROR_NOT_OK_RETURN(status
);
191 talloc_free(attr
->value_ctr
.values
[0].blob
->data
);
192 *attr
->value_ctr
.values
[0].blob
= plain_data
;
197 static WERROR
drsuapi_encrypt_attribute_value(TALLOC_CTX
*mem_ctx
,
198 const DATA_BLOB
*gensec_skey
,
204 DATA_BLOB rid_crypt_out
= data_blob(NULL
, 0);
205 DATA_BLOB confounder
;
208 uint8_t _enc_key
[16];
211 DATA_BLOB enc_buffer
;
216 * users with rid == 0 should not exist
218 if (rid_crypt
&& rid
== 0) {
219 return WERR_DS_DRA_INVALID_PARAMETER
;
223 * The following rid_crypt obfuscation isn't session specific
224 * and not really needed here, because we allways know the rid of the
227 * some attributes with this 'additional encryption' include
228 * dBCSPwd, unicodePwd, ntPwdHistory, lmPwdHistory
230 * But for the rest of samba it's easier when we remove this static
234 uint32_t i
, num_hashes
;
235 rid_crypt_out
= data_blob_talloc(mem_ctx
, in
->data
, in
->length
);
236 W_ERROR_HAVE_NO_MEMORY(rid_crypt_out
.data
);
238 if ((rid_crypt_out
.length
% 16) != 0) {
239 return WERR_DS_DRA_INVALID_PARAMETER
;
242 num_hashes
= rid_crypt_out
.length
/ 16;
243 for (i
= 0; i
< num_hashes
; i
++) {
244 uint32_t offset
= i
* 16;
245 sam_rid_crypt(rid
, in
->data
+ offset
, rid_crypt_out
.data
+ offset
, 1);
251 * the first 16 bytes at the beginning are the confounder
252 * followed by the 4 byte crc32 checksum
255 enc_buffer
= data_blob_talloc(mem_ctx
, NULL
, in
->length
+20);
256 if (!enc_buffer
.data
) {
257 talloc_free(rid_crypt_out
.data
);
261 confounder
= data_blob_const(enc_buffer
.data
, 16);
262 generate_random_buffer(confounder
.data
, confounder
.length
);
265 * build the encryption key md5 over the session key followed
268 * here the gensec session key is used and
269 * not the dcerpc ncacn_ip_tcp "SystemLibraryDTC" key!
271 enc_key
= data_blob_const(_enc_key
, sizeof(_enc_key
));
273 MD5Update(&md5
, gensec_skey
->data
, gensec_skey
->length
);
274 MD5Update(&md5
, confounder
.data
, confounder
.length
);
275 MD5Final(enc_key
.data
, &md5
);
278 * the first 4 byte are the crc32 checksum
279 * of the remaining bytes
281 crc32_calc
= crc32_calc_buffer(in
->data
, in
->length
);
282 SIVAL(enc_buffer
.data
, 16, crc32_calc
);
285 * copy the plain buffer part and
286 * encrypt it using the created encryption key using arcfour
288 memcpy(enc_buffer
.data
+20, in
->data
, in
->length
);
289 talloc_free(rid_crypt_out
.data
);
291 arcfour_crypt_blob(enc_buffer
.data
+16, enc_buffer
.length
-16, &enc_key
);
299 encrypt a DRSUAPI attribute ready for sending over the wire
300 Only some attribute types are encrypted
302 WERROR
drsuapi_encrypt_attribute(TALLOC_CTX
*mem_ctx
,
303 const DATA_BLOB
*gensec_skey
,
305 struct drsuapi_DsReplicaAttribute
*attr
)
308 DATA_BLOB
*plain_data
;
310 bool rid_crypt
= false;
312 if (attr
->value_ctr
.num_values
== 0) {
316 switch (attr
->attid
) {
317 case DRSUAPI_ATTID_dBCSPwd
:
318 case DRSUAPI_ATTID_unicodePwd
:
319 case DRSUAPI_ATTID_ntPwdHistory
:
320 case DRSUAPI_ATTID_lmPwdHistory
:
323 case DRSUAPI_ATTID_supplementalCredentials
:
324 case DRSUAPI_ATTID_priorValue
:
325 case DRSUAPI_ATTID_currentValue
:
326 case DRSUAPI_ATTID_trustAuthOutgoing
:
327 case DRSUAPI_ATTID_trustAuthIncoming
:
328 case DRSUAPI_ATTID_initialAuthOutgoing
:
329 case DRSUAPI_ATTID_initialAuthIncoming
:
335 if (attr
->value_ctr
.num_values
> 1) {
336 return WERR_DS_DRA_INVALID_PARAMETER
;
339 if (!attr
->value_ctr
.values
[0].blob
) {
340 return WERR_DS_DRA_INVALID_PARAMETER
;
343 plain_data
= attr
->value_ctr
.values
[0].blob
;
345 status
= drsuapi_encrypt_attribute_value(mem_ctx
,
351 W_ERROR_NOT_OK_RETURN(status
);
353 talloc_free(attr
->value_ctr
.values
[0].blob
->data
);
354 *attr
->value_ctr
.values
[0].blob
= enc_data
;