lib: Save a few bytes of .text
[samba.git] / libcli / drsuapi / repl_decrypt.c
1 /* 
2    Unix SMB/CIFS implementation.
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 #include "dsdb/samdb/samdb.h"
32
33 WERROR drsuapi_decrypt_attribute_value(TALLOC_CTX *mem_ctx,
34                                        const DATA_BLOB *gensec_skey,
35                                        bool rid_crypt,
36                                        uint32_t rid,
37                                        DATA_BLOB *in,
38                                        DATA_BLOB *out)
39 {
40         DATA_BLOB confounder;
41         DATA_BLOB enc_buffer;
42
43         MD5_CTX md5;
44         uint8_t _enc_key[16];
45         DATA_BLOB enc_key;
46
47         DATA_BLOB dec_buffer;
48
49         uint32_t crc32_given;
50         uint32_t crc32_calc;
51         DATA_BLOB checked_buffer;
52
53         DATA_BLOB plain_buffer;
54
55         /*
56          * users with rid == 0 should not exist
57          */
58         if (rid_crypt && rid == 0) {
59                 return WERR_DS_DRA_INVALID_PARAMETER;
60         }
61
62         /* 
63          * the first 16 bytes at the beginning are the confounder
64          * followed by the 4 byte crc32 checksum
65          */
66         if (in->length < 20) {
67                 return WERR_DS_DRA_INVALID_PARAMETER;
68         }
69         confounder = data_blob_const(in->data, 16);
70         enc_buffer = data_blob_const(in->data + 16, in->length - 16);
71
72         /* 
73          * build the encryption key md5 over the session key followed
74          * by the confounder
75          * 
76          * here the gensec session key is used and
77          * not the dcerpc ncacn_ip_tcp "SystemLibraryDTC" key!
78          */
79         enc_key = data_blob_const(_enc_key, sizeof(_enc_key));
80         MD5Init(&md5);
81         MD5Update(&md5, gensec_skey->data, gensec_skey->length);
82         MD5Update(&md5, confounder.data, confounder.length);
83         MD5Final(enc_key.data, &md5);
84
85         /*
86          * copy the encrypted buffer part and 
87          * decrypt it using the created encryption key using arcfour
88          */
89         dec_buffer = data_blob_const(enc_buffer.data, enc_buffer.length);
90         arcfour_crypt_blob(dec_buffer.data, dec_buffer.length, &enc_key);
91
92         /* 
93          * the first 4 byte are the crc32 checksum
94          * of the remaining bytes
95          */
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);
99
100         plain_buffer = data_blob_talloc(mem_ctx, checked_buffer.data, checked_buffer.length);
101         W_ERROR_HAVE_NO_MEMORY(plain_buffer.data);
102
103         if (crc32_given != crc32_calc) {
104                 return W_ERROR(HRES_ERROR_V(HRES_SEC_E_DECRYPT_FAILURE));
105         }
106         /*
107          * The following rid_crypt obfuscation isn't session specific
108          * and not really needed here, because we allways know the rid of the
109          * user account.
110          *
111          * some attributes with this 'additional encryption' include
112          * dBCSPwd, unicodePwd, ntPwdHistory, lmPwdHistory
113          *
114          * But for the rest of samba it's easier when we remove this static
115          * obfuscation here
116          */
117         if (rid_crypt) {
118                 uint32_t i, num_hashes;
119
120                 if ((checked_buffer.length % 16) != 0) {
121                         return WERR_DS_DRA_INVALID_PARAMETER;
122                 }
123
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);
128                 }
129         }
130
131         *out = plain_buffer;
132         return WERR_OK;
133 }
134
135 WERROR drsuapi_decrypt_attribute(TALLOC_CTX *mem_ctx, 
136                                  const DATA_BLOB *gensec_skey,
137                                  uint32_t rid,
138                                  uint32_t dsdb_repl_flags,
139                                  struct drsuapi_DsReplicaAttribute *attr)
140 {
141         WERROR status;
142         DATA_BLOB *enc_data;
143         DATA_BLOB plain_data;
144         bool rid_crypt = false;
145
146         if (attr->value_ctr.num_values == 0) {
147                 return WERR_OK;
148         }
149
150         switch (attr->attid) {
151         case DRSUAPI_ATTID_dBCSPwd:
152         case DRSUAPI_ATTID_unicodePwd:
153         case DRSUAPI_ATTID_ntPwdHistory:
154         case DRSUAPI_ATTID_lmPwdHistory:
155                 rid_crypt = true;
156                 break;
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:
164                 break;
165         default:
166                 return WERR_OK;
167         }
168
169         if (dsdb_repl_flags & DSDB_REPL_FLAG_EXPECT_NO_SECRETS) {
170                 return WERR_TOO_MANY_SECRETS;
171         }
172
173         if (attr->value_ctr.num_values > 1) {
174                 return WERR_DS_DRA_INVALID_PARAMETER;
175         }
176
177         if (!attr->value_ctr.values[0].blob) {
178                 return WERR_DS_DRA_INVALID_PARAMETER;
179         }
180
181         enc_data        = attr->value_ctr.values[0].blob;
182
183         status = drsuapi_decrypt_attribute_value(mem_ctx,
184                                                  gensec_skey,
185                                                  rid_crypt,
186                                                  rid,
187                                                  enc_data,
188                                                  &plain_data);
189         W_ERROR_NOT_OK_RETURN(status);
190
191         talloc_free(attr->value_ctr.values[0].blob->data);
192         *attr->value_ctr.values[0].blob = plain_data;
193
194         return WERR_OK;
195 }
196
197 static WERROR drsuapi_encrypt_attribute_value(TALLOC_CTX *mem_ctx,
198                                               const DATA_BLOB *gensec_skey,
199                                               bool rid_crypt,
200                                               uint32_t rid,
201                                               DATA_BLOB *in,
202                                               DATA_BLOB *out)
203 {
204         DATA_BLOB rid_crypt_out = data_blob(NULL, 0);
205         DATA_BLOB confounder;
206
207         MD5_CTX md5;
208         uint8_t _enc_key[16];
209         DATA_BLOB enc_key;
210
211         DATA_BLOB enc_buffer;
212
213         uint32_t crc32_calc;
214
215         /*
216          * users with rid == 0 should not exist
217          */
218         if (rid_crypt && rid == 0) {
219                 return WERR_DS_DRA_INVALID_PARAMETER;
220         }
221
222         /*
223          * The following rid_crypt obfuscation isn't session specific
224          * and not really needed here, because we allways know the rid of the
225          * user account.
226          *
227          * some attributes with this 'additional encryption' include
228          * dBCSPwd, unicodePwd, ntPwdHistory, lmPwdHistory
229          *
230          * But for the rest of samba it's easier when we remove this static
231          * obfuscation here
232          */
233         if (rid_crypt) {
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);
237
238                 if ((rid_crypt_out.length % 16) != 0) {
239                         return WERR_DS_DRA_INVALID_PARAMETER;
240                 }
241
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);
246                 }
247                 in = &rid_crypt_out;
248         }
249
250         /* 
251          * the first 16 bytes at the beginning are the confounder
252          * followed by the 4 byte crc32 checksum
253          */
254
255         enc_buffer = data_blob_talloc(mem_ctx, NULL, in->length+20);
256         if (!enc_buffer.data) {
257                 talloc_free(rid_crypt_out.data);
258                 return WERR_NOT_ENOUGH_MEMORY;
259         };
260         
261         confounder = data_blob_const(enc_buffer.data, 16);
262         generate_random_buffer(confounder.data, confounder.length);
263
264         /* 
265          * build the encryption key md5 over the session key followed
266          * by the confounder
267          * 
268          * here the gensec session key is used and
269          * not the dcerpc ncacn_ip_tcp "SystemLibraryDTC" key!
270          */
271         enc_key = data_blob_const(_enc_key, sizeof(_enc_key));
272         MD5Init(&md5);
273         MD5Update(&md5, gensec_skey->data, gensec_skey->length);
274         MD5Update(&md5, confounder.data, confounder.length);
275         MD5Final(enc_key.data, &md5);
276
277         /* 
278          * the first 4 byte are the crc32 checksum
279          * of the remaining bytes
280          */
281         crc32_calc = crc32_calc_buffer(in->data, in->length);
282         SIVAL(enc_buffer.data, 16, crc32_calc);
283
284         /*
285          * copy the plain buffer part and 
286          * encrypt it using the created encryption key using arcfour
287          */
288         memcpy(enc_buffer.data+20, in->data, in->length); 
289         talloc_free(rid_crypt_out.data);
290
291         arcfour_crypt_blob(enc_buffer.data+16, enc_buffer.length-16, &enc_key);
292
293         *out = enc_buffer;
294
295         return WERR_OK;
296 }
297
298 /*
299   encrypt a DRSUAPI attribute ready for sending over the wire
300   Only some attribute types are encrypted
301  */
302 WERROR drsuapi_encrypt_attribute(TALLOC_CTX *mem_ctx, 
303                                  const DATA_BLOB *gensec_skey,
304                                  uint32_t rid,
305                                  struct drsuapi_DsReplicaAttribute *attr)
306 {
307         WERROR status;
308         DATA_BLOB *plain_data;
309         DATA_BLOB enc_data;
310         bool rid_crypt = false;
311
312         if (attr->value_ctr.num_values == 0) {
313                 return WERR_OK;
314         }
315
316         switch (attr->attid) {
317         case DRSUAPI_ATTID_dBCSPwd:
318         case DRSUAPI_ATTID_unicodePwd:
319         case DRSUAPI_ATTID_ntPwdHistory:
320         case DRSUAPI_ATTID_lmPwdHistory:
321                 rid_crypt = true;
322                 break;
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:
330                 break;
331         default:
332                 return WERR_OK;
333         }
334
335         if (attr->value_ctr.num_values > 1) {
336                 return WERR_DS_DRA_INVALID_PARAMETER;
337         }
338
339         if (!attr->value_ctr.values[0].blob) {
340                 return WERR_DS_DRA_INVALID_PARAMETER;
341         }
342
343         plain_data      = attr->value_ctr.values[0].blob;
344
345         status = drsuapi_encrypt_attribute_value(mem_ctx,
346                                                  gensec_skey,
347                                                  rid_crypt,
348                                                  rid,
349                                                  plain_data,
350                                                  &enc_data);
351         W_ERROR_NOT_OK_RETURN(status);
352
353         talloc_free(attr->value_ctr.values[0].blob->data);
354         *attr->value_ctr.values[0].blob = enc_data;
355
356         return WERR_OK;
357 }
358