This commit was manufactured by cvs2svn to create tag
[krb5.git] / src / lib / krb5 / ccache / ccapi / stdcc_util.c
1 /*
2  * stdcc_util.c
3  * utility functions used in implementing the ccache api for krb5
4  * not publicly exported
5  * Frank Dabek, July 1998
6  */
7
8 #include <stdlib.h>
9 #include <string.h>
10
11 #if defined(_MSDOS) || defined(_WIN32)
12 #include <malloc.h>
13 #endif
14
15 #include "stdcc_util.h"
16 #include "krb5.h"
17 #include "kv5m_err.h"
18
19 #define fieldSize 255
20
21 /*
22  * CopyCCDataArrayToK5
23  * - copy and translate the null terminated arrays of data records
24  *       used in k5 tickets
25  */
26 int copyCCDataArrayToK5(cc_creds *ccCreds, krb5_creds *v5Creds, char whichArray) {
27
28     if (whichArray == kAddressArray) {
29         if (ccCreds->addresses == NULL) {
30             v5Creds->addresses = NULL;
31         } else {
32
33             krb5_address        **addrPtr, *addr;
34             cc_data                     **dataPtr, *data;
35             unsigned int                numRecords = 0;
36
37             /* Allocate the array of pointers: */
38             for (dataPtr = ccCreds->addresses; *dataPtr != NULL; numRecords++, dataPtr++) {}
39
40             v5Creds->addresses = (krb5_address **) malloc (sizeof(krb5_address *) * (numRecords + 1));
41             if (v5Creds->addresses == NULL)
42                 return ENOMEM;
43
44             /* Fill in the array, allocating the address structures: */
45             for (dataPtr = ccCreds->addresses, addrPtr = v5Creds->addresses; *dataPtr != NULL; addrPtr++, dataPtr++) {
46
47                 *addrPtr = (krb5_address *) malloc (sizeof(krb5_address));
48                 if (*addrPtr == NULL)
49                     return ENOMEM;
50                 data = *dataPtr;
51                 addr = *addrPtr;
52
53                 addr->addrtype = data->type;
54                 addr->magic    = KV5M_ADDRESS;
55                 addr->length   = data->length;
56                 addr->contents = (krb5_octet *) malloc (sizeof(krb5_octet) * addr->length);
57                 if (addr->contents == NULL)
58                     return ENOMEM;
59                 memmove(addr->contents, data->data, addr->length); /* copy contents */
60             }
61
62             /* Write terminator: */
63             *addrPtr = NULL;
64         }
65     }
66
67     if (whichArray == kAuthDataArray) {
68         if (ccCreds->authdata == NULL) {
69             v5Creds->authdata = NULL;
70         } else {
71             krb5_authdata       **authPtr, *auth;
72             cc_data                     **dataPtr, *data;
73             unsigned int                numRecords = 0;
74
75             /* Allocate the array of pointers: */
76             for (dataPtr = ccCreds->authdata; *dataPtr != NULL; numRecords++, dataPtr++) {}
77
78             v5Creds->authdata = (krb5_authdata **) malloc (sizeof(krb5_authdata *) * (numRecords + 1));
79             if (v5Creds->authdata == NULL)
80                 return ENOMEM;
81
82             /* Fill in the array, allocating the address structures: */
83             for (dataPtr = ccCreds->authdata, authPtr = v5Creds->authdata; *dataPtr != NULL; authPtr++, dataPtr++) {
84
85                 *authPtr = (krb5_authdata *) malloc (sizeof(krb5_authdata));
86                 if (*authPtr == NULL)
87                     return ENOMEM;
88                 data = *dataPtr;
89                 auth = *authPtr;
90
91                 auth->ad_type  = data->type;
92                 auth->magic    = KV5M_AUTHDATA;
93                 auth->length   = data->length;
94                 auth->contents = (krb5_octet *) malloc (sizeof(krb5_octet) * auth->length);
95                 if (auth->contents == NULL)
96                     return ENOMEM;
97                 memmove(auth->contents, data->data, auth->length); /* copy contents */
98             }
99
100             /* Write terminator: */
101             *authPtr = NULL;
102         }
103     }
104
105     return 0;
106 }
107
108 /*
109  * copyK5DataArrayToCC
110  * - analagous to above, but in the other direction
111  */
112 int copyK5DataArrayToCC(krb5_creds *v5Creds, cc_creds *ccCreds, char whichArray)
113 {
114     if (whichArray == kAddressArray) {
115         if (v5Creds->addresses == NULL) {
116             ccCreds->addresses = NULL;
117         } else {
118
119             krb5_address        **addrPtr, *addr;
120             cc_data                     **dataPtr, *data;
121             unsigned int                        numRecords = 0;
122
123             /* Allocate the array of pointers: */
124             for (addrPtr = v5Creds->addresses; *addrPtr != NULL; numRecords++, addrPtr++) {}
125
126             ccCreds->addresses = (cc_data **) malloc (sizeof(cc_data *) * (numRecords + 1));
127             if (ccCreds->addresses == NULL)
128                 return ENOMEM;
129
130             /* Fill in the array, allocating the address structures: */
131             for (dataPtr = ccCreds->addresses, addrPtr = v5Creds->addresses; *addrPtr != NULL; addrPtr++, dataPtr++) {
132
133                 *dataPtr = (cc_data *) malloc (sizeof(cc_data));
134                 if (*dataPtr == NULL)
135                     return ENOMEM;
136                 data = *dataPtr;
137                 addr = *addrPtr;
138
139                 data->type   = addr->addrtype;
140                 data->length = addr->length;
141                 data->data   = malloc (sizeof(char) * data->length);
142                 if (data->data == NULL)
143                     return ENOMEM;
144                 memmove(data->data, addr->contents, data->length); /* copy contents */
145             }
146
147             /* Write terminator: */
148             *dataPtr = NULL;
149         }
150     }
151
152     if (whichArray == kAuthDataArray) {
153         if (v5Creds->authdata == NULL) {
154             ccCreds->authdata = NULL;
155         } else {
156             krb5_authdata       **authPtr, *auth;
157             cc_data                     **dataPtr, *data;
158             unsigned int                        numRecords = 0;
159
160             /* Allocate the array of pointers: */
161             for (authPtr = v5Creds->authdata; *authPtr != NULL; numRecords++, authPtr++) {}
162
163             ccCreds->authdata = (cc_data **) malloc (sizeof(cc_data *) * (numRecords + 1));
164             if (ccCreds->authdata == NULL)
165                 return ENOMEM;
166
167             /* Fill in the array, allocating the address structures: */
168             for (dataPtr = ccCreds->authdata, authPtr = v5Creds->authdata; *authPtr != NULL; authPtr++, dataPtr++) {
169
170                 *dataPtr = (cc_data *) malloc (sizeof(cc_data));
171                 if (*dataPtr == NULL)
172                     return ENOMEM;
173                 data = *dataPtr;
174                 auth = *authPtr;
175
176                 data->type   = auth->ad_type;
177                 data->length = auth->length;
178                 data->data   = malloc (sizeof(char) * data->length);
179                 if (data->data == NULL)
180                     return ENOMEM;
181                 memmove(data->data, auth->contents, data->length); /* copy contents */
182             }
183
184             /* Write terminator: */
185             *dataPtr = NULL;
186         }
187     }
188
189     return 0;
190 }
191
192 /*
193  * dupcctok5
194  * - allocate an empty k5 style ticket and copy info from the cc_creds ticket
195  */
196
197 void dupCCtoK5(krb5_context context, cc_creds *src, krb5_creds *dest)
198 {
199     krb5_int32 offset_seconds = 0, offset_microseconds = 0;
200     int err;
201
202     /*
203      * allocate and copy
204      * copy all of those damn fields back
205      */
206     err = krb5_parse_name(context, src->client, &(dest->client));
207     err = krb5_parse_name(context, src->server, &(dest->server));
208     if (err) return; /* parsename fails w/o krb5.ini for example */
209
210     /* copy keyblock */
211     dest->keyblock.enctype = src->keyblock.type;
212     dest->keyblock.length = src->keyblock.length;
213     dest->keyblock.contents = (krb5_octet *)malloc(dest->keyblock.length);
214     memcpy(dest->keyblock.contents, src->keyblock.data, dest->keyblock.length);
215
216     /* copy times */
217 #ifdef macintosh
218     err = krb5_get_time_offsets(context, &offset_seconds, &offset_microseconds);
219     if (err) return;
220 #endif
221     dest->times.authtime   = src->authtime     + offset_seconds;
222     dest->times.starttime  = src->starttime    + offset_seconds;
223     dest->times.endtime    = src->endtime      + offset_seconds;
224     dest->times.renew_till = src->renew_till   + offset_seconds;
225     dest->is_skey          = src->is_skey;
226     dest->ticket_flags     = src->ticket_flags;
227
228     /* more branching fields */
229     err = copyCCDataArrayToK5(src, dest, kAddressArray);
230     if (err) return;
231
232     dest->ticket.length = src->ticket.length;
233     dest->ticket.data = (char *)malloc(src->ticket.length);
234     memcpy(dest->ticket.data, src->ticket.data, src->ticket.length);
235     dest->second_ticket.length = src->second_ticket.length;
236     (dest->second_ticket).data = ( char *)malloc(src->second_ticket.length);
237     memcpy(dest->second_ticket.data, src->second_ticket.data, src->second_ticket.length);
238
239     /* zero out magic number */
240     dest->magic = 0;
241
242     /* authdata */
243     err = copyCCDataArrayToK5(src, dest, kAuthDataArray);
244     if (err) return;
245
246     return;
247 }
248
249 /*
250  * dupK5toCC
251  * - analagous to above but in the reverse direction
252  */
253 void dupK5toCC(krb5_context context, krb5_creds *creds, cred_union **cu)
254 {
255     cc_creds *c;
256     int err;
257     krb5_int32 offset_seconds = 0, offset_microseconds = 0;
258 #ifdef macintosh
259     char *tempname = NULL;
260 #endif
261
262     if (cu == NULL) return;
263
264     /* allocate the cred_union */
265     *cu = (cred_union *)malloc(sizeof(cred_union));
266     if ((*cu) == NULL)
267         return;
268
269     (*cu)->cred_type = CC_CRED_V5;
270
271     /* allocate creds structure (and install) */
272     c  = (cc_creds *)malloc(sizeof(cc_creds));
273     if (c == NULL) return;
274     (*cu)->cred.pV5Cred = c;
275
276     /* convert krb5 principals to flat principals */
277 #ifdef macintosh
278     /*
279      * and make sure the memory for c->client and c->server is on
280      * the system heap with NewPtr for the Mac (krb5_unparse_name
281      * puts it in appl heap with malloc)
282      */
283     err = krb5_unparse_name(context, creds->client, &tempname);
284     c->client = malloc(strlen(tempname)+1);
285     if (c->client != NULL)
286         strcpy(c->client,tempname);
287     free(tempname);
288     tempname = NULL;
289
290     err = krb5_unparse_name(context, creds->server, &tempname);
291     c->server = malloc(strlen(tempname)+1);
292     if (c->server != NULL)
293         strcpy(c->server,tempname);
294     free(tempname);
295 #else
296     err = krb5_unparse_name(context, creds->client, &(c->client));
297     err = krb5_unparse_name(context, creds->server, &(c->server));
298 #endif
299     if (err) return;
300
301     /* copy more fields */
302     c->keyblock.type = creds->keyblock.enctype;
303     c->keyblock.length = creds->keyblock.length;
304
305     if (creds->keyblock.contents != NULL) {
306         c->keyblock.data = (unsigned char *)malloc(creds->keyblock.length);
307         memcpy(c->keyblock.data, creds->keyblock.contents, creds->keyblock.length);
308     } else {
309         c->keyblock.data = NULL;
310     }
311
312 #ifdef macintosh
313     err = krb5_get_time_offsets(context, &offset_seconds, &offset_microseconds);
314     if (err) return;
315 #endif
316     c->authtime     = creds->times.authtime   - offset_seconds;
317     c->starttime    = creds->times.starttime  - offset_seconds;
318     c->endtime      = creds->times.endtime    - offset_seconds;
319     c->renew_till   = creds->times.renew_till - offset_seconds;
320     c->is_skey      = creds->is_skey;
321     c->ticket_flags = creds->ticket_flags;
322
323     err = copyK5DataArrayToCC(creds, c, kAddressArray);
324     if (err) return;
325
326     c->ticket.length = creds->ticket.length;
327     if (creds->ticket.data != NULL) {
328         c->ticket.data = (unsigned char *)malloc(creds->ticket.length);
329         memcpy(c->ticket.data, creds->ticket.data, creds->ticket.length);
330     } else {
331         c->ticket.data = NULL;
332     }
333
334     c->second_ticket.length = creds->second_ticket.length;
335     if (creds->second_ticket.data != NULL) {
336         c->second_ticket.data = (unsigned char *)malloc(creds->second_ticket.length);
337         memcpy(c->second_ticket.data, creds->second_ticket.data, creds->second_ticket.length);
338     } else {
339         c->second_ticket.data = NULL;
340     }
341
342     err = copyK5DataArrayToCC(creds, c, kAuthDataArray);
343     if (err) return;
344
345     return;
346 }
347
348 /*
349  * Utility functions...
350  */
351 static krb5_boolean
352 times_match(t1, t2)
353     register const krb5_ticket_times *t1;
354 register const krb5_ticket_times *t2;
355 {
356     if (t1->renew_till) {
357         if (t1->renew_till > t2->renew_till)
358             return FALSE;               /* this one expires too late */
359     }
360     if (t1->endtime) {
361         if (t1->endtime > t2->endtime)
362             return FALSE;               /* this one expires too late */
363     }
364     /* only care about expiration on a times_match */
365     return TRUE;
366 }
367
368 static krb5_boolean
369 times_match_exact (t1, t2)
370     register const krb5_ticket_times *t1, *t2;
371 {
372     return (t1->authtime == t2->authtime
373             && t1->starttime == t2->starttime
374             && t1->endtime == t2->endtime
375             && t1->renew_till == t2->renew_till);
376 }
377
378 static krb5_boolean
379 standard_fields_match(context, mcreds, creds)
380     krb5_context context;
381 register const krb5_creds *mcreds, *creds;
382 {
383     return (krb5_principal_compare(context, mcreds->client,creds->client) &&
384             krb5_principal_compare(context, mcreds->server,creds->server));
385 }
386
387 /* only match the server name portion, not the server realm portion */
388
389 static krb5_boolean
390 srvname_match(context, mcreds, creds)
391     krb5_context context;
392 register const krb5_creds *mcreds, *creds;
393 {
394     krb5_boolean retval;
395     krb5_principal_data p1, p2;
396
397     retval = krb5_principal_compare(context, mcreds->client,creds->client);
398     if (retval != TRUE)
399         return retval;
400     /*
401      * Hack to ignore the server realm for the purposes of the compare.
402      */
403     p1 = *mcreds->server;
404     p2 = *creds->server;
405     p1.realm = p2.realm;
406     return krb5_principal_compare(context, &p1, &p2);
407 }
408
409
410 static krb5_boolean
411 authdata_match(mdata, data)
412     krb5_authdata *const *mdata, *const *data;
413 {
414     const krb5_authdata *mdatap, *datap;
415
416     if (mdata == data)
417         return TRUE;
418
419     if (mdata == NULL)
420         return *data == NULL;
421
422     if (data == NULL)
423         return *mdata == NULL;
424
425     while ((mdatap = *mdata)
426            && (datap = *data)
427            && mdatap->ad_type == datap->ad_type
428            && mdatap->length == datap->length
429            && !memcmp ((char *) mdatap->contents, (char *) datap->contents,
430                        datap->length)) {
431         mdata++;
432         data++;
433     }
434
435     return !*mdata && !*data;
436 }
437
438 static krb5_boolean
439 data_match(data1, data2)
440     register const krb5_data *data1, *data2;
441 {
442     if (!data1) {
443         if (!data2)
444             return TRUE;
445         else
446             return FALSE;
447     }
448     if (!data2) return FALSE;
449
450     if (data1->length != data2->length)
451         return FALSE;
452     else
453         return memcmp(data1->data, data2->data, data1->length) ? FALSE : TRUE;
454 }
455
456 #define MATCH_SET(bits) (whichfields & bits)
457 #define flags_match(a,b) (((a) & (b)) == (a))
458
459 /*  stdccCredsMatch
460  *  - check to see if the creds match based on the whichFields variable
461  *  NOTE: if whichfields is zero we are now comparing 'standard fields.'
462  *               This is the bug that was killing fetch for a
463  *               week. The behaviour is what krb5 expects, however.
464  */
465 int stdccCredsMatch(krb5_context context, krb5_creds *base,
466                     krb5_creds *match, int whichfields)
467 {
468     if (((MATCH_SET(KRB5_TC_MATCH_SRV_NAMEONLY) &&
469           srvname_match(context, match, base)) ||
470          standard_fields_match(context, match, base))
471         &&
472         (! MATCH_SET(KRB5_TC_MATCH_IS_SKEY) ||
473          match->is_skey == base->is_skey)
474         &&
475         (! MATCH_SET(KRB5_TC_MATCH_FLAGS_EXACT) ||
476          match->ticket_flags == base->ticket_flags)
477         &&
478         (! MATCH_SET(KRB5_TC_MATCH_FLAGS) ||
479          flags_match(match->ticket_flags, base->ticket_flags))
480         &&
481         (! MATCH_SET(KRB5_TC_MATCH_TIMES_EXACT) ||
482          times_match_exact(&match->times, &base->times))
483         &&
484         (! MATCH_SET(KRB5_TC_MATCH_TIMES) ||
485          times_match(&match->times, &base->times))
486         &&
487         (! MATCH_SET(KRB5_TC_MATCH_AUTHDATA) ||
488          authdata_match (match->authdata, base->authdata))
489         &&
490         (! MATCH_SET(KRB5_TC_MATCH_2ND_TKT) ||
491          data_match (&match->second_ticket, &base->second_ticket))
492         &&
493         ((! MATCH_SET(KRB5_TC_MATCH_KTYPE))||
494          (match->keyblock.enctype == base->keyblock.enctype))
495         )
496         return TRUE;
497     return FALSE;
498 }
499
500 /* ----- free_cc_cred_union, etc -------------- */
501 /*
502   Since the Kerberos5 library allocates a credentials cache structure
503   (in dupK5toCC() above) with its own memory allocation routines - which
504   may be different than how the CCache allocates memory - the Kerb5 library
505   must have its own version of cc_free_creds() to deallocate it.  These
506   functions do that.  The top-level function to substitue for cc_free_creds()
507   is krb5_free_cc_cred_union().
508
509   If the CCache library wants to use a cred_union structure created by
510   the Kerb5 library, it should make a deep copy of it to "translate" to its
511   own memory allocation space.
512 */
513 static void deep_free_cc_data (cc_data data)
514 {
515     if (data.data != NULL)
516         free (data.data);
517 }
518
519 static void deep_free_cc_data_array (cc_data** data) {
520
521     unsigned int        index;
522
523     if (data == NULL)
524         return;
525
526     for (index = 0; data [index] != NULL; index++) {
527         deep_free_cc_data (*(data [index]));
528         free (data [index]);
529     }
530
531     free (data);
532 }
533
534 static void deep_free_cc_v5_creds (cc_creds* creds)
535 {
536     if (creds == NULL)
537         return;
538
539     if (creds -> client != NULL)
540         free (creds -> client);
541     if (creds -> server != NULL)
542         free (creds -> server);
543
544     deep_free_cc_data (creds -> keyblock);
545     deep_free_cc_data (creds -> ticket);
546     deep_free_cc_data (creds -> second_ticket);
547
548     deep_free_cc_data_array (creds -> addresses);
549     deep_free_cc_data_array (creds -> authdata);
550
551     free(creds);
552 }
553
554 static void deep_free_cc_creds (cred_union creds)
555 {
556     if (creds.cred_type == CC_CRED_V4) {
557         /* we shouldn't get this, of course */
558         free (creds.cred.pV4Cred);
559     } else if (creds.cred_type == CC_CRED_V5) {
560         deep_free_cc_v5_creds (creds.cred.pV5Cred);
561     }
562 }
563
564 /* top-level exported function */
565 cc_int32 krb5_free_cc_cred_union (cred_union** creds)
566 {
567     if (creds == NULL)
568         return CC_BAD_PARM;
569
570     if (*creds != NULL) {
571         deep_free_cc_creds (**creds);
572         free (*creds);
573         *creds = NULL;
574     }
575
576     return CC_NOERROR;
577 }