Line data Source code
1 : /*
2 : SSSD
3 :
4 : Password obfuscation logic
5 :
6 : Author: Jakub Hrozek <jhrozek@redhat.com>
7 :
8 : Copyright (C) Red Hat, Inc 2010
9 :
10 : This program is free software; you can redistribute it and/or modify
11 : it under the terms of the GNU General Public License as published by
12 : the Free Software Foundation; either version 3 of the License, or
13 : (at your option) any later version.
14 :
15 : This program is distributed in the hope that it will be useful,
16 : but WITHOUT ANY WARRANTY; without even the implied warranty of
17 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 : GNU General Public License for more details.
19 :
20 : You should have received a copy of the GNU General Public License
21 : along with this program. If not, see <http://www.gnu.org/licenses/>.
22 : */
23 :
24 : /*
25 : * READ ME:
26 : *
27 : * Please note that password obfuscation does not improve security in any
28 : * way. It is just a mechanism to make the password human-unreadable. If you
29 : * need to secure passwords in your application, you should probably take a
30 : * look at storing passwords in NSS-backed database.
31 : */
32 :
33 : #include "config.h"
34 :
35 : #include <prerror.h>
36 : #include <nss.h>
37 : #include <pk11func.h>
38 : #include <base64.h>
39 : #include <talloc.h>
40 :
41 : #include "util/util.h"
42 : #include "util/crypto/sss_crypto.h"
43 : #include "util/crypto/nss/nss_util.h"
44 :
45 : #define OBF_BUFFER_SENTINEL "\0\1\2\3"
46 : #define OBF_BUFFER_SENTINEL_SIZE 4
47 :
48 : #define MAKE_SECITEM(sdata, slen, sitem) do { \
49 : (sitem)->type = (siBuffer); \
50 : (sitem)->data = (sdata); \
51 : (sitem)->len = (slen); \
52 : } while(0)
53 :
54 : struct sss_nss_crypto_ctx {
55 : PK11SlotInfo *slot;
56 : PK11Context *ectx;
57 : PK11SymKey *keyobj;
58 : SECItem *sparam;
59 :
60 : SECItem *iv;
61 : SECItem *key;
62 : };
63 :
64 : struct crypto_mech_data {
65 : CK_MECHANISM_TYPE cipher;
66 : uint16_t keylen;
67 : uint16_t bsize;
68 : };
69 :
70 : static struct crypto_mech_data cmdata[] = {
71 : /* AES with automatic padding, 256b key, 128b block */
72 : { CKM_AES_CBC_PAD, 32, 16 },
73 : /* sentinel */
74 : { 0, 0, 0 }
75 : };
76 :
77 6 : static struct crypto_mech_data *get_crypto_mech_data(enum obfmethod meth)
78 : {
79 6 : if (meth >= NUM_OBFMETHODS) {
80 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unsupported cipher type\n");
81 0 : return NULL;
82 : }
83 6 : return &cmdata[meth];
84 : }
85 :
86 6 : static int generate_random_key(TALLOC_CTX *mem_ctx,
87 : PK11SlotInfo *slot,
88 : struct crypto_mech_data *mech_props,
89 : SECItem **_key)
90 : {
91 : SECStatus sret;
92 : SECItem *randkeydata;
93 6 : SECItem *key = NULL;
94 : PK11SymKey *randkey;
95 : int ret;
96 :
97 6 : randkey = PK11_KeyGen(slot, mech_props->cipher,
98 6 : NULL, mech_props->keylen, NULL);
99 6 : if (randkey == NULL) {
100 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failure to generate key (err %d)\n",
101 : PR_GetError());
102 0 : ret = EIO;
103 0 : goto done;
104 : }
105 :
106 6 : sret = PK11_ExtractKeyValue(randkey);
107 6 : if (sret != SECSuccess) {
108 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failure to extract key value (err %d)\n",
109 : PR_GetError());
110 0 : ret = EIO;
111 0 : goto done;
112 : }
113 :
114 6 : randkeydata = PK11_GetKeyData(randkey);
115 6 : if (randkeydata == NULL) {
116 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failure to get key data (err %d)\n",
117 : PR_GetError());
118 0 : ret = EIO;
119 0 : goto done;
120 : }
121 :
122 : /* randkeydata is valid until randkey is. Copy with talloc to
123 : * get a nice memory hierarchy symmetrical in encrypt
124 : * and decrypt case */
125 6 : key = talloc_zero(mem_ctx, SECItem);
126 6 : if (!key) {
127 0 : ret = ENOMEM;
128 0 : goto done;
129 : }
130 :
131 6 : key->data = talloc_memdup(key, randkeydata->data, randkeydata->len);
132 6 : if (!key->data) {
133 0 : ret = ENOMEM;
134 0 : goto done;
135 : }
136 6 : key->len = randkeydata->len;
137 :
138 6 : *_key = key;
139 6 : ret = EOK;
140 : done:
141 6 : if (ret != EOK) talloc_zfree(key);
142 6 : PK11_FreeSymKey(randkey);
143 6 : return ret;
144 : }
145 :
146 6 : static int sss_nss_crypto_ctx_destructor(struct sss_nss_crypto_ctx *cctx)
147 : {
148 6 : if (cctx->ectx) PK11_DestroyContext(cctx->ectx, PR_TRUE);
149 6 : if (cctx->sparam) SECITEM_FreeItem(cctx->sparam, PR_TRUE);
150 6 : if (cctx->slot) PK11_FreeSlot(cctx->slot);
151 6 : if (cctx->keyobj) PK11_FreeSymKey(cctx->keyobj);
152 :
153 6 : return EOK;
154 : }
155 :
156 6 : static int nss_ctx_init(TALLOC_CTX *mem_ctx,
157 : struct crypto_mech_data *mech_props,
158 : struct sss_nss_crypto_ctx **_cctx)
159 : {
160 : struct sss_nss_crypto_ctx *cctx;
161 : int ret;
162 :
163 6 : cctx = talloc_zero(mem_ctx, struct sss_nss_crypto_ctx);
164 6 : if (!cctx) {
165 0 : return ENOMEM;
166 : }
167 6 : talloc_set_destructor(cctx, sss_nss_crypto_ctx_destructor);
168 :
169 6 : cctx->slot = PK11_GetBestSlot(mech_props->cipher, NULL);
170 6 : if (cctx->slot == NULL) {
171 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unable to find security device (err %d)\n",
172 : PR_GetError());
173 0 : ret = EIO;
174 0 : goto done;
175 : }
176 :
177 6 : ret = EOK;
178 6 : *_cctx = cctx;
179 : done:
180 6 : if (ret) talloc_zfree(cctx);
181 6 : return ret;
182 : }
183 :
184 6 : static int nss_encrypt_decrypt_init(struct crypto_mech_data *mech_props,
185 : bool do_encrypt,
186 : struct sss_nss_crypto_ctx *cctx)
187 : {
188 : CK_ATTRIBUTE_TYPE op;
189 : int ret;
190 :
191 6 : op = do_encrypt ? CKA_ENCRYPT : CKA_DECRYPT;
192 :
193 : /* turn the raw key into a key object */
194 6 : cctx->keyobj = PK11_ImportSymKey(cctx->slot, mech_props->cipher,
195 : PK11_OriginUnwrap, op, cctx->key, NULL);
196 6 : if (cctx->keyobj == NULL) {
197 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failure to import key into NSS (err %d)\n",
198 : PR_GetError());
199 0 : ret = EIO;
200 0 : goto done;
201 : }
202 :
203 : /* turn the raw IV into a initialization vector object */
204 6 : cctx->sparam = PK11_ParamFromIV(mech_props->cipher, cctx->iv);
205 6 : if (cctx->sparam == NULL) {
206 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failure to set up PKCS11 param (err %d)\n",
207 : PR_GetError());
208 0 : ret = EIO;
209 0 : goto done;
210 : }
211 :
212 : /* Create cipher context */
213 6 : cctx->ectx = PK11_CreateContextBySymKey(mech_props->cipher, op,
214 : cctx->keyobj, cctx->sparam);
215 6 : if (cctx->ectx == NULL) {
216 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create cipher context (err %d)\n",
217 : PORT_GetError());
218 0 : ret = EIO;
219 0 : goto done;
220 : }
221 :
222 6 : ret = EOK;
223 : done:
224 6 : return ret;
225 : }
226 :
227 3 : int sss_password_encrypt(TALLOC_CTX *mem_ctx, const char *password, int plen,
228 : enum obfmethod meth, char **obfpwd)
229 : {
230 : SECStatus sret;
231 : int ret;
232 3 : TALLOC_CTX *tmp_ctx = NULL;
233 : struct crypto_mech_data *mech_props;
234 : struct sss_nss_crypto_ctx *cctx;
235 :
236 : unsigned char *plaintext;
237 :
238 : unsigned char *cryptotext;
239 : int ct_maxsize;
240 : int ctlen;
241 : unsigned int digestlen;
242 : int result_len;
243 :
244 : unsigned char *obfbuf;
245 3 : size_t obufsize = 0;
246 3 : size_t p = 0;
247 :
248 3 : tmp_ctx = talloc_new(mem_ctx);
249 3 : if (!tmp_ctx) {
250 0 : return ENOMEM;
251 : }
252 :
253 : /* initialize NSS if needed */
254 3 : ret = nspr_nss_init();
255 3 : if (ret != EOK) {
256 0 : ret = EIO;
257 0 : goto done;
258 : }
259 :
260 3 : mech_props = get_crypto_mech_data(meth);
261 3 : if (mech_props == NULL) {
262 0 : ret = EINVAL;
263 0 : goto done;
264 : }
265 :
266 3 : ret = nss_ctx_init(tmp_ctx, mech_props, &cctx);
267 3 : if (ret) {
268 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Cannot initialize NSS context\n");
269 0 : goto done;
270 : }
271 :
272 : /* generate random encryption and IV key */
273 3 : ret = generate_random_key(cctx, cctx->slot, mech_props, &cctx->key);
274 3 : if (ret != EOK) {
275 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Could not generate encryption key\n");
276 0 : goto done;
277 : }
278 :
279 3 : ret = generate_random_key(cctx, cctx->slot, mech_props, &cctx->iv);
280 3 : if (ret != EOK) {
281 0 : DEBUG(SSSDBG_CRIT_FAILURE,
282 : "Could not generate initialization vector\n");
283 0 : goto done;
284 : }
285 :
286 3 : ret = nss_encrypt_decrypt_init(mech_props, true, cctx);
287 3 : if (ret) {
288 0 : DEBUG(SSSDBG_CRIT_FAILURE,
289 : "Cannot initialize NSS context properties\n");
290 0 : goto done;
291 : }
292 :
293 3 : plaintext = (unsigned char *) talloc_strndup(tmp_ctx, password, plen);
294 3 : if (!plaintext) {
295 0 : ret = ENOMEM;
296 0 : goto done;
297 : }
298 :
299 : /* cryptotext buffer must be at least len(plaintext)+blocksize */
300 3 : ct_maxsize = plen + (mech_props->bsize);
301 3 : cryptotext = talloc_array(tmp_ctx, unsigned char, ct_maxsize);
302 3 : if (!cryptotext) {
303 0 : ret = ENOMEM;
304 0 : goto done;
305 : }
306 :
307 : /* sample data we'll encrypt and decrypt */
308 3 : sret = PK11_CipherOp(cctx->ectx, cryptotext, &ctlen, ct_maxsize,
309 : plaintext, plen);
310 3 : if (sret != SECSuccess) {
311 0 : DEBUG(SSSDBG_CRIT_FAILURE,
312 : "Cannot execute the encryption operation (err %d)\n",
313 : PR_GetError());
314 0 : ret = EIO;
315 0 : goto done;
316 : }
317 :
318 3 : sret = PK11_DigestFinal(cctx->ectx, cryptotext+ctlen, &digestlen,
319 3 : ct_maxsize-ctlen);
320 3 : if (sret != SECSuccess) {
321 0 : DEBUG(SSSDBG_CRIT_FAILURE,
322 : "Cannot execute the digest operation (err %d)\n",
323 : PR_GetError());
324 0 : ret = EIO;
325 0 : goto done;
326 : }
327 3 : result_len = ctlen + digestlen;
328 3 : if (result_len < 0 || result_len > UINT16_MAX) {
329 0 : ret = ERANGE;
330 0 : goto done;
331 : }
332 :
333 : /* Pack the obfuscation buffer */
334 : /* The buffer consists of:
335 : * uint16_t the type of the cipher
336 : * uint16_t length of the cryptotext in bytes (clen)
337 : * uint8_t[klen] key
338 : * uint8_t[blen] IV
339 : * uint8_t[clen] cryptotext
340 : * 4 bytes of "sentinel" denoting end of the buffer
341 : */
342 3 : obufsize = sizeof(uint16_t) + sizeof(uint16_t) +
343 3 : mech_props->keylen + mech_props->bsize +
344 : result_len + OBF_BUFFER_SENTINEL_SIZE;
345 3 : obfbuf = talloc_array(tmp_ctx, unsigned char, obufsize);
346 3 : if (!obfbuf) {
347 0 : ret = ENOMEM;
348 0 : goto done;
349 : }
350 :
351 3 : DEBUG(SSSDBG_TRACE_INTERNAL, "Writing method: %d\n", meth);
352 3 : SAFEALIGN_SET_UINT16(&obfbuf[p], meth, &p);
353 3 : DEBUG(SSSDBG_TRACE_INTERNAL, "Writing bufsize: %d\n", result_len);
354 3 : SAFEALIGN_SET_UINT16(&obfbuf[p], result_len, &p);
355 3 : safealign_memcpy(&obfbuf[p], cctx->key->data, mech_props->keylen, &p);
356 3 : safealign_memcpy(&obfbuf[p], cctx->iv->data, mech_props->bsize, &p);
357 3 : safealign_memcpy(&obfbuf[p], cryptotext, result_len, &p);
358 3 : safealign_memcpy(&obfbuf[p], OBF_BUFFER_SENTINEL,
359 : OBF_BUFFER_SENTINEL_SIZE, &p);
360 :
361 : /* Base64 encode the resulting buffer */
362 3 : *obfpwd = sss_base64_encode(mem_ctx, obfbuf, obufsize);
363 3 : if (*obfpwd == NULL) {
364 0 : ret = ENOMEM;
365 0 : goto done;
366 : }
367 :
368 3 : ret = EOK;
369 : done:
370 3 : talloc_free(tmp_ctx);
371 3 : nspr_nss_cleanup();
372 3 : return ret;
373 : }
374 :
375 3 : int sss_password_decrypt(TALLOC_CTX *mem_ctx, char *b64encoded,
376 : char **password)
377 : {
378 : SECStatus sret;
379 : int ret;
380 3 : TALLOC_CTX *tmp_ctx = NULL;
381 : struct crypto_mech_data *mech_props;
382 : struct sss_nss_crypto_ctx *cctx;
383 :
384 : int plainlen;
385 : unsigned int digestlen;
386 3 : unsigned char *obfbuf = NULL;
387 : size_t obflen;
388 : char *pwdbuf;
389 :
390 : /* for unmarshaling data */
391 : uint16_t meth;
392 : uint16_t ctsize;
393 3 : size_t p = 0;
394 : unsigned char *cryptotext;
395 : unsigned char *keybuf;
396 : unsigned char *ivbuf;
397 : unsigned char sentinel_check[OBF_BUFFER_SENTINEL_SIZE];
398 :
399 3 : tmp_ctx = talloc_new(mem_ctx);
400 3 : if (!tmp_ctx) {
401 0 : return ENOMEM;
402 : }
403 :
404 : /* initialize NSS if needed */
405 3 : ret = nspr_nss_init();
406 3 : if (ret != EOK) {
407 0 : ret = EIO;
408 0 : goto done;
409 : }
410 :
411 : /* Base64 decode the incoming buffer */
412 3 : obfbuf = sss_base64_decode(tmp_ctx, b64encoded, &obflen);
413 3 : if (!obfbuf) {
414 0 : ret = ENOMEM;
415 0 : goto done;
416 : }
417 :
418 : /* unpack obfuscation buffer */
419 3 : SAFEALIGN_COPY_UINT16_CHECK(&meth, obfbuf+p, obflen, &p);
420 3 : DEBUG(SSSDBG_TRACE_INTERNAL, "Read method: %d\n", meth);
421 3 : SAFEALIGN_COPY_UINT16_CHECK(&ctsize, obfbuf+p, obflen, &p);
422 3 : DEBUG(SSSDBG_TRACE_INTERNAL, "Read bufsize: %d\n", ctsize);
423 :
424 3 : mech_props = get_crypto_mech_data(meth);
425 3 : if (mech_props == NULL) {
426 0 : ret = EINVAL;
427 0 : goto done;
428 : }
429 :
430 : /* check that we got sane mechanism properties and cryptotext size */
431 3 : memcpy(sentinel_check,
432 3 : obfbuf + p + mech_props->keylen + mech_props->bsize + ctsize,
433 : OBF_BUFFER_SENTINEL_SIZE);
434 3 : if (memcmp(sentinel_check, OBF_BUFFER_SENTINEL, OBF_BUFFER_SENTINEL_SIZE) != 0) {
435 0 : DEBUG(SSSDBG_FATAL_FAILURE,
436 : "Obfuscation buffer seems corrupt, aborting\n");
437 0 : ret = EFAULT;
438 0 : goto done;
439 : }
440 :
441 : /* copy out key, ivbuf and cryptotext */
442 3 : keybuf = talloc_array(tmp_ctx, unsigned char, mech_props->keylen);
443 3 : if (keybuf == NULL) {
444 0 : ret = ENOMEM;
445 0 : goto done;
446 : }
447 3 : safealign_memcpy(keybuf, obfbuf+p, mech_props->keylen, &p);
448 :
449 3 : ivbuf = talloc_array(tmp_ctx, unsigned char, mech_props->bsize);
450 3 : if (ivbuf == NULL) {
451 0 : ret = ENOMEM;
452 0 : goto done;
453 : }
454 3 : safealign_memcpy(ivbuf, obfbuf+p, mech_props->bsize, &p);
455 :
456 3 : cryptotext = talloc_array(tmp_ctx, unsigned char, ctsize);
457 3 : if (cryptotext == NULL) {
458 0 : ret = ENOMEM;
459 0 : goto done;
460 : }
461 3 : safealign_memcpy(cryptotext, obfbuf+p, ctsize, &p);
462 :
463 3 : ret = nss_ctx_init(tmp_ctx, mech_props, &cctx);
464 3 : if (ret) {
465 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Cannot initialize NSS context\n");
466 0 : goto done;
467 : }
468 :
469 3 : cctx->iv = talloc_zero(cctx, SECItem);
470 3 : cctx->key = talloc_zero(cctx, SECItem);
471 3 : if (!cctx->iv || !cctx->key) {
472 0 : ret = ENOMEM;
473 0 : goto done;
474 : }
475 :
476 3 : MAKE_SECITEM(ivbuf, mech_props->bsize, cctx->iv);
477 3 : MAKE_SECITEM(keybuf, mech_props->keylen, cctx->key);
478 :
479 3 : ret = nss_encrypt_decrypt_init(mech_props, false, cctx);
480 3 : if (ret) {
481 0 : goto done;
482 : }
483 :
484 3 : pwdbuf = talloc_array(tmp_ctx, char, ctsize);
485 3 : if (!pwdbuf) {
486 0 : ret = ENOMEM;
487 0 : goto done;
488 : }
489 :
490 3 : sret = PK11_CipherOp(cctx->ectx, (unsigned char *) pwdbuf, &plainlen, ctsize,
491 : cryptotext, ctsize);
492 3 : if (sret != SECSuccess) {
493 0 : DEBUG(SSSDBG_CRIT_FAILURE,
494 : "Cannot execute the encryption operation (err %d)\n",
495 : PR_GetError());
496 0 : ret = EIO;
497 0 : goto done;
498 : }
499 :
500 3 : sret = PK11_DigestFinal(cctx->ectx, (unsigned char *) pwdbuf+plainlen, &digestlen,
501 3 : ctsize - plainlen);
502 3 : if (sret != SECSuccess) {
503 0 : DEBUG(SSSDBG_CRIT_FAILURE,
504 : "Cannot execute the encryption operation (err %d)\n",
505 : PR_GetError());
506 0 : ret = EIO;
507 0 : goto done;
508 : }
509 :
510 3 : *password = talloc_move(mem_ctx, &pwdbuf);
511 3 : ret = EOK;
512 : done:
513 3 : talloc_free(tmp_ctx);
514 3 : nspr_nss_cleanup();
515 3 : return ret;
516 : }
|