0f70214a375009ac483e20432d7bd9a0f59ce244
[openafs.git] / src / rxgk / rxgk_serv.c
1 /*
2  * Copyright (c) 1995 - 1998, 2002 Kungliga Tekniska Högskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
5  * 
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 
17  * 3. Neither the name of the Institute nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  * 
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33
34 #include "rxgk_locl.h"
35
36 RCSID("$Id$");
37
38 #include <errno.h>
39
40 #include "rxgk_proto.ss.h"
41
42 /* Security object specific server data */
43 typedef struct rxgk_serv_class {
44     struct rx_securityClass klass;
45     rxgk_level min_level;
46     char *principal;
47     char *service_principal;
48     void *appl_data;
49     int (*get_key)(void *, const char *, int, int, krb5_keyblock *);
50     int (*user_ok)(const char *name, const char *realm, int kvno);
51     uint32_t serviceId;
52 } rxgk_serv_class;
53
54 extern krb5_context gk_context;
55 extern krb5_crypto gk_crypto;
56
57 static int
58 server_NewConnection(struct rx_securityClass *obj, struct rx_connection *con)
59 {
60     serv_con_data *cdat;
61     assert(con->securityData == 0);
62     assert(gk_context != NULL);
63     obj->refCount++;
64     con->securityData = (char *) osi_Alloc(sizeof(serv_con_data));
65     memset(con->securityData, 0x0, sizeof(serv_con_data));
66     cdat = (serv_con_data *)con->securityData;
67     cdat->k.ks_context = gk_context;
68     return 0;
69 }
70
71 static int
72 server_Close(struct rx_securityClass *obj)
73 {
74     obj->refCount--;
75     if (obj->refCount <= 0)
76         osi_Free(obj, sizeof(rxgk_serv_class));
77     return 0;
78 }
79
80 static
81 int
82 server_DestroyConnection(struct rx_securityClass *obj,
83                          struct rx_connection *con)
84 {
85   serv_con_data *cdat = (serv_con_data *)con->securityData;
86
87   if (cdat)
88       osi_Free(cdat, sizeof(serv_con_data));
89   return server_Close(obj);
90 }
91
92 /*
93  * Check whether a connection authenticated properly.
94  * Zero is good (authentication succeeded).
95  */
96 static int
97 server_CheckAuthentication(struct rx_securityClass *obj,
98                            struct rx_connection *con)
99 {
100     serv_con_data *cdat = (serv_con_data *) con->securityData;
101
102     if (cdat)
103         return !cdat->authenticated;
104     else
105         return RXGKNOAUTH;
106 }
107
108 /*
109  * Select a nonce for later use.
110  */
111 static
112 int
113 server_CreateChallenge(struct rx_securityClass *obj_,
114                        struct rx_connection *con)
115 {
116     serv_con_data *cdat = (serv_con_data *) con->securityData;
117
118     cdat->nonce = arc4random();
119     cdat->authenticated = 0;
120     return 0;
121 }
122
123 /*
124  * Wrap the nonce in a challenge packet.
125  */
126 static int
127 server_GetChallenge(const struct rx_securityClass *obj_,
128                     const struct rx_connection *con,
129                     struct rx_packet *pkt)
130 {
131     rxgk_serv_class *obj = (rxgk_serv_class *) obj_;
132     serv_con_data *cdat = (serv_con_data *) con->securityData;
133     struct RXGK_Challenge c;
134     int initial_challage = 1;
135
136     c.rc_version = htonl(RXGK_VERSION);
137     c.rc_nonce = htonl(cdat->nonce);
138     c.rc_max_seq_skew = htonl(200); /* XXX */
139     c.rc_min_level = htonl(obj->min_level);
140
141     if (initial_challage) {
142         /* Make challenge */
143         c.rc_opcode = htonl(RXKG_OPCODE_CHALLENGE);
144     
145         /* Stuff into packet */
146         if (rx_SlowWritePacket(pkt, 0, sizeof(c), &c) != sizeof(c))
147             return RXGKPACKETSHORT;
148         rx_SetDataSize(pkt, sizeof(c));
149 #if 0
150     } else {
151         RXGK_ReKey rk;
152         RXGK_ReKey_Crypt rkc;
153         char bufrk[RXGK_REKEY_MAX_SIZE];
154         char bufrkc[RXGK_REKEY_CRYPT_MAX_SIZE];
155         krb5_data data;
156         size_t sz;
157         int ret;
158         key_stuff *k = &cdat->k;
159
160         memset(&rk, 0, sizeof(rk));
161         memset(&rkc, 0, sizeof(rkc));
162
163         c.rc_opcode = htonl(RXKG_OPCODE_REKEY);
164
165         if (rx_SlowWritePacket(pkt, 0, sizeof(c), &c) != sizeof(c))
166             return RXGKPACKETSHORT;
167         
168         rkc.rkc_version = RXGK_VERSION;
169         rkc.rkc_max_seq_num = 200; /* XXX */
170         rkc.rkc_kvno = /* current_key + 1 */ 1;
171         rkc.rkc_keycontribution.len = 0; /* XXX */
172         rkc.rkc_keycontribution.val = NULL;
173
174         sz = RXGK_REKEY_CRYPT_MAX_SIZE;
175         if (ydr_encode_RXGK_ReKey_Crypt(&rkc, bufrkc, &sz) == NULL)
176             return RXGKPACKETSHORT;
177         sz = RXGK_REKEY_CRYPT_MAX_SIZE - sz;
178         
179         ret = krb5_encrypt(k->ks_context, k->ks_crypto, 
180                            RXGK_SERVER_ENC_REKEY, bufrkc, sz, &data);
181         if (ret)
182             return ret;
183
184         rk.rk_ctext.val = data.data;
185         rk.rk_ctext.len = data.length;
186
187         sz = RXGK_REKEY_MAX_SIZE;
188         if (ydr_encode_RXGK_ReKey(&rk, bufrk, &sz) == NULL) {
189             krb5_data_free(&data);
190             return RXGKPACKETSHORT;
191         }
192         sz = RXGK_REKEY_MAX_SIZE - sz;
193
194         krb5_data_free(&data);
195         
196         if (rx_SlowWritePacket(pkt, sizeof(c), sz, bufrk) != sz)
197             return RXGKPACKETSHORT;
198
199         rx_SetDataSize(pkt, sizeof(c) + sz);
200 #endif
201     }
202     return 0;
203 }
204
205 /*
206  * Process a response to a challange.
207  */
208 static int
209 server_CheckResponse(struct rx_securityClass *obj_,
210                      struct rx_connection *con,
211                      struct rx_packet *pkt)
212 {
213     serv_con_data *cdat = (serv_con_data *) con->securityData;
214     
215     struct RXGK_Response r;
216     struct RXGK_Response_Crypt rc;
217     struct RXGK_AUTH_CRED c;
218     char response[RXGK_RESPONSE_MAX_SIZE];
219     size_t len, len2;
220     int ret;
221     krb5_context context = cdat->k.ks_context;
222     krb5_data data;
223     
224     memset(&r, 0, sizeof(r));
225     memset(&c, 0, sizeof(r));
226     
227     len = rx_SlowReadPacket(pkt, 0, sizeof(response), response);
228     if (len <= 0)
229         return RXGKPACKETSHORT;
230     
231     len2 = len;
232     if (ydr_decode_RXGK_Response(&r, response, &len2) == NULL) {
233         ret = RXGKPACKETSHORT;
234         goto out;
235     }
236     
237     ret = rxgk_decode_auth_token(r.rr_auth_token.val, r.rr_auth_token.len, &c);
238     if (ret)
239         goto out;
240     
241     ret = rxgk_random_to_key(c.ac_enctype, c.ac_key.val, c.ac_key.len,
242                              &cdat->k.ks_key);
243     if (ret)
244         goto out;
245
246     cdat->k.ks_crypto = NULL; /* XXX */
247     ret = krb5_crypto_init(context, &cdat->k.ks_key, cdat->k.ks_key.keytype, 
248                            &cdat->k.ks_crypto);
249     if (ret)
250         goto out2;
251
252
253     ret = krb5_decrypt(context, cdat->k.ks_crypto, RXGK_CLIENT_ENC_CHALLENGE, 
254                        r.rr_ctext.val, r.rr_ctext.len, &data);
255     if (ret)
256         goto out2;
257
258     len = data.length;
259     if (ydr_decode_RXGK_Response_Crypt(&rc, data.data, &len) == NULL) {
260         krb5_data_free(&data);
261         goto out2;
262     }
263
264     krb5_data_free(&data);
265
266     if (rc.epoch != con->epoch
267         || rc.cid != (con->cid & RX_CIDMASK)
268 #if 0
269         || rc.security_index != con->securityIndex
270 #endif
271         ) {
272         ret = RXGKSEALEDINCON;
273         goto out2;
274     }
275
276     {
277         int i;
278         for (i = 0; i < RX_MAXCALLS; i++)
279         {
280             if (rc.call_numbers[i] < 0) {
281                 ret = RXGKSEALEDINCON;
282                 goto out2;
283             }
284         }
285
286     }
287
288     if (rc.nonce != cdat->nonce + 1) {
289         ret = RXGKOUTOFSEQUENCE;
290         goto out2;
291     }
292
293     /* XXX */
294     if (rc.level != rxgk_crypt) {
295         ret = RXGKLEVELFAIL;
296         goto out2;
297     }
298
299     if ((rc.level != rxgk_auth && rc.level != rxgk_crypt) ||
300         rc.level < cdat->cur_level) 
301     {
302         ret = RXGKLEVELFAIL;
303         goto out2;
304     }
305
306 #if 0
307     ret = rxgk_derive_transport_key(context, &cdat->k.ks_key,
308                                     &rc.contrib, &cdat->k.ks_skey);
309     if (ret)
310         goto out2;
311 #endif
312
313     ret = krb5_crypto_init (context, &cdat->k.ks_skey,
314                             cdat->k.ks_skey.keytype,
315                             &cdat->k.ks_scrypto);
316     if (ret)
317         goto out2;
318
319     rxi_SetCallNumberVector(con, rc.call_numbers);
320
321     cdat->authenticated = 1;
322     cdat->expires = c.ac_endtime;
323     cdat->cur_level = rc.level;
324
325     rxgk_set_conn(con, cdat->k.ks_key.keytype,
326                   rc.level == rxgk_crypt ? 1 : 0);
327
328  out2:
329     if (ret) {
330         krb5_free_keyblock_contents(context, &cdat->k.ks_key);
331         if (cdat->k.ks_crypto)
332             krb5_crypto_destroy(context, cdat->k.ks_crypto);
333         cdat->k.ks_crypto = NULL;
334     }
335
336  out:  
337
338     ydr_free_RXGK_AUTH_CRED(&c);
339     ydr_free_RXGK_Response(&r);
340     
341     return ret;
342 }
343
344 /*
345  * Checksum and/or encrypt packet
346  */
347 static int
348 server_PreparePacket(struct rx_securityClass *obj_,
349                      struct rx_call *call,
350                      struct rx_packet *pkt)
351 {
352     struct rx_connection *con = rx_ConnectionOf(call);
353     serv_con_data *cdat = (serv_con_data *) con->securityData;
354     key_stuff *k = &cdat->k;
355     end_stuff *e = &cdat->e;
356     
357     return rxgk_prepare_packet(pkt, con, cdat->cur_level, k, e);
358 }
359
360 /*
361  * Verify checksum and/or decrypt packet.
362  */
363 static int
364 server_CheckPacket(struct rx_securityClass *obj_,
365                    struct rx_call *call,
366                    struct rx_packet *pkt)
367 {
368     struct rx_connection *con = rx_ConnectionOf(call);
369     serv_con_data *cdat = (serv_con_data *) con->securityData;
370     key_stuff *k = &cdat->k;
371     end_stuff *e = &cdat->e;
372
373     if (time(0) > cdat->expires)        /* Use fast time package instead??? */
374         return RXGKEXPIRED;
375
376     return rxgk_check_packet(pkt, con, cdat->cur_level, k, e);
377 }
378
379 static int
380 server_GetStats(const struct rx_securityClass *obj_,
381                 const struct rx_connection *con,
382                 struct rx_securityObjectStats *st)
383 {
384     rxgk_serv_class *obj = (rxgk_serv_class *) obj_;
385     serv_con_data *cdat = (serv_con_data *) con->securityData;
386     
387     st->type = rxgk_disipline;
388     st->level = obj->min_level;
389     st->flags = rxgk_checksummed;
390     if (cdat == 0)
391         st->flags |= rxgk_unallocated;
392     {
393         st->bytesReceived = cdat->e.bytesReceived;
394         st->packetsReceived = cdat->e.packetsReceived;
395         st->bytesSent = cdat->e.bytesSent;
396         st->packetsSent = cdat->e.packetsSent;
397         st->expires = cdat->expires;
398         st->level = cdat->cur_level;
399         if (cdat->authenticated)
400             st->flags |= rxgk_authenticated;
401     }
402     return 0;
403 }
404
405 static
406 void
407 free_context(void)
408 {
409     return;
410 }
411
412 static
413 int
414 server_NewService(const struct rx_securityClass *obj_,
415                   struct rx_service *service,
416                   int reuse)
417 {
418     rxgk_serv_class *obj = (rxgk_serv_class *) obj_;
419
420     if (service->serviceId == obj->serviceId)
421         return 0;
422     
423     if (!reuse) {
424         struct rx_securityClass *sec[2];
425         struct rx_service *secservice;
426         
427         sec[0] = rxnull_NewServerSecurityObject();
428         sec[1] = NULL;
429         
430         secservice = rx_NewService (service->servicePort,
431                                     obj->serviceId,
432                                     "rxgk", 
433                                     sec, 1, 
434                                     RXGK_ExecuteRequest);
435         
436         secservice->destroyConnProc = free_context;
437         rx_setServiceRock(secservice, obj->principal);
438     }
439     return 0;
440 }
441
442
443 static struct rx_securityOps server_ops = {
444     server_Close,
445     server_NewConnection,
446     server_PreparePacket,
447     NULL,
448     server_CheckAuthentication,
449     server_CreateChallenge,
450     server_GetChallenge,
451     NULL,
452     server_CheckResponse,
453     server_CheckPacket,
454     server_DestroyConnection,
455     server_GetStats,
456     server_NewService,
457 };
458
459 struct rx_securityClass *
460 rxgk_NewServerSecurityObject(/*rxgk_level*/ int min_level,
461                              const char *principal,
462                              void *appl_data,
463                              int (*get_key)(void *data, const char *principal,
464                                             int enctype, int kvno,
465                                             krb5_keyblock *key),
466                              int (*user_ok)(const char *name,
467                                             const char *realm,
468                                             int kvno),
469                              uint32_t serviceId)
470 {
471     rxgk_serv_class *obj;
472     int ret;
473
474     if (get_key == NULL || principal == NULL)
475         return NULL;
476
477     ret = rxgk_server_init();
478     if (ret)
479         return NULL;
480
481     obj = (rxgk_serv_class *) osi_Alloc(sizeof(rxgk_serv_class));
482     obj->klass.refCount = 1;
483     obj->klass.ops = &server_ops;
484     obj->klass.privateData = (char *) obj;
485     
486     obj->min_level = min_level;
487     obj->appl_data = appl_data;
488     obj->get_key = get_key;
489     obj->user_ok = user_ok;
490     obj->principal = strdup(principal);
491     if (obj->principal == NULL) {
492         osi_Free(obj, sizeof(rxgk_serv_class));
493         return NULL;
494     }
495     obj->serviceId = serviceId;
496     
497     return &obj->klass;
498 }