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