1/*
2 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
3 * Use is subject to license terms.
4 */
5
6#pragma ident	"%Z%%M%	%I%	%E% SMI"
7
8/*
9 * lib/kdb/kdb_helper.c
10 *
11 * Copyright 1995 by the Massachusetts Institute of Technology.
12 * All Rights Reserved.
13 *
14 * Export of this software from the United States of America may
15 *   require a specific license from the United States Government.
16 *   It is the responsibility of any person or organization contemplating
17 *   export to obtain such a license before exporting.
18 *
19 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
20 * distribute this software and its documentation for any purpose and
21 * without fee is hereby granted, provided that the above copyright
22 * notice appear in all copies and that both that copyright notice and
23 * this permission notice appear in supporting documentation, and that
24 * the name of M.I.T. not be used in advertising or publicity pertaining
25 * to distribution of the software without specific, written prior
26 * permission.  Furthermore if you modify this software you must label
27 * your software as modified software and not distribute it in such a
28 * fashion that it might be confused with the original M.I.T. software.
29 * M.I.T. makes no representations about the suitability of
30 * this software for any purpose.  It is provided "as is" without express
31 * or implied warranty.
32 *
33 */
34
35#include "k5-int.h"
36#include "kdb.h"
37#include <string.h>
38#include <stdio.h>
39#include <errno.h>
40#include <libintl.h>
41
42
43/*
44 * Given a particular enctype and optional salttype and kvno, find the
45 * most appropriate krb5_key_data entry of the database entry.
46 *
47 * If stype or kvno is negative, it is ignored.
48 * If kvno is 0 get the key which is maxkvno for the princ and matches
49 * the other attributes.
50 */
51krb5_error_code
52krb5_dbe_def_search_enctype(kcontext, dbentp, start, ktype, stype, kvno, kdatap)
53    krb5_context	kcontext;
54    krb5_db_entry	*dbentp;
55    krb5_int32		*start;
56    krb5_int32		ktype;
57    krb5_int32		stype;
58    krb5_int32		kvno;
59    krb5_key_data	**kdatap;
60{
61    int			i, idx;
62    int			maxkvno;
63    krb5_key_data	*datap;
64    krb5_error_code	ret;
65
66    ret = 0;
67    if (kvno == -1 && stype == -1 && ktype == -1)
68	kvno = 0;
69
70    if (kvno == 0) {
71	/* Get the max key version */
72	for (i = 0; i < dbentp->n_key_data; i++) {
73	    if (kvno < dbentp->key_data[i].key_data_kvno) {
74		kvno = dbentp->key_data[i].key_data_kvno;
75	    }
76	}
77    }
78
79    maxkvno = -1;
80    datap = (krb5_key_data *) NULL;
81    for (i = *start; i < dbentp->n_key_data; i++) {
82        krb5_boolean    similar;
83        krb5_int32      db_stype;
84
85	ret = 0;
86	if (dbentp->key_data[i].key_data_ver > 1) {
87	    db_stype = dbentp->key_data[i].key_data_type[1];
88	} else {
89	    db_stype = KRB5_KDB_SALTTYPE_NORMAL;
90	}
91
92	/*
93	 * Filter out non-permitted enctypes.
94	 */
95	if (!krb5_is_permitted_enctype(kcontext,
96				       dbentp->key_data[i].key_data_type[0])) {
97	    ret = KRB5_KDB_NO_PERMITTED_KEY;
98	    continue;
99	}
100
101
102	if (ktype > 0) {
103	    if ((ret = krb5_c_enctype_compare(kcontext, (krb5_enctype) ktype,
104					      dbentp->key_data[i].key_data_type[0],
105					      &similar)))
106
107		return(ret);
108	}
109
110	if (((ktype <= 0) || similar) &&
111	    ((db_stype == stype) || (stype < 0))) {
112	    if (kvno >= 0) {
113		if (kvno == dbentp->key_data[i].key_data_kvno) {
114		    datap = &dbentp->key_data[i];
115		    idx = i;
116		    maxkvno = kvno;
117		    break;
118		}
119	    } else {
120		if (dbentp->key_data[i].key_data_kvno > maxkvno) {
121		    maxkvno = dbentp->key_data[i].key_data_kvno;
122		    datap = &dbentp->key_data[i];
123		    idx = i;
124		}
125	    }
126	}
127    }
128    if (maxkvno < 0)
129	return ret ? ret : KRB5_KDB_NO_MATCHING_KEY;
130    *kdatap = datap;
131    *start = idx+1;
132    return 0;
133}
134
135/*
136 *  kdb default functions. Ideally, some other file should have this functions. For now, TBD.
137 */
138#ifndef min
139#define min(a,b) (((a) < (b)) ? (a) : (b))
140#endif
141
142krb5_error_code
143krb5_def_store_mkey(context, keyfile, mname, key, master_pwd)
144    krb5_context context;
145    char *keyfile;
146    krb5_principal mname;
147    krb5_keyblock *key;
148    char *master_pwd;
149{
150    FILE *kf;
151    krb5_error_code retval = 0;
152    krb5_ui_2 enctype;
153    char defkeyfile[MAXPATHLEN+1];
154    krb5_data *realm = krb5_princ_realm(context, mname);
155#if HAVE_UMASK
156    mode_t oumask;
157#endif
158
159    if (!keyfile) {
160	(void) strcpy(defkeyfile, DEFAULT_KEYFILE_STUB);
161	(void) strncat(defkeyfile, realm->data,
162		       min(sizeof(defkeyfile)-sizeof(DEFAULT_KEYFILE_STUB)-1,
163			   realm->length));
164	defkeyfile[sizeof(defkeyfile) - 1] = '\0';
165	keyfile = defkeyfile;
166    }
167
168#if HAVE_UMASK
169    oumask = umask(077);
170#endif
171#ifdef ANSI_STDIO
172    /* Solaris Kerberos: using F to deal with 256 open file limit */
173    if (!(kf = fopen(keyfile, "wbF")))
174#else
175    if (!(kf = fopen(keyfile, "wF")))
176#endif
177    {
178	int e = errno;
179#if HAVE_UMASK
180	(void) umask(oumask);
181#endif
182	krb5_set_error_message (context, e,
183				gettext("%s accessing file '%s'"),
184				error_message (e), keyfile);
185	return e;
186    }
187    enctype = key->enctype;
188    if ((fwrite((krb5_pointer) &enctype,
189		2, 1, kf) != 1) ||
190	(fwrite((krb5_pointer) &key->length,
191		sizeof(key->length), 1, kf) != 1) ||
192	(fwrite((krb5_pointer) key->contents,
193		sizeof(key->contents[0]), (unsigned) key->length,
194		kf) != key->length)) {
195	retval = errno;
196	(void) fclose(kf);
197    }
198    if (fclose(kf) == EOF)
199	retval = errno;
200#if HAVE_UMASK
201    (void) umask(oumask);
202#endif
203    return retval;
204}
205
206
207krb5_error_code
208krb5_db_def_fetch_mkey( krb5_context   context,
209			krb5_principal mname,
210			krb5_keyblock *key,
211			int           *kvno,
212			char          *db_args)
213{
214    krb5_error_code retval;
215    krb5_ui_2 enctype;
216    char defkeyfile[MAXPATHLEN+1];
217    krb5_data *realm = krb5_princ_realm(context, mname);
218    FILE *kf = NULL;
219
220    retval = 0;
221    key->magic = KV5M_KEYBLOCK;
222    (void) strcpy(defkeyfile, DEFAULT_KEYFILE_STUB);
223    (void) strncat(defkeyfile, realm->data,
224		   min(sizeof(defkeyfile)-sizeof(DEFAULT_KEYFILE_STUB)-1,
225		       realm->length));
226    defkeyfile[sizeof(defkeyfile) - 1] = '\0';
227
228#ifdef ANSI_STDIO
229    /* Solaris Kerberos: using F to deal with 256 open file limit */
230    if (!(kf = fopen((db_args) ? db_args : defkeyfile, "rbF")))
231#else
232    if (!(kf = fopen((db_args) ? db_args : defkeyfile, "rF")))
233#endif
234	return KRB5_KDB_CANTREAD_STORED;
235
236    if (fread((krb5_pointer) &enctype, 2, 1, kf) != 1) {
237	retval = KRB5_KDB_CANTREAD_STORED;
238	goto errout;
239    }
240
241    if (key->enctype == ENCTYPE_UNKNOWN)
242	key->enctype = enctype;
243    else if (enctype != key->enctype) {
244	retval = KRB5_KDB_BADSTORED_MKEY;
245	goto errout;
246    }
247
248    if (fread((krb5_pointer) &key->length,
249	      sizeof(key->length), 1, kf) != 1) {
250	retval = KRB5_KDB_CANTREAD_STORED;
251	goto errout;
252    }
253
254    if (!key->length || ((int) key->length) < 0) {
255	retval = KRB5_KDB_BADSTORED_MKEY;
256	goto errout;
257    }
258
259    if (!(key->contents = (krb5_octet *)malloc(key->length))) {
260	retval = ENOMEM;
261	goto errout;
262    }
263
264    if (fread((krb5_pointer) key->contents,
265	      sizeof(key->contents[0]), key->length, kf)
266	!= key->length) {
267	retval = KRB5_KDB_CANTREAD_STORED;
268	memset(key->contents, 0,  key->length);
269	free(key->contents);
270	key->contents = 0;
271    } else
272	retval = 0;
273
274    *kvno = 0;
275
276 errout:
277    (void) fclose(kf);
278    return retval;
279
280}
281
282
283krb5_error_code
284krb5_def_verify_master_key(context, mprinc, mkey)
285    krb5_context context;
286    krb5_principal mprinc;
287    krb5_keyblock *mkey;
288{
289    krb5_error_code retval;
290    krb5_db_entry master_entry;
291    int nprinc;
292    krb5_boolean more;
293    krb5_keyblock tempkey;
294
295    nprinc = 1;
296    if ((retval = krb5_db_get_principal(context, mprinc,
297					&master_entry, &nprinc, &more)))
298	return(retval);
299
300    if (nprinc != 1) {
301	if (nprinc)
302	    krb5_db_free_principal(context, &master_entry, nprinc);
303	return(KRB5_KDB_NOMASTERKEY);
304    } else if (more) {
305	krb5_db_free_principal(context, &master_entry, nprinc);
306	return(KRB5KDC_ERR_PRINCIPAL_NOT_UNIQUE);
307    }
308
309    if ((retval = krb5_dbekd_decrypt_key_data(context, mkey,
310					      &master_entry.key_data[0],
311					      &tempkey, NULL))) {
312	krb5_db_free_principal(context, &master_entry, nprinc);
313	return retval;
314    }
315
316    if (mkey->length != tempkey.length ||
317	memcmp((char *)mkey->contents,
318	       (char *)tempkey.contents,mkey->length)) {
319	retval = KRB5_KDB_BADMASTERKEY;
320    }
321
322    memset((char *)tempkey.contents, 0, tempkey.length);
323    krb5_xfree(tempkey.contents);
324    krb5_db_free_principal(context, &master_entry, nprinc);
325
326    return retval;
327}
328
329
330krb5_error_code kdb_def_set_mkey ( krb5_context kcontext,
331				   char *pwd,
332				   krb5_keyblock *key )
333{
334    /* printf("default set master key\n"); */
335    return 0;
336}
337
338krb5_error_code kdb_def_get_mkey ( krb5_context kcontext,
339				   krb5_keyblock **key )
340{
341    /* printf("default get master key\n"); */
342    return 0;
343}
344
345krb5_error_code krb5_def_promote_db (krb5_context kcontext,
346				     char *s, char **args)
347{
348    /* printf("default promote_db\n"); */
349    return KRB5_PLUGIN_OP_NOTSUPP;
350}
351