5bcb2cea5da01c94831762ea27a0dd8eb712e041
[openafs.git] / src / rxgk / rxgk_clnt.c
1 /*
2  * Copyright (c) 1995 - 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 /* Security object specific client data */
39 typedef struct rxgk_clnt_class {
40     struct rx_securityClass klass;
41     krb5_context context;
42     rxgk_level level;
43     krb5_keyblock krb_key;
44     key_stuff k;
45     int32_t kvno;
46     RXGK_Token ticket;
47     uint32_t serviceId;
48 #if 0
49     RXGK_rxtransport_key contrib;
50 #endif
51 } rxgk_clnt_class;
52
53 /* Per connection specific client data */
54 typedef struct clnt_con_data {
55     RXGK_Token auth_token;
56     int32_t auth_token_kvno;
57     end_stuff e;
58 } clnt_con_data;
59
60 static
61 int
62 client_NewConnection(struct rx_securityClass *obj_, struct rx_connection *con)
63 {
64     rxgk_clnt_class *obj = (rxgk_clnt_class *) obj_;
65     clnt_con_data *cdat;
66     int ret;
67     
68     assert(con->securityData == 0);
69     obj->klass.refCount++;
70     cdat = (clnt_con_data *) osi_Alloc(sizeof(clnt_con_data));
71     cdat->e.bytesReceived = cdat->e.packetsReceived = 0;
72     cdat->e.bytesSent = cdat->e.packetsSent = 0;
73     
74     con->securityData = (char *) cdat;
75     rx_nextCid += RX_MAXCALLS;
76     con->epoch = rx_epoch;
77     con->cid = rx_nextCid;
78     cdat->auth_token.len = 0;
79     cdat->auth_token.val = NULL;
80
81     ret = rxgk5_get_auth_token(obj->context,
82                                rx_HostOf(con->peer), rx_PortOf(con->peer),
83                                obj->serviceId,
84                                &obj->ticket, &cdat->auth_token, 
85                                &obj->krb_key, 
86                                &obj->k.ks_key,
87                                &cdat->auth_token_kvno);
88     if (ret) {
89         osi_Free(cdat, sizeof(clnt_con_data));
90         return ret;
91     }
92
93     /* XXX derive crypto key */
94
95     ret = krb5_crypto_init(obj->k.ks_context,
96                            &obj->k.ks_key, obj->k.ks_key.keytype,
97                            &obj->k.ks_crypto);
98     if (ret)
99         goto out;
100
101 #if 0
102     obj->contrib.server_keycontribution.val = "";
103     obj->contrib.server_keycontribution.len = 0;
104
105     obj->contrib.client_keycontribution.len = rxgk_key_contrib_size;
106     obj->contrib.client_keycontribution.val = malloc(rxgk_key_contrib_size);
107     if (obj->contrib.client_keycontribution.val == NULL)
108         goto out;
109
110     {
111         int i;
112
113         for (i = 0; i < rxgk_key_contrib_size; i++)
114             obj->contrib.client_keycontribution.val[i] = arc4random(); /*XXX*/
115     }
116
117     ret = rxgk_derive_transport_key(obj->k.ks_context,
118                                     &obj->k.ks_key,
119                                     &obj->contrib,
120                                     &obj->k.ks_skey);
121     if (ret)
122         return ret;
123 #endif
124
125     ret = krb5_crypto_init (obj->context, &obj->k.ks_skey,
126                             obj->k.ks_skey.keytype,
127                             &obj->k.ks_scrypto);
128     if (ret)
129         return ret;
130
131     ret = rxgk_set_conn(con, obj->k.ks_key.keytype, 
132                         obj->level == rxgk_crypt ? 1 : 0);
133
134  out:
135     if (ret) {
136         if (obj->k.ks_crypto)
137             krb5_crypto_destroy(obj->k.ks_context, obj->k.ks_crypto);
138         obj->k.ks_crypto = NULL;
139         krb5_free_keyblock_contents(obj->k.ks_context, &obj->k.ks_skey);
140         memset(&obj->k.ks_skey, 0, sizeof(obj->k.ks_skey));
141         osi_Free(cdat->auth_token.val, cdat->auth_token.len);
142         osi_Free(cdat, sizeof(clnt_con_data));
143         return ret;
144     }
145     
146     return 0;
147 }
148
149 static
150 int
151 client_Close(struct rx_securityClass *obj_)
152 {
153     rxgk_clnt_class *obj = (rxgk_clnt_class *) obj_;
154     obj->klass.refCount--;
155     if (obj->klass.refCount <= 0)
156     {
157         osi_Free(obj->ticket.val, obj->ticket.len);
158         osi_Free(obj, sizeof(rxgk_clnt_class));
159     }
160     return 0;
161 }
162
163 static
164 int
165 client_DestroyConnection(struct rx_securityClass *obj,
166                          struct rx_connection *con)
167 {
168     clnt_con_data *cdat = (clnt_con_data *)con->securityData;
169   
170     if (cdat)
171       osi_Free(cdat, sizeof(clnt_con_data));
172     return client_Close(obj);
173 }
174
175 /*
176  * Receive a challange and respond.
177  */
178 static
179 int
180 client_GetResponse(const struct rx_securityClass *obj_,
181                    const struct rx_connection *con,
182                    struct rx_packet *pkt)
183 {
184     rxgk_clnt_class *obj = (rxgk_clnt_class *) obj_;
185     clnt_con_data *cdat = (clnt_con_data *)con->securityData;
186     key_stuff *k = &obj->k;
187     struct RXGK_Challenge c;
188     struct RXGK_Response r;
189     struct RXGK_Response_Crypt rc;
190     char bufrc[RXGK_RESPONSE_CRYPT_SIZE];
191     char bufr[RXGK_RESPONSE_MAX_SIZE];
192     krb5_data data;
193     size_t len;
194     int ret;
195     char *p;
196     
197     memset(&r, 0, sizeof(r));
198     memset(&rc, 0, sizeof(rc));
199     
200     /* Get challenge */
201     if (rx_SlowReadPacket(pkt, 0, sizeof(c), &c) != sizeof(c))
202         return RXGKPACKETSHORT;
203     
204     if (ntohl(c.rc_version) != RXGK_VERSION)
205         return RXGKINCONSISTENCY;
206     
207     if (ntohl(c.rc_min_level) > obj->level)
208         return RXGKLEVELFAIL;
209     
210     if (c.rc_opcode == htonl(RXKG_OPCODE_CHALLENGE)) {
211         ;
212     } else if (c.rc_opcode == htonl(RXKG_OPCODE_REKEY)) {
213         /* XXX decode ydr_encode_RXGK_ReKey_Crypt info */
214         return RXGKINCONSISTENCY;
215 #if 0
216         ret = rxgk_derive_transport_key(obj->k.ks_context,
217                                         &obj->k.ks_key,
218                                         &obj->contrib,
219                                         &obj->k.ks_skey);
220         if (ret)
221             return ret;
222         
223         ret = krb5_crypto_init (obj->context, &obj->k.ks_skey,
224                                 obj->k.ks_skey.keytype,
225                                 &obj->k.ks_scrypto);
226         if (ret)
227             return ret;
228 #endif
229     } else
230         return RXGKINCONSISTENCY;
231     
232     rc.nonce = ntohl(c.rc_nonce) + 1;
233     rc.epoch = con->epoch;
234     rc.cid = con->cid & RX_CIDMASK;
235     rxi_GetCallNumberVector(con, rc.call_numbers);
236 #if 0
237     rc.security_index = con->securityIndex;
238 #endif
239     rc.level = obj->level;
240 #if 0
241     rc.last_seq = 0; /* XXX */
242 #endif
243     rc.key_version = 0;
244 #if 0
245     rc.contrib = obj->contrib; /* XXX copy_RXGK_rxtransport_key */
246 #endif
247     
248     {
249         int i;
250         for (i = 0; i < RX_MAXCALLS; i++) {
251             if (rc.call_numbers[i] < 0)
252                 return RXGKINCONSISTENCY;
253         }
254     }
255     len = sizeof(bufrc);
256     p = ydr_encode_RXGK_Response_Crypt(&rc, bufrc, &len);
257     if (p == NULL)
258         return RXGKINCONSISTENCY;
259     len = sizeof(bufrc) - len;
260
261     ret = krb5_encrypt(obj->context, k->ks_crypto, 
262                        RXGK_CLIENT_ENC_CHALLENGE, bufrc, len, &data);
263     if (ret)
264         return ret;
265
266     r.rr_version = RXGK_VERSION;
267     r.rr_auth_token_kvno = cdat->auth_token_kvno;
268     r.rr_auth_token.val = cdat->auth_token.val;
269     r.rr_auth_token.len = cdat->auth_token.len;
270     r.rr_ctext.val = data.data;
271     r.rr_ctext.len = data.length;
272
273     len = sizeof(bufr);
274     p = ydr_encode_RXGK_Response(&r, bufr, &len);
275     len = sizeof(bufr) - len;
276     krb5_data_free(&data);
277
278     if (p == NULL)
279         return RXGKINCONSISTENCY;
280
281     if (rx_SlowWritePacket(pkt, 0, len, bufr) != len)
282         return RXGKPACKETSHORT;
283     rx_SetDataSize(pkt, len);
284
285     return 0;
286 }
287
288 /*
289  * Checksum and/or encrypt packet.
290  */
291 static
292 int
293 client_PreparePacket(struct rx_securityClass *obj_,
294                      struct rx_call *call,
295                      struct rx_packet *pkt)
296 {
297     rxgk_clnt_class *obj = (rxgk_clnt_class *) obj_;
298     key_stuff *k = &obj->k;
299     struct rx_connection *con = rx_ConnectionOf(call);
300     end_stuff *e = &((clnt_con_data *) con->securityData)->e;
301     
302     return rxgk_prepare_packet(pkt, con, obj->level, k, e);
303 }
304
305 /*
306  * Verify checksums and/or decrypt packet.
307  */
308 static
309 int
310 client_CheckPacket(struct rx_securityClass *obj_,
311                    struct rx_call *call,
312                    struct rx_packet *pkt)
313 {
314     rxgk_clnt_class *obj = (rxgk_clnt_class *) obj_;
315     key_stuff *k = &obj->k;
316     struct rx_connection *con = rx_ConnectionOf(call);
317     end_stuff *e = &((clnt_con_data *) con->securityData)->e;
318     
319     return rxgk_check_packet(pkt, con, obj->level, k, e);
320 }
321
322 static
323 int
324 client_GetStats(const struct rx_securityClass *obj,
325                 const struct rx_connection *con,
326                 struct rx_securityObjectStats *st)
327 {
328     clnt_con_data *cdat = (clnt_con_data *) con->securityData;
329     
330     st->type = rxgk_disipline;
331     st->level = ((rxgk_clnt_class *)obj)->level;
332     st->flags = rxgk_checksummed;
333     if (cdat == 0)
334         st->flags |= rxgk_unallocated;
335     {
336         st->bytesReceived = cdat->e.bytesReceived;
337         st->packetsReceived = cdat->e.packetsReceived;
338         st->bytesSent = cdat->e.bytesSent;
339         st->packetsSent = cdat->e.packetsSent;
340     }
341     return 0;
342 }
343
344 static
345 struct rx_securityOps client_ops = {
346     client_Close,
347     client_NewConnection,
348     client_PreparePacket,
349     NULL,
350     NULL,
351     NULL,
352     NULL,
353     client_GetResponse,
354     NULL,
355     client_CheckPacket,
356     client_DestroyConnection,
357     client_GetStats,
358     NULL,
359     NULL,
360     NULL,
361 };
362
363 int rxgk_EpochWasSet = 0;
364
365 int rxgk_min_level = rxgk_crypt; /* rxgk_{auth,crypt} */ /* XXX */
366
367 struct rx_securityClass *
368 rxgk_k5_NewClientSecurityObject(/*rxgk_level*/ int level,
369                                 krb5_keyblock *key,
370                                 int32_t kvno,
371                                 int ticket_len,
372                                 void *ticket,
373                                 uint32_t serviceId,
374                                 krb5_context context)
375 {
376     rxgk_clnt_class *obj;
377     static int inited = 0;
378     int ret;
379     
380     if (level < rxgk_min_level)
381         level = rxgk_min_level; /* Boost security level */
382     
383     if (!inited) {
384         rx_SetEpoch(arc4random());
385         inited = 1;
386     }
387     
388     obj = (rxgk_clnt_class *) osi_Alloc(sizeof(rxgk_clnt_class));
389     obj->klass.refCount = 1;
390     obj->klass.ops = &client_ops;
391     
392     obj->klass.privateData = (char *) obj;
393     
394     obj->context = context;
395     obj->level = level;
396     obj->kvno = kvno;
397     obj->serviceId = serviceId;
398     
399     ret = krb5_copy_keyblock_contents(context, key, &obj->krb_key);
400     if (ret) {
401         osi_Free(obj, sizeof(rxgk_clnt_class));
402         return NULL;
403     }
404
405     memset(&obj->k.ks_key, 0, sizeof(obj->k.ks_key));
406     obj->k.ks_crypto = NULL;
407
408     memset(&obj->k.ks_skey, 0, sizeof(obj->k.ks_skey));
409     obj->k.ks_scrypto = NULL;
410     obj->k.ks_context = context;
411
412     obj->ticket.len = ticket_len;
413     obj->ticket.val = osi_Alloc(ticket_len);
414     memcpy(obj->ticket.val, ticket, obj->ticket.len);
415     
416     return &obj->klass;
417 }