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