]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - zephyr/lib/ZMkAuth.c
krb5-interrealm patches
[1ts-debian.git] / zephyr / lib / ZMkAuth.c
1 /* This file is part of the Project Athena Zephyr Notification System.
2  * It contains source for the ZMakeAuthentication function.
3  *
4  *      Created by:     Robert French
5  *
6  *      $Id: ZMkAuth.c,v 1.20 2002/11/11 18:28:06 ghudson Exp $
7  *
8  *      Copyright (c) 1987 by the Massachusetts Institute of Technology.
9  *      For copying and distribution information, see the file
10  *      "mit-copyright.h". 
11  */
12
13 #include <internal.h>
14
15 #ifndef lint
16 static const char rcsid_ZMakeAuthentication_c[] = "$Id: ZMkAuth.c,v 1.20 2002/11/11 18:28:06 ghudson Exp $";
17 #endif
18
19 #ifdef HAVE_KRB4
20 #include <krb_err.h>
21 #endif
22
23 #if defined(HAVE_KRB5) && !HAVE_KRB5_FREE_DATA
24 #define krb5_free_data(ctx, dat) free((dat)->data)
25 #endif
26
27 Code_t ZResetAuthentication () {
28     return ZERR_NONE;
29 }
30
31 Code_t ZMakeAuthentication(notice, buffer, buffer_len, len)
32     register ZNotice_t *notice;
33     char *buffer;
34     int buffer_len;
35     int *len;
36 {
37 #ifdef HAVE_KRB4
38     int result;
39     time_t now;
40     KTEXT_ST authent;
41     char *cstart, *cend;
42     ZChecksum_t checksum;
43     CREDENTIALS cred;
44     extern unsigned long des_quad_cksum();
45
46     result = krb_mk_req(&authent, SERVER_SERVICE, 
47                         SERVER_INSTANCE, __Zephyr_realm, 0);
48     if (result != MK_AP_OK)
49         return (result+krb_err_base);
50     result = krb_get_cred(SERVER_SERVICE, SERVER_INSTANCE,
51                           __Zephyr_realm, &cred);
52     if (result != KSUCCESS)
53         return (result+krb_err_base);
54
55     notice->z_auth = 1;
56     notice->z_authent_len = authent.length;
57     notice->z_ascii_authent = (char *)malloc((unsigned)authent.length*3);
58     /* zero length authent is an error, so malloc(0) is not a problem */
59     if (!notice->z_ascii_authent)
60         return (ENOMEM);
61     if ((result = ZMakeAscii(notice->z_ascii_authent, 
62                              authent.length*3, 
63                              authent.dat, 
64                              authent.length)) != ZERR_NONE) {
65         free(notice->z_ascii_authent);
66         return (result);
67     }
68     result = Z_FormatRawHeader(notice, buffer, buffer_len, len, &cstart,
69                                &cend);
70     free(notice->z_ascii_authent);
71     notice->z_authent_len = 0;
72     if (result)
73         return(result);
74
75     /* Compute a checksum over the header and message. */
76     checksum = des_quad_cksum(buffer, NULL, cstart - buffer, 0, cred.session);
77     checksum ^= des_quad_cksum(cend, NULL, buffer + *len - cend, 0,
78                                cred.session);
79     checksum ^= des_quad_cksum(notice->z_message, NULL, notice->z_message_len,
80                                0, cred.session);
81     notice->z_checksum = checksum;
82     ZMakeAscii32(cstart, buffer + buffer_len - cstart, checksum);
83
84     return (ZERR_NONE);
85 #else
86     notice->z_checksum = 0;
87     notice->z_auth = 1;
88     notice->z_authent_len = 0;
89     notice->z_ascii_authent = "";
90     return (Z_FormatRawHeader(notice, buffer, buffer_len, len, NULL, NULL));
91 #endif
92 }
93
94 Code_t ZMakeZcodeAuthentication(notice, buffer, buffer_len, phdr_len)
95     register ZNotice_t *notice;
96     char *buffer;
97     int buffer_len;
98     int *phdr_len;
99 {
100     return ZMakeZcodeRealmAuthentication(notice, buffer, buffer_len, phdr_len,
101                                          __Zephyr_realm);
102 }
103
104 Code_t ZMakeZcodeRealmAuthentication(notice, buffer, buffer_len, phdr_len, 
105                                      realm)
106     register ZNotice_t *notice;
107     char *buffer;
108     int buffer_len;
109     int *phdr_len;
110     char *realm;
111 {
112 #ifdef HAVE_KRB5
113     krb5_error_code result;
114     krb5_ccache ccache;
115     krb5_creds creds_in, *creds;
116     krb5_keyblock *keyblock;
117     krb5_enctype enctype;
118     krb5_cksumtype cksumtype;
119     krb5_auth_context authctx;
120     krb5_data *authent;
121     krb5_data cksumbuf;
122 #if HAVE_KRB5_C_MAKE_CHECKSUM
123     krb5_checksum checksum;
124 #else
125     krb5_crypto cryptctx;
126     Checksum checksum;
127     size_t xlen;
128 #endif
129     char *svcinst, *x, *y;
130     char *cksum_start, *cstart, *cend, *asn1_data;
131     int plain_len;   /* length of part not to be checksummed */
132     int cksum_len;   /* length of part to be checksummed (incl cksum) */
133     int cksum0_len;  /* length of part before checksum */
134     int cksum1_len;  /* length of part after checksum */
135     int i, zcode_len, asn1_len;
136     
137     /* Get a pointer to the default ccache.  We don't need to free this. */
138     result = krb5_cc_default(Z_krb5_ctx, &ccache);
139     if (result)
140         return result;
141     
142     /* GRRR.  There's no allocator or constructor for krb5_creds */
143     /* GRRR.  It would be nice if this API were documented at all */
144     memset(&creds_in, 0, sizeof(creds_in));
145
146     result = krb5_cc_get_principal(Z_krb5_ctx, ccache, &creds_in.client);
147     if (result) {
148       krb5_cc_close(Z_krb5_ctx, ccache);
149       return(result);
150     }
151
152     /* construct the service principal */
153     result = krb5_build_principal(Z_krb5_ctx, &creds_in.server,
154                                   strlen(realm),
155                                   realm,
156                                   SERVER_KRB5_SERVICE, SERVER_INSTANCE, 0);
157     if (result) {
158         krb5_free_cred_contents(Z_krb5_ctx, &creds_in); /* hope this is OK */
159         krb5_cc_close(Z_krb5_ctx, ccache);
160         return result;
161     }
162     /* HOLDING: creds_in.server, ccache */
163     
164     /* look up or get the credentials we need */
165     result = krb5_get_credentials(Z_krb5_ctx, 0 /* flags */, ccache,
166                                   &creds_in, &creds);
167     krb5_cc_close(Z_krb5_ctx, ccache);
168     krb5_free_cred_contents(Z_krb5_ctx, &creds_in); /* hope this is OK */
169     if (result)
170         return result;
171     /* HOLDING: creds */
172     
173     /* Figure out what checksum type to use */
174 #if HAVE_KRB5_CREDS_KEYBLOCK_ENCTYPE
175     keyblock = &creds->keyblock;
176     enctype  = keyblock->enctype;
177
178     result = Z_krb5_lookup_cksumtype(enctype, &cksumtype);
179     if (result) {
180         krb5_free_creds(Z_krb5_ctx, creds);
181         return result;
182     }
183 #else
184     keyblock = &creds->session;
185     {
186        unsigned int len;
187        ENCTYPE *val;
188        int i = 0;
189
190        result  = krb5_keytype_to_enctypes(Z_krb5_ctx, keyblock->keytype,
191                                            &len, &val);
192        if (result) {
193            krb5_free_creds(Z_krb5_ctx, creds); 
194            return result;
195        }
196
197        do {
198            if (i == len) break;
199            result = Z_krb5_lookup_cksumtype(val[i], &cksumtype);
200            i++;
201        } while (result != 0);
202
203        if (result) {
204            krb5_free_creds(Z_krb5_ctx, creds); 
205            return result;
206        }
207        enctype = val[i-1];
208     }
209 #endif
210     /* HOLDING: creds */
211     
212     /* Create the authenticator */
213     result = krb5_auth_con_init(Z_krb5_ctx, &authctx);
214     if (result) {
215         krb5_free_creds(Z_krb5_ctx, creds);
216         return (result);
217     }
218
219     authent = (krb5_data *)malloc(sizeof(krb5_data));
220
221     /* HOLDING: creds, authctx */
222     result = krb5_mk_req_extended(Z_krb5_ctx, &authctx, 0 /* options */,
223                                   0 /* in_data */, creds, authent);
224     krb5_auth_con_free(Z_krb5_ctx, authctx);
225     if (result) {
226         krb5_free_creds(Z_krb5_ctx, creds);
227         return (result);
228     }
229     /* HOLDING: creds, authent */
230     
231     /* Encode the authenticator */
232     notice->z_auth = 1;
233     notice->z_authent_len = authent->length;
234     zcode_len = authent->length * 2 + 2; /* 2x growth plus Z and null */
235     notice->z_ascii_authent = (char *)malloc(zcode_len);
236     if (!notice->z_ascii_authent) {
237         krb5_free_data(Z_krb5_ctx, authent);
238         krb5_free_creds(Z_krb5_ctx, creds);
239         return (ENOMEM);
240     }
241     /* HOLDING: creds, authent, notice->z_ascii_authent */
242     result = ZMakeZcode(notice->z_ascii_authent, zcode_len, 
243                         authent->data, authent->length);
244     krb5_free_data(Z_krb5_ctx, authent);
245     if (result) {
246         free(notice->z_ascii_authent);
247         krb5_free_creds(Z_krb5_ctx, creds);
248         return (result);
249     }
250     /* HOLDING: creds, notice->z_ascii_authent */
251     
252     /* format the notice header, with a zero checksum */
253     result = Z_NewFormatRawHeader(notice, buffer, buffer_len, phdr_len,
254                                   &cksum_start, &cksum_len, &cstart, &cend);
255     free(notice->z_ascii_authent);
256     notice->z_authent_len = 0;
257     if (result) {
258         krb5_free_creds(Z_krb5_ctx, creds);
259         return (result);
260     }
261     /* HOLDING: creds */
262     
263     /* Assemble the things to be checksummed */
264     plain_len  = cksum_start - buffer;
265     cksum0_len = cstart - cksum_start;
266     cksum1_len = (cksum_start + cksum_len) - cend;
267     memset(&cksumbuf, 0, sizeof(cksumbuf));
268     cksumbuf.length = cksum0_len + cksum1_len + notice->z_message_len;
269     cksumbuf.data = malloc(cksumbuf.length);
270     if (!cksumbuf.data) {
271         krb5_free_creds(Z_krb5_ctx, creds);
272         return (ENOMEM);
273     }
274     /* HOLDING: creds, cksumbuf.data */
275     memcpy(cksumbuf.data, cksum_start, cksum0_len);
276     memcpy(cksumbuf.data + cksum0_len, cend, cksum1_len);
277     memcpy(cksumbuf.data + cksum0_len + cksum1_len,
278            notice->z_message, notice->z_message_len);
279     
280 #if HAVE_KRB5_C_MAKE_CHECKSUM
281     /* Create the checksum -- MIT crypto API */
282     result = krb5_c_make_checksum(Z_krb5_ctx, cksumtype,
283                                   keyblock, Z_KEYUSAGE_CLT_CKSUM,
284                                   &cksumbuf, &checksum);
285     krb5_free_creds(Z_krb5_ctx, creds);
286     if (result) {
287         free(cksumbuf.data);
288         return result;
289     }
290     /* HOLDING: cksumbuf.data, checksum */
291
292     asn1_data = checksum.contents;
293     asn1_len = checksum.length;
294 #else
295     /* Create the checksum -- heimdal crypto API */
296     result = krb5_crypto_init(Z_krb5_ctx, keyblock, enctype, &cryptctx);
297     krb5_free_creds(Z_krb5_ctx, creds);
298     if (result) {
299         free(cksumbuf.data);
300         return result;
301     }
302     /* HOLDING: cksumbuf.data, cryptctx */
303     result = krb5_create_checksum(Z_krb5_ctx, cryptctx,
304                                   Z_KEYUSAGE_CLT_CKSUM, cksumtype,
305                                   cksumbuf.data, cksumbuf.length,
306                                   &checksum);
307     krb5_crypto_destroy(Z_krb5_ctx, cryptctx);
308     if (result) {
309         free(cksumbuf.data);
310         return result;
311     }
312     asn1_len = checksum.checksum.length;
313     asn1_data = checksum.checksum.data;
314     /* HOLDING: cksumbuf.data, checksum */
315 #endif
316     
317     /* 
318      * OK....  we can zcode to a space starting at 'cstart',
319      * with a length of buffer_len - (plain_len + cksum_len).
320      * Then we tack on the end part, which is located at
321      * cksumbuf.data + cksum0_len and has length cksum1_len
322      */
323     result = ZMakeZcode(cstart, buffer_len - (plain_len + cksum_len),
324                         asn1_data, asn1_len);
325     if (!result) {
326         zcode_len = strlen(cstart) + 1;
327         memcpy(cstart + zcode_len, cksumbuf.data + cksum0_len, cksum1_len);
328         *phdr_len -= cksum_len - (cksum0_len + cksum1_len);
329         *phdr_len += zcode_len;
330     }
331     
332     /* free stuff up, and then return the result from the last call */
333
334     free(cksumbuf.data);
335 #if HAVE_KRB5_C_MAKE_CHECKSUM
336     krb5_free_checksum_contents(Z_krb5_ctx, &checksum);
337 #else
338     free_Checksum(&checksum);
339 #endif
340     return (result);
341 #endif /* HAVE_KRB5 */
342 }