Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/crypto/krb5/src/lib/kdb/kdb_cpw.c
39566 views
1
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
/* lib/kdb/kdb_cpw.c */
3
/*
4
* Copyright 1995, 2009, 2014 by the Massachusetts Institute of Technology.
5
* All Rights Reserved.
6
*
7
* Export of this software from the United States of America may
8
* require a specific license from the United States Government.
9
* It is the responsibility of any person or organization contemplating
10
* export to obtain such a license before exporting.
11
*
12
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13
* distribute this software and its documentation for any purpose and
14
* without fee is hereby granted, provided that the above copyright
15
* notice appear in all copies and that both that copyright notice and
16
* this permission notice appear in supporting documentation, and that
17
* the name of M.I.T. not be used in advertising or publicity pertaining
18
* to distribution of the software without specific, written prior
19
* permission. Furthermore if you modify this software you must label
20
* your software as modified software and not distribute it in such a
21
* fashion that it might be confused with the original M.I.T. software.
22
* M.I.T. makes no representations about the suitability of
23
* this software for any purpose. It is provided "as is" without express
24
* or implied warranty.
25
*/
26
/*
27
* Copyright (C) 1998 by the FundsXpress, INC.
28
*
29
* All rights reserved.
30
*
31
* Export of this software from the United States of America may require
32
* a specific license from the United States Government. It is the
33
* responsibility of any person or organization contemplating export to
34
* obtain such a license before exporting.
35
*
36
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
37
* distribute this software and its documentation for any purpose and
38
* without fee is hereby granted, provided that the above copyright
39
* notice appear in all copies and that both that copyright notice and
40
* this permission notice appear in supporting documentation, and that
41
* the name of FundsXpress. not be used in advertising or publicity pertaining
42
* to distribution of the software without specific, written prior
43
* permission. FundsXpress makes no representations about the suitability of
44
* this software for any purpose. It is provided "as is" without express
45
* or implied warranty.
46
*
47
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
48
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
49
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
50
*/
51
52
#include "k5-int.h"
53
#include "kdb.h"
54
#include <stdio.h>
55
#include <errno.h>
56
57
int
58
krb5_db_get_key_data_kvno(krb5_context context, int count, krb5_key_data *data)
59
{
60
int i, kvno;
61
/* Find last key version number */
62
for (kvno = i = 0; i < count; i++) {
63
if (kvno < data[i].key_data_kvno) {
64
kvno = data[i].key_data_kvno;
65
}
66
}
67
return(kvno);
68
}
69
70
static void
71
cleanup_key_data(krb5_context context, int count, krb5_key_data *data)
72
{
73
int i;
74
75
/* If data is NULL, count is always 0 */
76
if (data == NULL) return;
77
78
for (i = 0; i < count; i++)
79
krb5_dbe_free_key_data_contents(context, &data[i]);
80
free(data);
81
}
82
83
/* Transfer key data from old_kd to new_kd, making sure that new_kd is
84
* encrypted with mkey. May steal from old_kd and zero it out. */
85
static krb5_error_code
86
preserve_one_old_key(krb5_context context, krb5_keyblock *mkey,
87
krb5_db_entry *dbent, krb5_key_data *old_kd,
88
krb5_key_data *new_kd)
89
{
90
krb5_error_code ret;
91
krb5_keyblock kb;
92
krb5_keysalt salt;
93
94
memset(new_kd, 0, sizeof(*new_kd));
95
96
ret = krb5_dbe_decrypt_key_data(context, mkey, old_kd, &kb, NULL);
97
if (ret == 0) {
98
/* old_kd is already encrypted in mkey, so just move it. */
99
*new_kd = *old_kd;
100
memset(old_kd, 0, sizeof(*old_kd));
101
krb5_free_keyblock_contents(context, &kb);
102
return 0;
103
}
104
105
/* Decrypt and re-encrypt old_kd using mkey. */
106
ret = krb5_dbe_decrypt_key_data(context, NULL, old_kd, &kb, &salt);
107
if (ret)
108
return ret;
109
ret = krb5_dbe_encrypt_key_data(context, mkey, &kb, &salt,
110
old_kd->key_data_kvno, new_kd);
111
krb5_free_keyblock_contents(context, &kb);
112
krb5_free_data_contents(context, &salt.data);
113
return ret;
114
}
115
116
/*
117
* Add key_data to dbent, making sure that each entry is encrypted in mkey. If
118
* keepold is greater than 1, preserve only the first (keepold-1) key versions,
119
* so that the total number of key versions including the new key set is
120
* keepold. May steal some elements from key_data and zero them out.
121
*/
122
static krb5_error_code
123
preserve_old_keys(krb5_context context, krb5_keyblock *mkey,
124
krb5_db_entry *dbent, unsigned int keepold, int n_key_data,
125
krb5_key_data *key_data)
126
{
127
krb5_error_code ret;
128
krb5_kvno last_kvno = 0, kvno_changes = 0;
129
int i;
130
131
for (i = 0; i < n_key_data; i++) {
132
if (keepold > 1) {
133
if (i > 0 && key_data[i].key_data_kvno != last_kvno)
134
kvno_changes++;
135
if (kvno_changes >= keepold - 1)
136
break;
137
last_kvno = key_data[i].key_data_kvno;
138
}
139
140
ret = krb5_dbe_create_key_data(context, dbent);
141
if (ret)
142
return ret;
143
ret = preserve_one_old_key(context, mkey, dbent, &key_data[i],
144
&dbent->key_data[dbent->n_key_data - 1]);
145
if (ret)
146
return ret;
147
}
148
return 0;
149
}
150
151
static krb5_error_code
152
add_key_rnd(krb5_context context, krb5_keyblock *master_key,
153
krb5_key_salt_tuple *ks_tuple, int ks_tuple_count,
154
krb5_db_entry *db_entry, int kvno)
155
{
156
krb5_keyblock key;
157
int i, j;
158
krb5_error_code retval;
159
krb5_key_data *kd_slot;
160
161
for (i = 0; i < ks_tuple_count; i++) {
162
krb5_boolean similar;
163
164
similar = 0;
165
166
/*
167
* We could use krb5_keysalt_iterate to replace this loop, or use
168
* krb5_keysalt_is_present for the loop below, but we want to avoid
169
* circular library dependencies.
170
*/
171
for (j = 0; j < i; j++) {
172
if ((retval = krb5_c_enctype_compare(context,
173
ks_tuple[i].ks_enctype,
174
ks_tuple[j].ks_enctype,
175
&similar)))
176
return(retval);
177
178
if (similar)
179
break;
180
}
181
182
if (similar)
183
continue;
184
185
if ((retval = krb5_dbe_create_key_data(context, db_entry)))
186
return retval;
187
kd_slot = &db_entry->key_data[db_entry->n_key_data - 1];
188
189
/* there used to be code here to extract the old key, and derive
190
a new key from it. Now that there's a unified prng, that isn't
191
necessary. */
192
193
/* make new key */
194
if ((retval = krb5_c_make_random_key(context, ks_tuple[i].ks_enctype,
195
&key)))
196
return retval;
197
198
retval = krb5_dbe_encrypt_key_data(context, master_key, &key, NULL,
199
kvno, kd_slot);
200
201
krb5_free_keyblock_contents(context, &key);
202
if( retval )
203
return retval;
204
}
205
206
return 0;
207
}
208
209
/* Construct a random explicit salt. */
210
static krb5_error_code
211
make_random_salt(krb5_context context, krb5_keysalt *salt_out)
212
{
213
krb5_error_code retval;
214
unsigned char rndbuf[8];
215
krb5_data salt, rnd = make_data(rndbuf, sizeof(rndbuf));
216
unsigned int i;
217
218
/*
219
* Salts are limited by RFC 4120 to 7-bit ASCII. For ease of examination
220
* and to avoid certain folding issues for older enctypes, we use printable
221
* characters with four fixed bits and four random bits, encoding 64
222
* psuedo-random bits into 16 bytes.
223
*/
224
retval = krb5_c_random_make_octets(context, &rnd);
225
if (retval)
226
return retval;
227
retval = alloc_data(&salt, sizeof(rndbuf) * 2);
228
if (retval)
229
return retval;
230
for (i = 0; i < sizeof(rndbuf); i++) {
231
salt.data[i * 2] = 0x40 | (rndbuf[i] >> 4);
232
salt.data[i * 2 + 1] = 0x40 | (rndbuf[i] & 0xf);
233
}
234
235
salt_out->type = KRB5_KDB_SALTTYPE_SPECIAL;
236
salt_out->data = salt;
237
return 0;
238
}
239
240
/*
241
* Add key_data for a krb5_db_entry
242
* If passwd is NULL the assumes that the caller wants a random password.
243
*/
244
static krb5_error_code
245
add_key_pwd(krb5_context context, krb5_keyblock *master_key,
246
krb5_key_salt_tuple *ks_tuple, int ks_tuple_count,
247
const char *passwd, krb5_db_entry *db_entry, int kvno)
248
{
249
krb5_error_code retval;
250
krb5_keysalt key_salt;
251
krb5_keyblock key;
252
krb5_data pwd;
253
int i, j;
254
krb5_key_data *kd_slot;
255
256
for (i = 0; i < ks_tuple_count; i++) {
257
krb5_boolean similar;
258
259
similar = 0;
260
261
/*
262
* We could use krb5_keysalt_iterate to replace this loop, or use
263
* krb5_keysalt_is_present for the loop below, but we want to avoid
264
* circular library dependencies.
265
*/
266
for (j = 0; j < i; j++) {
267
if ((retval = krb5_c_enctype_compare(context,
268
ks_tuple[i].ks_enctype,
269
ks_tuple[j].ks_enctype,
270
&similar)))
271
return(retval);
272
273
if (similar)
274
break;
275
}
276
277
if (j < i)
278
continue;
279
280
if ((retval = krb5_dbe_create_key_data(context, db_entry)))
281
return(retval);
282
kd_slot = &db_entry->key_data[db_entry->n_key_data - 1];
283
284
/* Convert password string to key using appropriate salt */
285
switch (key_salt.type = ks_tuple[i].ks_salttype) {
286
case KRB5_KDB_SALTTYPE_ONLYREALM: {
287
krb5_data * saltdata;
288
if ((retval = krb5_copy_data(context, krb5_princ_realm(context,
289
db_entry->princ), &saltdata)))
290
return(retval);
291
292
key_salt.data = *saltdata;
293
free(saltdata);
294
}
295
break;
296
case KRB5_KDB_SALTTYPE_NOREALM:
297
if ((retval=krb5_principal2salt_norealm(context, db_entry->princ,
298
&key_salt.data)))
299
return(retval);
300
break;
301
case KRB5_KDB_SALTTYPE_NORMAL:
302
if ((retval = krb5_principal2salt(context, db_entry->princ,
303
&key_salt.data)))
304
return(retval);
305
break;
306
case KRB5_KDB_SALTTYPE_SPECIAL:
307
retval = make_random_salt(context, &key_salt);
308
if (retval)
309
return retval;
310
break;
311
default:
312
return(KRB5_KDB_BAD_SALTTYPE);
313
}
314
315
pwd = string2data((char *)passwd);
316
317
retval = krb5_c_string_to_key_with_params(context,
318
ks_tuple[i].ks_enctype,
319
&pwd, &key_salt.data,
320
NULL, &key);
321
if (retval) {
322
free(key_salt.data.data);
323
return retval;
324
}
325
326
retval = krb5_dbe_encrypt_key_data(context, master_key, &key,
327
(const krb5_keysalt *)&key_salt,
328
kvno, kd_slot);
329
if (key_salt.data.data)
330
free(key_salt.data.data);
331
free(key.contents);
332
333
if( retval )
334
return retval;
335
}
336
337
return 0;
338
}
339
340
static krb5_error_code
341
rekey(krb5_context context, krb5_keyblock *mkey, krb5_key_salt_tuple *ks_tuple,
342
int ks_tuple_count, const char *password, int new_kvno,
343
unsigned int keepold, krb5_db_entry *db_entry)
344
{
345
krb5_error_code ret;
346
krb5_key_data *key_data;
347
int n_key_data, old_kvno;
348
349
/* Save aside the old key data. */
350
n_key_data = db_entry->n_key_data;
351
key_data = db_entry->key_data;
352
db_entry->n_key_data = 0;
353
db_entry->key_data = NULL;
354
355
/* Make sure the new kvno is greater than the old largest kvno. */
356
old_kvno = krb5_db_get_key_data_kvno(context, n_key_data, key_data);
357
if (new_kvno < old_kvno + 1)
358
new_kvno = old_kvno + 1;
359
/* Wrap from 65535 to 1; we can only store 16-bit kvno values in key_data,
360
* and we assign special meaning to kvno 0. */
361
if (new_kvno == (1 << 16))
362
new_kvno = 1;
363
364
/* Add new keys to the front of the list. */
365
if (password != NULL) {
366
ret = add_key_pwd(context, mkey, ks_tuple, ks_tuple_count, password,
367
db_entry, new_kvno);
368
} else {
369
ret = add_key_rnd(context, mkey, ks_tuple, ks_tuple_count, db_entry,
370
new_kvno);
371
}
372
if (ret) {
373
cleanup_key_data(context, db_entry->n_key_data, db_entry->key_data);
374
db_entry->n_key_data = n_key_data;
375
db_entry->key_data = key_data;
376
return ret;
377
}
378
379
/* Possibly add some or all of the old keys to the back of the list. May
380
* steal from and zero out some of the old key data entries. */
381
if (keepold > 0) {
382
ret = preserve_old_keys(context, mkey, db_entry, keepold, n_key_data,
383
key_data);
384
}
385
386
/* Free any old key data entries not stolen and zeroed out above. */
387
cleanup_key_data(context, n_key_data, key_data);
388
return ret;
389
}
390
391
/*
392
* Change random key for a krb5_db_entry
393
* Assumes the max kvno
394
*
395
* As a side effect all old keys are nuked if keepold is false.
396
*/
397
krb5_error_code
398
krb5_dbe_crk(krb5_context context, krb5_keyblock *mkey,
399
krb5_key_salt_tuple *ks_tuple, int ks_tuple_count,
400
unsigned int keepold, krb5_db_entry *dbent)
401
{
402
return rekey(context, mkey, ks_tuple, ks_tuple_count, NULL, 0, keepold,
403
dbent);
404
}
405
406
/*
407
* Add random key for a krb5_db_entry
408
* Assumes the max kvno
409
*
410
* As a side effect all old keys older than the max kvno are nuked.
411
*/
412
krb5_error_code
413
krb5_dbe_ark(krb5_context context, krb5_keyblock *mkey,
414
krb5_key_salt_tuple *ks_tuple, int ks_tuple_count,
415
krb5_db_entry *dbent)
416
{
417
return rekey(context, mkey, ks_tuple, ks_tuple_count, NULL, 0, 2, dbent);
418
}
419
420
/*
421
* Change password for a krb5_db_entry
422
* Assumes the max kvno
423
*
424
* As a side effect all old keys are nuked if keepold is false.
425
*/
426
krb5_error_code
427
krb5_dbe_def_cpw(krb5_context context, krb5_keyblock *mkey,
428
krb5_key_salt_tuple *ks_tuple, int ks_tuple_count,
429
char *password, int new_kvno, unsigned int keepold,
430
krb5_db_entry *dbent)
431
{
432
return rekey(context, mkey, ks_tuple, ks_tuple_count, password, new_kvno,
433
keepold, dbent);
434
}
435
436
/*
437
* Add password for a krb5_db_entry
438
* Assumes the max kvno
439
*
440
* As a side effect all old keys older than the max kvno are nuked.
441
*/
442
krb5_error_code
443
krb5_dbe_apw(krb5_context context, krb5_keyblock *mkey,
444
krb5_key_salt_tuple *ks_tuple, int ks_tuple_count, char *password,
445
krb5_db_entry *dbent)
446
{
447
return rekey(context, mkey, ks_tuple, ks_tuple_count, password, 0, 2,
448
dbent);
449
}
450
451