1 #pragma ident	"%Z%%M%	%I%	%E% SMI"
2 
3 /*
4  * lib/kdb/kdb_xdr.c
5  *
6  * Copyright 1995 by the Massachusetts Institute of Technology.
7  * All Rights Reserved.
8  *
9  * Export of this software from the United States of America may
10  *   require a specific license from the United States Government.
11  *   It is the responsibility of any person or organization contemplating
12  *   export to obtain such a license before exporting.
13  *
14  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
15  * distribute this software and its documentation for any purpose and
16  * without fee is hereby granted, provided that the above copyright
17  * notice appear in all copies and that both that copyright notice and
18  * this permission notice appear in supporting documentation, and that
19  * the name of M.I.T. not be used in advertising or publicity pertaining
20  * to distribution of the software without specific, written prior
21  * permission.  Furthermore if you modify this software you must label
22  * your software as modified software and not distribute it in such a
23  * fashion that it might be confused with the original M.I.T. software.
24  * M.I.T. makes no representations about the suitability of
25  * this software for any purpose.  It is provided "as is" without express
26  * or implied warranty.
27  *
28  */
29 
30 #include "k5-int.h"
31 #include <string.h>
32 #include <stdio.h>
33 #include <errno.h>
34 #include "kdb_xdr.h"
35 
36 krb5_error_code
krb5_encode_princ_dbkey(context,key,principal)37 krb5_encode_princ_dbkey(context, key, principal)
38     krb5_context context;
39     krb5_data  *key;
40     krb5_const_principal principal;
41 {
42     char *princ_name;
43     krb5_error_code retval;
44 
45     if (!(retval = krb5_unparse_name(context, principal, &princ_name))) {
46         /* need to store the NULL for decoding */
47         key->length = strlen(princ_name)+1;
48         key->data = princ_name;
49     }
50     return(retval);
51 }
52 
53 void
krb5_free_princ_dbkey(context,key)54 krb5_free_princ_dbkey(context, key)
55     krb5_context context;
56     krb5_data  *key;
57 {
58     (void) krb5_free_data_contents(context, key);
59 }
60 
61 krb5_error_code
krb5_encode_princ_contents(context,content,entry)62 krb5_encode_princ_contents(context, content, entry)
63     krb5_context 	  context;
64     krb5_data  		* content;
65     krb5_db_entry 	* entry;
66 {
67     int 		  i, j;
68     unsigned int	  unparse_princ_size;
69     char 		* unparse_princ;
70     char		* nextloc;
71     krb5_tl_data	* tl_data;
72     krb5_error_code 	  retval;
73     krb5_int16		  psize16;
74 
75     /*
76      * Generate one lump of data from the krb5_db_entry.
77      * This data must be independent of byte order of the machine,
78      * compact and extensible.
79      */
80 
81     /*
82      * First allocate enough space for all the data.
83      * Need  2 bytes for the length of the base structure
84      * then 36 [ 8 * 4 + 2 * 2] bytes for the base information
85      *         [ attributes, max_life, max_renewable_life, expiration,
86      *	  	 pw_expiration, last_success, last_failed, fail_auth_count ]
87      *         [ n_key_data, n_tl_data ]
88      * then XX bytes [ e_length ] for the extra data [ e_data ]
89      * then XX bytes [ 2 for length + length for string ] for the principal,
90      * then (4 [type + length] + tl_data_length) bytes per tl_data
91      * then (4 + (4 + key_data_length) per key_data_contents) bytes per key_data
92      */
93     content->length = entry->len + entry->e_length;
94 
95     if ((retval = krb5_unparse_name(context, entry->princ, &unparse_princ)))
96 	return(retval);
97 
98     unparse_princ_size = strlen(unparse_princ) + 1;
99     content->length += unparse_princ_size;
100     content->length += 2;
101 
102     i = 0;
103     /* tl_data is a linked list */
104     for (tl_data = entry->tl_data; tl_data; tl_data = tl_data->tl_data_next) {
105 	content->length += tl_data->tl_data_length;
106 	content->length += 4; /* type, length */
107 	i++;
108     }
109 
110     if (i != entry->n_tl_data) {
111 	retval = KRB5_KDB_TRUNCATED_RECORD;
112 	goto epc_error;
113     }
114 
115     /* key_data is an array */
116     for (i = 0; i < entry->n_key_data; i++) {
117 	content->length += 4; /* Version, KVNO */
118 	for (j = 0; j < entry->key_data[i].key_data_ver; j++) {
119 	    content->length += entry->key_data[i].key_data_length[j];
120 	    content->length += 4; /* type + length */
121 	}
122     }
123 
124     if ((content->data = malloc(content->length)) == NULL) {
125 	retval = ENOMEM;
126 	goto epc_error;
127     }
128 
129     /*
130      * Now we go through entry again, this time copying data
131      * These first entries are always saved regardless of version
132      */
133     nextloc = content->data;
134 
135 	/* Base Length */
136     krb5_kdb_encode_int16(entry->len, nextloc);
137     nextloc += 2;
138 
139 	/* Attributes */
140     krb5_kdb_encode_int32(entry->attributes, nextloc);
141     nextloc += 4;
142 
143 	/* Max Life */
144     krb5_kdb_encode_int32(entry->max_life, nextloc);
145     nextloc += 4;
146 
147 	/* Max Renewable Life */
148     krb5_kdb_encode_int32(entry->max_renewable_life, nextloc);
149     nextloc += 4;
150 
151 	/* When the client expires */
152     krb5_kdb_encode_int32(entry->expiration, nextloc);
153     nextloc += 4;
154 
155 	/* When its passwd expires */
156     krb5_kdb_encode_int32(entry->pw_expiration, nextloc);
157     nextloc += 4;
158 
159 	/* Last successful passwd */
160     krb5_kdb_encode_int32(entry->last_success, nextloc);
161     nextloc += 4;
162 
163 	/* Last failed passwd attempt */
164     krb5_kdb_encode_int32(entry->last_failed, nextloc);
165     nextloc += 4;
166 
167 	/* # of failed passwd attempt */
168     krb5_kdb_encode_int32(entry->fail_auth_count, nextloc);
169     nextloc += 4;
170 
171 	/* # tl_data strutures */
172     krb5_kdb_encode_int16(entry->n_tl_data, nextloc);
173     nextloc += 2;
174 
175 	/* # key_data strutures */
176     krb5_kdb_encode_int16(entry->n_key_data, nextloc);
177     nextloc += 2;
178 
179     	/* Put extended fields here */
180     if (entry->len != KRB5_KDB_V1_BASE_LENGTH)
181 	abort();
182 
183 	/* Any extra data that this version doesn't understand. */
184     if (entry->e_length) {
185 	memcpy(nextloc, entry->e_data, entry->e_length);
186 	nextloc += entry->e_length;
187     }
188 
189 	/*
190 	 * Now we get to the principal.
191 	 * To squeze a few extra bytes out it is always assumed to come
192 	 * after the base type.
193 	 */
194     psize16 = (krb5_int16) unparse_princ_size;
195     krb5_kdb_encode_int16(psize16, nextloc);
196     nextloc += 2;
197     (void) memcpy(nextloc, unparse_princ, unparse_princ_size);
198     nextloc += unparse_princ_size;
199 
200     	/* tl_data is a linked list, of type, legth, contents */
201     for (tl_data = entry->tl_data; tl_data; tl_data = tl_data->tl_data_next) {
202 	krb5_kdb_encode_int16(tl_data->tl_data_type, nextloc);
203 	nextloc += 2;
204 	krb5_kdb_encode_int16(tl_data->tl_data_length, nextloc);
205 	nextloc += 2;
206 
207 	memcpy(nextloc, tl_data->tl_data_contents, tl_data->tl_data_length);
208 	nextloc += tl_data->tl_data_length;
209     }
210 
211     	/* key_data is an array */
212     for (i = 0; i < entry->n_key_data; i++) {
213 	krb5_kdb_encode_int16(entry->key_data[i].key_data_ver, nextloc);
214 	nextloc += 2;
215 	krb5_kdb_encode_int16(entry->key_data[i].key_data_kvno, nextloc);
216 	nextloc += 2;
217 
218 	for (j = 0; j < entry->key_data[i].key_data_ver; j++) {
219 	    krb5_int16 type = entry->key_data[i].key_data_type[j];
220 	    krb5_ui_2  length = entry->key_data[i].key_data_length[j];
221 
222     	    krb5_kdb_encode_int16(type, nextloc);
223 	    nextloc += 2;
224     	    krb5_kdb_encode_int16(length, nextloc);
225 	    nextloc += 2;
226 
227 	    if (length) {
228 	        memcpy(nextloc, entry->key_data[i].key_data_contents[j],length);
229 	        nextloc += length;
230 	    }
231 	}
232     }
233 
234 epc_error:;
235     free(unparse_princ);
236     return retval;
237 }
238 
239 void
krb5_free_princ_contents(context,contents)240 krb5_free_princ_contents(context, contents)
241     krb5_context 	  context;
242     krb5_data *contents;
243 {
244     krb5_free_data_contents(context, contents);
245     return;
246 }
247 
248 krb5_error_code
krb5_decode_princ_contents(context,content,entry)249 krb5_decode_princ_contents(context, content, entry)
250     krb5_context 	  context;
251     krb5_data  		* content;
252     krb5_db_entry 	* entry;
253 {
254     int			  sizeleft, i;
255     char 		* nextloc;
256     krb5_tl_data       ** tl_data;
257     krb5_int16		  i16;
258 
259     krb5_error_code retval;
260 
261     /* Zero out entry and NULL pointers */
262     memset(entry, 0, sizeof(krb5_db_entry));
263 
264     /*
265      * undo the effects of encode_princ_contents.
266      *
267      * The first part is decoding the base type. If the base type is
268      * bigger than the original base type then the additional fields
269      * need to be filled in. If the base type is larger than any
270      * known base type the additional data goes in e_data.
271      */
272 
273     /* First do the easy stuff */
274     nextloc = content->data;
275     sizeleft = content->length;
276     if ((sizeleft -= KRB5_KDB_V1_BASE_LENGTH) < 0)
277 	return KRB5_KDB_TRUNCATED_RECORD;
278 
279 	/* Base Length */
280     krb5_kdb_decode_int16(nextloc, entry->len);
281     nextloc += 2;
282 
283 	/* Attributes */
284     krb5_kdb_decode_int32(nextloc, entry->attributes);
285     nextloc += 4;
286 
287 	/* Max Life */
288     krb5_kdb_decode_int32(nextloc, entry->max_life);
289     nextloc += 4;
290 
291 	/* Max Renewable Life */
292     krb5_kdb_decode_int32(nextloc, entry->max_renewable_life);
293     nextloc += 4;
294 
295 	/* When the client expires */
296     krb5_kdb_decode_int32(nextloc, entry->expiration);
297     nextloc += 4;
298 
299 	/* When its passwd expires */
300     krb5_kdb_decode_int32(nextloc, entry->pw_expiration);
301     nextloc += 4;
302 
303 	/* Last successful passwd */
304     krb5_kdb_decode_int32(nextloc, entry->last_success);
305     nextloc += 4;
306 
307 	/* Last failed passwd attempt */
308     krb5_kdb_decode_int32(nextloc, entry->last_failed);
309     nextloc += 4;
310 
311 	/* # of failed passwd attempt */
312     krb5_kdb_decode_int32(nextloc, entry->fail_auth_count);
313     nextloc += 4;
314 
315 	/* # tl_data strutures */
316     krb5_kdb_decode_int16(nextloc, entry->n_tl_data);
317     nextloc += 2;
318 
319     if (entry->n_tl_data < 0)
320 	return KRB5_KDB_TRUNCATED_RECORD;
321 
322 	/* # key_data strutures */
323     krb5_kdb_decode_int16(nextloc, entry->n_key_data);
324     nextloc += 2;
325 
326     if (entry->n_key_data < 0)
327 	return KRB5_KDB_TRUNCATED_RECORD;
328 
329 	/* Check for extra data */
330     if (entry->len > KRB5_KDB_V1_BASE_LENGTH) {
331 	entry->e_length = entry->len - KRB5_KDB_V1_BASE_LENGTH;
332 	if ((entry->e_data = (krb5_octet *)malloc(entry->e_length))) {
333 	    memcpy(entry->e_data, nextloc, entry->e_length);
334 	    nextloc += entry->e_length;
335 	} else {
336 	    return ENOMEM;
337 	}
338     }
339 
340     /*
341      * Get the principal name for the entry
342      * (stored as a string which gets unparsed.)
343      */
344     if ((sizeleft -= 2) < 0) {
345 	retval = KRB5_KDB_TRUNCATED_RECORD;
346 	goto error_out;
347     }
348 
349     i = 0;
350     krb5_kdb_decode_int16(nextloc, i16);
351     i = (int) i16;
352     nextloc += 2;
353 
354     if ((retval = krb5_parse_name(context, nextloc, &(entry->princ))))
355 	goto error_out;
356     if (((size_t) i != (strlen(nextloc) + 1)) || (sizeleft < i)) {
357 	retval = KRB5_KDB_TRUNCATED_RECORD;
358 	goto error_out;
359     }
360     sizeleft -= i;
361     nextloc += i;
362 
363     	/* tl_data is a linked list */
364     tl_data = &entry->tl_data;
365     for (i = 0; i < entry->n_tl_data; i++) {
366     	if ((sizeleft -= 4) < 0) {
367 	    retval = KRB5_KDB_TRUNCATED_RECORD;
368 	    goto error_out;
369 	}
370 	if ((*tl_data = (krb5_tl_data *)
371 	  malloc(sizeof(krb5_tl_data))) == NULL) {
372 	    retval = ENOMEM;
373 	    goto error_out;
374 	}
375 	(*tl_data)->tl_data_next = NULL;
376 	(*tl_data)->tl_data_contents = NULL;
377 	krb5_kdb_decode_int16(nextloc, (*tl_data)->tl_data_type);
378 	nextloc += 2;
379 	krb5_kdb_decode_int16(nextloc, (*tl_data)->tl_data_length);
380 	nextloc += 2;
381 
382     	if ((sizeleft -= (*tl_data)->tl_data_length) < 0) {
383 	    retval = KRB5_KDB_TRUNCATED_RECORD;
384 	    goto error_out;
385 	}
386 	if (((*tl_data)->tl_data_contents = (krb5_octet *)
387 	  malloc((*tl_data)->tl_data_length)) == NULL) {
388 	    retval = ENOMEM;
389 	    goto error_out;
390 	}
391 	memcpy((*tl_data)->tl_data_contents,nextloc,(*tl_data)->tl_data_length);
392 	nextloc += (*tl_data)->tl_data_length;
393 	tl_data = &((*tl_data)->tl_data_next);
394     }
395 
396     	/* key_data is an array */
397     if (entry->n_key_data && ((entry->key_data = (krb5_key_data *)
398       malloc(sizeof(krb5_key_data) * entry->n_key_data)) == NULL)) {
399         retval = ENOMEM;
400 	goto error_out;
401     }
402     for (i = 0; i < entry->n_key_data; i++) {
403 	krb5_key_data * key_data;
404         int j;
405 
406     	if ((sizeleft -= 4) < 0) {
407 	    retval = KRB5_KDB_TRUNCATED_RECORD;
408 	    goto error_out;
409 	}
410 	key_data = entry->key_data + i;
411 	memset(key_data, 0, sizeof(krb5_key_data));
412 	krb5_kdb_decode_int16(nextloc, key_data->key_data_ver);
413 	nextloc += 2;
414 	krb5_kdb_decode_int16(nextloc, key_data->key_data_kvno);
415 	nextloc += 2;
416 
417 	/* key_data_ver determins number of elements and how to unparse them. */
418 	if (key_data->key_data_ver <= KRB5_KDB_V1_KEY_DATA_ARRAY) {
419 	    for (j = 0; j < key_data->key_data_ver; j++) {
420     	        if ((sizeleft -= 4) < 0) {
421 	            retval = KRB5_KDB_TRUNCATED_RECORD;
422 	            goto error_out;
423 	        }
424 		krb5_kdb_decode_int16(nextloc, key_data->key_data_type[j]);
425 		nextloc += 2;
426 		krb5_kdb_decode_int16(nextloc, key_data->key_data_length[j]);
427 		nextloc += 2;
428 
429     	        if ((sizeleft -= key_data->key_data_length[j]) < 0) {
430 	            retval = KRB5_KDB_TRUNCATED_RECORD;
431 	            goto error_out;
432 	        }
433 	        if (key_data->key_data_length[j]) {
434 	    	    if ((key_data->key_data_contents[j] = (krb5_octet *)
435 	    	      malloc(key_data->key_data_length[j])) == NULL) {
436 	                retval = ENOMEM;
437 	                goto error_out;
438 	            }
439 	            memcpy(key_data->key_data_contents[j], nextloc,
440 		           key_data->key_data_length[j]);
441 	            nextloc += key_data->key_data_length[j];
442 		}
443 	    }
444 	} else {
445 	    /* This isn't right. I'll fix it later */
446 	    abort();
447 	}
448     }
449     return 0;
450 
451 error_out:;
452     krb5_dbe_free_contents(context, entry);
453     return retval;
454 }
455 
456 void
krb5_dbe_free_contents(context,entry)457 krb5_dbe_free_contents(context, entry)
458      krb5_context 	  context;
459      krb5_db_entry 	* entry;
460 {
461     krb5_tl_data 	* tl_data_next;
462     krb5_tl_data 	* tl_data;
463     int i, j;
464 
465     if (entry->e_data)
466 	free(entry->e_data);
467     if (entry->princ)
468 	krb5_free_principal(context, entry->princ);
469     for (tl_data = entry->tl_data; tl_data; tl_data = tl_data_next) {
470 	tl_data_next = tl_data->tl_data_next;
471 	if (tl_data->tl_data_contents)
472 	    free(tl_data->tl_data_contents);
473 	free(tl_data);
474     }
475     if (entry->key_data) {
476     	for (i = 0; i < entry->n_key_data; i++) {
477 	    for (j = 0; j < entry->key_data[i].key_data_ver; j++) {
478 	    	if (entry->key_data[i].key_data_length[j]) {
479 		    if (entry->key_data[i].key_data_contents[j]) {
480 		        memset(entry->key_data[i].key_data_contents[j],
481 			       0,
482 			       (unsigned) entry->key_data[i].key_data_length[j]);
483 		    	free (entry->key_data[i].key_data_contents[j]);
484 		    }
485 		}
486 		entry->key_data[i].key_data_contents[j] = NULL;
487 		entry->key_data[i].key_data_length[j] = 0;
488 		entry->key_data[i].key_data_type[j] = 0;
489 	    }
490 	}
491 	free(entry->key_data);
492     }
493     memset(entry, 0, sizeof(*entry));
494     return;
495 }
496