Branch data 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(1, ("Unsupported cipher type\n"));
[ # # ][ # # ]
[ # # ]
81 : : 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(1, ("Failure to generate key (err %d)\n",
[ # # ][ # # ]
[ # # ]
101 : : PR_GetError()));
102 : : ret = EIO;
103 : : goto done;
104 : : }
105 : :
106 : 6 : sret = PK11_ExtractKeyValue(randkey);
107 [ - + ]: 6 : if (sret != SECSuccess) {
108 [ # # ][ # # ]: 0 : DEBUG(1, ("Failure to extract key value (err %d)\n",
[ # # ][ # # ]
[ # # ]
109 : : PR_GetError()));
110 : : ret = EIO;
111 : : goto done;
112 : : }
113 : :
114 : 6 : randkeydata = PK11_GetKeyData(randkey);
115 [ - + ]: 6 : if (randkey == NULL) {
116 [ # # ][ # # ]: 0 : DEBUG(1, ("Failure to get key data (err %d)\n",
[ # # ][ # # ]
[ # # ]
117 : : PR_GetError()));
118 : : ret = EIO;
119 : : 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 : : ret = ENOMEM;
128 : : goto done;
129 : : }
130 : :
131 : 6 : key->data = talloc_memdup(key, randkeydata->data, randkeydata->len);
132 [ + - ]: 6 : if (!key->data) {
133 : : ret = ENOMEM;
134 : : 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 : : 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(1, ("Unable to find security device (err %d)\n",
[ # # ][ # # ]
[ # # ]
172 : : PR_GetError()));
173 : : ret = EIO;
174 : : goto done;
175 : : }
176 : :
177 : 6 : ret = EOK;
178 : 6 : *_cctx = cctx;
179 : : done:
180 [ - + ]: 6 : if (ret) talloc_zfree(cctx);
181 : : 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(1, ("Failure to import key into NSS (err %d)\n",
[ # # ][ # # ]
[ # # ]
198 : : PR_GetError()));
199 : : ret = EIO;
200 : : 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(1, ("Failure to set up PKCS11 param (err %d)\n",
[ # # ][ # # ]
[ # # ]
207 : : PR_GetError()));
208 : : ret = EIO;
209 : : 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(1, ("Cannot create cipher context (err %d)\n",
[ # # ][ # # ]
[ # # ]
217 : : PORT_GetError()));
218 : : ret = EIO;
219 : : goto done;
220 : : }
221 : :
222 : : 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 : : return ENOMEM;
251 : : }
252 : :
253 : : /* initialize NSS if needed */
254 : 3 : ret = nspr_nss_init();
255 [ + - ]: 3 : if (ret != EOK) {
256 : : ret = EIO;
257 : : goto done;
258 : : }
259 : :
260 : 3 : mech_props = get_crypto_mech_data(meth);
261 [ + - ]: 3 : if (mech_props == NULL) {
262 : : ret = EINVAL;
263 : : goto done;
264 : : }
265 : :
266 : 3 : ret = nss_ctx_init(tmp_ctx, mech_props, &cctx);
267 [ - + ]: 3 : if (ret) {
268 [ # # ][ # # ]: 0 : DEBUG(1, ("Cannot initialize NSS context\n"));
[ # # ][ # # ]
[ # # ]
269 : : 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(1, ("Could not generate encryption key\n"));
[ # # ][ # # ]
[ # # ]
276 : : 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(1, ("Could not generate initialization vector\n"));
[ # # ][ # # ]
[ # # ]
282 : : goto done;
283 : : }
284 : :
285 : 3 : ret = nss_encrypt_decrypt_init(mech_props, true, cctx);
286 [ - + ]: 3 : if (ret) {
287 [ # # ][ # # ]: 0 : DEBUG(1, ("Cannot initialize NSS context properties\n"));
[ # # ][ # # ]
[ # # ]
288 : : goto done;
289 : : }
290 : :
291 : 3 : plaintext = (unsigned char *) talloc_strndup(tmp_ctx, password, plen);
292 [ + - ]: 3 : if (!plaintext) {
293 : : ret = ENOMEM;
294 : : goto done;
295 : : }
296 : :
297 : : /* cryptotext buffer must be at least len(plaintext)+blocksize */
298 : 3 : ct_maxsize = plen + (mech_props->bsize);
299 : 3 : cryptotext = talloc_array(tmp_ctx, unsigned char, ct_maxsize);
300 [ + - ]: 3 : if (!cryptotext) {
301 : : ret = ENOMEM;
302 : : goto done;
303 : : }
304 : :
305 : : /* sample data we'll encrypt and decrypt */
306 : 3 : sret = PK11_CipherOp(cctx->ectx, cryptotext, &ctlen, ct_maxsize,
307 : : plaintext, plen);
308 [ - + ]: 3 : if (sret != SECSuccess) {
309 [ # # ][ # # ]: 0 : DEBUG(1, ("Cannot execute the encryption operation (err %d)\n",
[ # # ][ # # ]
[ # # ]
310 : : PR_GetError()));
311 : : ret = EIO;
312 : : goto done;
313 : : }
314 : :
315 : 3 : sret = PK11_DigestFinal(cctx->ectx, cryptotext+ctlen, &digestlen,
316 : 3 : ct_maxsize-ctlen);
317 [ - + ]: 3 : if (sret != SECSuccess) {
318 [ # # ][ # # ]: 0 : DEBUG(1, ("Cannot execute the digest operation (err %d)\n",
[ # # ][ # # ]
[ # # ]
319 : : PR_GetError()));
320 : : ret = EIO;
321 : : goto done;
322 : : }
323 : 3 : result_len = ctlen + digestlen;
324 : :
325 : : /* Pack the obfuscation buffer */
326 : : /* The buffer consists of:
327 : : * uint16_t the type of the cipher
328 : : * uint32_t length of the cryptotext in bytes (clen)
329 : : * uint8_t[klen] key
330 : : * uint8_t[blen] IV
331 : : * uint8_t[clen] cryptotext
332 : : * 4 bytes of "sentinel" denoting end of the buffer
333 : : */
334 : 3 : obufsize = sizeof(uint16_t) + sizeof(uint32_t) +
335 : 3 : mech_props->keylen + mech_props->bsize +
336 : : result_len + OBF_BUFFER_SENTINEL_SIZE;
337 : 3 : obfbuf = talloc_array(tmp_ctx, unsigned char, obufsize);
338 [ + - ]: 3 : if (!obfbuf) {
339 : : ret = ENOMEM;
340 : : goto done;
341 : : }
342 : :
343 [ + - ][ + - ]: 3 : DEBUG(8, ("Writing method: %d\n", meth));
[ - + ][ # # ]
[ # # ]
344 : 3 : SAFEALIGN_SET_UINT16(&obfbuf[p], meth, &p);
345 [ + - ][ + - ]: 3 : DEBUG(8, ("Writing bufsize: %d\n", result_len));
[ - + ][ # # ]
[ # # ]
346 : 3 : SAFEALIGN_SET_UINT16(&obfbuf[p], result_len, &p);
347 : 3 : safealign_memcpy(&obfbuf[p], cctx->key->data, mech_props->keylen, &p);
348 : 3 : safealign_memcpy(&obfbuf[p], cctx->iv->data, mech_props->bsize, &p);
349 : 3 : safealign_memcpy(&obfbuf[p], cryptotext, result_len, &p);
350 : 3 : safealign_memcpy(&obfbuf[p], OBF_BUFFER_SENTINEL,
351 : : OBF_BUFFER_SENTINEL_SIZE, &p);
352 : :
353 : : /* Base64 encode the resulting buffer */
354 : 3 : *obfpwd = sss_base64_encode(mem_ctx, obfbuf, obufsize);
355 [ + - ]: 3 : if (*obfpwd == NULL) {
356 : : ret = ENOMEM;
357 : : goto done;
358 : : }
359 : :
360 : 3 : ret = EOK;
361 : : done:
362 : 3 : talloc_free(tmp_ctx);
363 : 3 : nspr_nss_cleanup();
364 : : return ret;
365 : : }
366 : :
367 : 3 : int sss_password_decrypt(TALLOC_CTX *mem_ctx, char *b64encoded,
368 : : char **password)
369 : : {
370 : : SECStatus sret;
371 : : int ret;
372 : 3 : TALLOC_CTX *tmp_ctx = NULL;
373 : : struct crypto_mech_data *mech_props;
374 : : struct sss_nss_crypto_ctx *cctx;
375 : :
376 : : int plainlen;
377 : : unsigned int digestlen;
378 : 3 : unsigned char *obfbuf = NULL;
379 : : size_t obflen;
380 : : char *pwdbuf;
381 : :
382 : : /* for unmarshaling data */
383 : : uint16_t meth;
384 : : uint16_t ctsize;
385 : 3 : size_t p = 0;
386 : : unsigned char *cryptotext;
387 : : unsigned char *keybuf;
388 : : unsigned char *ivbuf;
389 : : unsigned char sentinel_check[OBF_BUFFER_SENTINEL_SIZE];
390 : :
391 : 3 : tmp_ctx = talloc_new(mem_ctx);
392 [ + - ]: 3 : if (!tmp_ctx) {
393 : : return ENOMEM;
394 : : }
395 : :
396 : : /* initialize NSS if needed */
397 : 3 : ret = nspr_nss_init();
398 [ + - ]: 3 : if (ret != EOK) {
399 : : ret = EIO;
400 : : goto done;
401 : : }
402 : :
403 : : /* Base64 decode the incoming buffer */
404 : 3 : obfbuf = sss_base64_decode(tmp_ctx, b64encoded, &obflen);
405 [ + - ]: 3 : if (!obfbuf) {
406 : : ret = ENOMEM;
407 : : goto done;
408 : : }
409 : :
410 : : /* unpack obfuscation buffer */
411 [ + - ][ + - ]: 3 : SAFEALIGN_COPY_UINT16_CHECK(&meth, obfbuf+p, obflen, &p);
412 [ + - ][ + - ]: 3 : DEBUG(8, ("Read method: %d\n", meth));
[ - + ][ # # ]
[ # # ]
413 [ + - ][ + - ]: 3 : SAFEALIGN_COPY_UINT16_CHECK(&ctsize, obfbuf+p, obflen, &p);
414 [ + - ][ + - ]: 3 : DEBUG(8, ("Read bufsize: %d\n", ctsize));
[ - + ][ # # ]
[ # # ]
415 : :
416 : 3 : mech_props = get_crypto_mech_data(meth);
417 [ + - ]: 3 : if (mech_props == NULL) {
418 : : ret = EINVAL;
419 : : goto done;
420 : : }
421 : :
422 : : /* check that we got sane mechanism properties and cryptotext size */
423 : 3 : memcpy(sentinel_check,
424 : 3 : obfbuf + p + mech_props->keylen + mech_props->bsize + ctsize,
425 : : OBF_BUFFER_SENTINEL_SIZE);
426 [ - + ]: 3 : if (memcmp(sentinel_check, OBF_BUFFER_SENTINEL, OBF_BUFFER_SENTINEL_SIZE) != 0) {
427 [ # # ][ # # ]: 0 : DEBUG(0, ("Obfuscation buffer seems corrupt, aborting\n"));
[ # # ][ # # ]
[ # # ]
428 : : ret = EFAULT;
429 : : goto done;
430 : : }
431 : :
432 : : /* copy out key, ivbuf and cryptotext */
433 : 3 : keybuf = talloc_array(tmp_ctx, unsigned char, mech_props->keylen);
434 [ + - ]: 3 : if (keybuf == NULL) {
435 : : ret = ENOMEM;
436 : : goto done;
437 : : }
438 : 3 : safealign_memcpy(keybuf, obfbuf+p, mech_props->keylen, &p);
439 : :
440 : 3 : ivbuf = talloc_array(tmp_ctx, unsigned char, mech_props->bsize);
441 [ + - ]: 3 : if (ivbuf == NULL) {
442 : : ret = ENOMEM;
443 : : goto done;
444 : : }
445 : 3 : safealign_memcpy(ivbuf, obfbuf+p, mech_props->bsize, &p);
446 : :
447 : 3 : cryptotext = talloc_array(tmp_ctx, unsigned char, ctsize);
448 [ + - ]: 3 : if (cryptotext == NULL) {
449 : : ret = ENOMEM;
450 : : goto done;
451 : : }
452 : 3 : safealign_memcpy(cryptotext, obfbuf+p, ctsize, &p);
453 : :
454 : 3 : ret = nss_ctx_init(tmp_ctx, mech_props, &cctx);
455 [ - + ]: 3 : if (ret) {
456 [ # # ][ # # ]: 0 : DEBUG(1, ("Cannot initialize NSS context\n"));
[ # # ][ # # ]
[ # # ]
457 : : goto done;
458 : : }
459 : :
460 : 3 : cctx->iv = talloc_zero(cctx, SECItem);
461 : 3 : cctx->key = talloc_zero(cctx, SECItem);
462 [ + - ][ + - ]: 3 : if (!cctx->iv || !cctx->key) {
463 : : ret = ENOMEM;
464 : : goto done;
465 : : }
466 : :
467 : 3 : MAKE_SECITEM(ivbuf, mech_props->bsize, cctx->iv);
468 : 3 : MAKE_SECITEM(keybuf, mech_props->keylen, cctx->key);
469 : :
470 : 3 : ret = nss_encrypt_decrypt_init(mech_props, false, cctx);
471 [ + - ]: 3 : if (ret) {
472 : : goto done;
473 : : }
474 : :
475 : 3 : pwdbuf = talloc_array(tmp_ctx, char, ctsize);
476 [ + - ]: 3 : if (!pwdbuf) {
477 : : ret = ENOMEM;
478 : : goto done;
479 : : }
480 : :
481 : 3 : sret = PK11_CipherOp(cctx->ectx, (unsigned char *) pwdbuf, &plainlen, ctsize,
482 : : cryptotext, ctsize);
483 [ - + ]: 3 : if (sret != SECSuccess) {
484 [ # # ][ # # ]: 0 : DEBUG(1, ("Cannot execute the encryption operation (err %d)\n",
[ # # ][ # # ]
[ # # ]
485 : : PR_GetError()));
486 : : ret = EIO;
487 : : goto done;
488 : : }
489 : :
490 : 3 : sret = PK11_DigestFinal(cctx->ectx, (unsigned char *) pwdbuf+plainlen, &digestlen,
491 : 3 : ctsize - plainlen);
492 [ - + ]: 3 : if (sret != SECSuccess) {
493 [ # # ][ # # ]: 0 : DEBUG(1, ("Cannot execute the encryption operation (err %d)\n",
[ # # ][ # # ]
[ # # ]
494 : : PR_GetError()));
495 : : ret = EIO;
496 : : goto done;
497 : : }
498 : :
499 : 3 : *password = talloc_move(mem_ctx, &pwdbuf);
500 : 3 : ret = EOK;
501 : : done:
502 : 3 : talloc_free(tmp_ctx);
503 : 3 : nspr_nss_cleanup();
504 : : return ret;
505 : 12 : }
|