rxkad-prototypes-20081129
[openafs.git] / src / rxkad / rxkad_server.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /* The rxkad security object.  Authentication using a DES-encrypted
11  * Kerberos-style ticket.  These are the server-only routines. */
12
13
14 #include <afsconfig.h>
15 #include <afs/param.h>
16
17 RCSID
18     ("$Header$");
19
20 #include <afs/stds.h>
21 #include <sys/types.h>
22 #if (defined(AFS_AIX_ENV) && defined(KERNEL) && !defined(UKERNEL)) || defined(AFS_AUX_ENV) || defined(AFS_SUN5_ENV) 
23 #include <sys/systm.h>
24 #endif
25 #include <time.h>
26 #ifdef AFS_NT40_ENV
27 #include <winsock2.h>
28 #else
29 #include <netinet/in.h>
30 #endif
31 #include <string.h>
32 #include <rx/rx.h>
33 #include <rx/xdr.h>
34 #include <des.h>
35 #include <afs/afsutil.h>
36 #include <des/stats.h>
37 #include "private_data.h"
38 #define XPRT_RXKAD_SERVER
39
40 /*
41  * This can be set to allow alternate ticket decoding.
42  * Currently only used by the AFS/DFS protocol translator to recognize
43  * Kerberos V5 tickets. The actual code to do that is provided externally.
44  */
45 afs_int32(*rxkad_AlternateTicketDecoder) (afs_int32, char *, afs_int32, 
46                                           char *, char *, char *,
47                                           struct ktc_encryptionKey *,
48                                           afs_int32 *, afs_uint32 *,
49                                           afs_uint32 *);
50
51 static struct rx_securityOps rxkad_server_ops = {
52     rxkad_Close,
53     rxkad_NewConnection,
54     rxkad_PreparePacket,        /* once per packet creation */
55     0,                          /* send packet (once per retrans) */
56     rxkad_CheckAuthentication,
57     rxkad_CreateChallenge,
58     rxkad_GetChallenge,
59     0,
60     rxkad_CheckResponse,
61     rxkad_CheckPacket,          /* check data packet */
62     rxkad_DestroyConnection,
63     rxkad_GetStats,
64     rxkad_SetConfiguration,
65     0,                          /* spare 2 */
66     0,                          /* spare 3 */
67 };
68 extern afs_uint32 rx_MyMaxSendSize;
69
70 /* Miscellaneous random number routines that use the fcrypt module and the
71  * timeofday. */
72
73 static fc_KeySchedule random_int32_schedule;
74
75 #ifdef AFS_PTHREAD_ENV
76 /*
77  * This mutex protects the following global variables:
78  * random_int32_schedule
79  * seed
80  */
81
82 #include <assert.h>
83 pthread_mutex_t rxkad_random_mutex;
84 #define LOCK_RM assert(pthread_mutex_lock(&rxkad_random_mutex)==0)
85 #define UNLOCK_RM assert(pthread_mutex_unlock(&rxkad_random_mutex)==0)
86 #else
87 #define LOCK_RM
88 #define UNLOCK_RM
89 #endif /* AFS_PTHREAD_ENV */
90
91 static void
92 init_random_int32(void)
93 {
94     struct timeval key;
95
96     gettimeofday(&key, NULL);
97     LOCK_RM;
98     fc_keysched((struct ktc_encryptionKey*)&key, random_int32_schedule);
99     UNLOCK_RM;
100 }
101
102 static afs_int32
103 get_random_int32(void)
104 {
105     static struct timeval seed;
106     afs_int32 rc;
107
108     LOCK_RM;
109     fc_ecb_encrypt(&seed, &seed, random_int32_schedule, ENCRYPT);
110     rc = seed.tv_sec;
111     UNLOCK_RM;
112     return rc;
113 }
114
115 /* Called with four parameters.  The first is the level of encryption, as
116    defined in the rxkad.h file.  The second and third are a rock and a
117    procedure that is called with the key version number that accompanies the
118    ticket and returns a pointer to the server's decryption key.  The fourth
119    argument, if not NULL, is a pointer to a function that will be called for
120    every new connection with the name, instance and cell of the client.  The
121    routine should return zero if the user is NOT acceptible to the server.  If
122    this routine is not supplied, the server can call rxkad_GetServerInfo with
123    the rx connection pointer passed to the RPC routine to obtain information
124    about the client. */
125
126 /*
127   rxkad_level      level;               * minimum level *
128   char            *get_key_rock;        * rock for get_key implementor *
129   int            (*get_key)();          * passed kvno & addr(key) to fill *
130   int            (*user_ok)();          * passed name, inst, cell => bool *
131 */
132
133 struct rx_securityClass *
134 rxkad_NewServerSecurityObject(rxkad_level level, void *get_key_rock,
135                               int (*get_key) (void *get_key_rock, int kvno,
136                                               struct ktc_encryptionKey *
137                                               serverKey),
138                               int (*user_ok) (char *name, char *instance,
139                                               char *cell, afs_int32 kvno))
140 {
141     struct rx_securityClass *tsc;
142     struct rxkad_sprivate *tsp;
143     int size;
144
145     if (!get_key)
146         return 0;
147
148     size = sizeof(struct rx_securityClass);
149     tsc = (struct rx_securityClass *)osi_Alloc(size);
150     memset(tsc, 0, size);
151     tsc->refCount = 1;          /* caller has one reference */
152     tsc->ops = &rxkad_server_ops;
153     size = sizeof(struct rxkad_sprivate);
154     tsp = (struct rxkad_sprivate *)osi_Alloc(size);
155     memset(tsp, 0, size);
156     tsc->privateData = (char *)tsp;
157
158     tsp->type |= rxkad_server;  /* so can identify later */
159     tsp->level = level;         /* level of encryption */
160     tsp->get_key_rock = get_key_rock;
161     tsp->get_key = get_key;     /* to get server ticket */
162     tsp->user_ok = user_ok;     /* to inform server of client id. */
163     init_random_int32();
164
165     INC_RXKAD_STATS(serverObjects);
166     return tsc;
167 }
168
169 /* server: called to tell if a connection authenticated properly */
170
171 int
172 rxkad_CheckAuthentication(struct rx_securityClass *aobj,
173                           struct rx_connection *aconn)
174 {
175     struct rxkad_sconn *sconn;
176
177     /* first make sure the object exists */
178     if (!aconn->securityData)
179         return RXKADINCONSISTENCY;
180
181     sconn = (struct rxkad_sconn *)aconn->securityData;
182     return !sconn->authenticated;
183 }
184
185 /* server: put the current challenge in the connection structure for later use
186    by packet sender */
187
188 int
189 rxkad_CreateChallenge(struct rx_securityClass *aobj,
190                       struct rx_connection *aconn)
191 {
192     struct rxkad_sconn *sconn;
193     struct rxkad_sprivate *tsp;
194
195     sconn = (struct rxkad_sconn *)aconn->securityData;
196     sconn->challengeID = get_random_int32();
197     sconn->authenticated = 0;   /* conn unauth. 'til we hear back */
198     /* initialize level from object's minimum acceptable level */
199     tsp = (struct rxkad_sprivate *)aobj->privateData;
200     sconn->level = tsp->level;
201     return 0;
202 }
203
204 /* server: fill in a challenge in the packet */
205
206 int
207 rxkad_GetChallenge(struct rx_securityClass *aobj, struct rx_connection *aconn,
208                    struct rx_packet *apacket)
209 {
210     struct rxkad_sconn *sconn;
211     char *challenge;
212     int challengeSize;
213     struct rxkad_v2Challenge c_v2;      /* version 2 */
214     struct rxkad_oldChallenge c_old;    /* old style */
215
216     sconn = (struct rxkad_sconn *)aconn->securityData;
217     if (rx_IsUsingPktCksum(aconn))
218         sconn->cksumSeen = 1;
219
220     if (sconn->cksumSeen) {
221         memset(&c_v2, 0, sizeof(c_v2));
222         c_v2.version = htonl(RXKAD_CHALLENGE_PROTOCOL_VERSION);
223         c_v2.challengeID = htonl(sconn->challengeID);
224         c_v2.level = htonl((afs_int32) sconn->level);
225         c_v2.spare = 0;
226         challenge = (char *)&c_v2;
227         challengeSize = sizeof(c_v2);
228     } else {
229         memset(&c_old, 0, sizeof(c_old));
230         c_old.challengeID = htonl(sconn->challengeID);
231         c_old.level = htonl((afs_int32) sconn->level);
232         challenge = (char *)&c_old;
233         challengeSize = sizeof(c_old);
234     }
235     if (rx_MyMaxSendSize < challengeSize)
236         return RXKADPACKETSHORT;        /* not enough space */
237
238     rx_packetwrite(apacket, 0, challengeSize, challenge);
239     rx_SetDataSize(apacket, challengeSize);
240     sconn->tried = 1;
241     INC_RXKAD_STATS(challengesSent);
242     return 0;
243 }
244
245 /* server: process a response to a challenge packet */
246 /* XXX this does some copying of data in and out of the packet, but I'll bet it
247  * could just do it in place, especially if I used rx_Pullup...
248  */
249 int
250 rxkad_CheckResponse(struct rx_securityClass *aobj,
251                     struct rx_connection *aconn, struct rx_packet *apacket)
252 {
253     struct rxkad_sconn *sconn;
254     struct rxkad_sprivate *tsp;
255     struct ktc_encryptionKey serverKey;
256     struct rxkad_oldChallengeResponse oldr;     /* response format */
257     struct rxkad_v2ChallengeResponse v2r;
258     afs_int32 tlen;             /* ticket len */
259     afs_int32 kvno;             /* key version of ticket */
260     char tix[MAXKTCTICKETLEN];
261     afs_int32 incChallengeID;
262     rxkad_level level;
263     int code;
264     /* ticket contents */
265     struct ktc_principal client;
266     struct ktc_encryptionKey sessionkey;
267     afs_int32 host;
268     afs_uint32 start;
269     afs_uint32 end;
270     unsigned int pos;
271     struct rxkad_serverinfo *rock;
272
273     sconn = (struct rxkad_sconn *)aconn->securityData;
274     tsp = (struct rxkad_sprivate *)aobj->privateData;
275
276     if (sconn->cksumSeen) {
277         /* expect v2 response, leave fields in v2r in network order for cksum
278          * computation which follows decryption. */
279         if (rx_GetDataSize(apacket) < sizeof(v2r))
280             return RXKADPACKETSHORT;
281         rx_packetread(apacket, 0, sizeof(v2r), &v2r);
282         pos = sizeof(v2r);
283         /* version == 2 */
284         /* ignore spare */
285         kvno = ntohl(v2r.kvno);
286         tlen = ntohl(v2r.ticketLen);
287         if (rx_GetDataSize(apacket) < sizeof(v2r) + tlen)
288             return RXKADPACKETSHORT;
289     } else {
290         /* expect old format response */
291         if (rx_GetDataSize(apacket) < sizeof(oldr))
292             return RXKADPACKETSHORT;
293         rx_packetread(apacket, 0, sizeof(oldr), &oldr);
294         pos = sizeof(oldr);
295
296         kvno = ntohl(oldr.kvno);
297         tlen = ntohl(oldr.ticketLen);
298         if (rx_GetDataSize(apacket) != sizeof(oldr) + tlen)
299             return RXKADPACKETSHORT;
300     }
301     if ((tlen < MINKTCTICKETLEN) || (tlen > MAXKTCTICKETLEN))
302         return RXKADTICKETLEN;
303
304     rx_packetread(apacket, pos, tlen, tix);     /* get ticket */
305
306     /*
307      * We allow the ticket to be optionally decoded by an alternate
308      * ticket decoder, if the function variable
309      * rxkad_AlternateTicketDecoder is set. That function should
310      * return a code of -1 if it wants the ticket to be decoded by
311      * the standard decoder.
312      */
313     if (rxkad_AlternateTicketDecoder) {
314         code =
315             rxkad_AlternateTicketDecoder(kvno, tix, tlen, client.name,
316                                          client.instance, client.cell,
317                                          &sessionkey, &host, &start, &end);
318         if (code && code != -1) {
319             return code;
320         }
321     } else {
322         code = -1;              /* No alternate ticket decoder present */
323     }
324
325     /*
326      * If the alternate decoder is not present, or returns -1, then
327      * assume the ticket is of the default style.
328      */
329     if (code == -1 && ((kvno == RXKAD_TKT_TYPE_KERBEROS_V5)
330         || (kvno == RXKAD_TKT_TYPE_KERBEROS_V5_ENCPART_ONLY))) {
331         code =
332             tkt_DecodeTicket5(tix, tlen, tsp->get_key, tsp->get_key_rock,
333                               kvno, client.name, client.instance, client.cell,
334                               &sessionkey, &host, &start, &end, 
335                               tsp->flags & RXS_CONFIG_FLAGS_DISABLE_DOTCHECK);
336         if (code)
337             return code;
338     }
339
340     /*
341      * If the alternate decoder/kerberos 5 decoder is not present, or
342      * returns -1, then assume the ticket is of the default style.
343      */
344     if (code == -1) {
345         /* get ticket's key */
346         code = (*tsp->get_key) (tsp->get_key_rock, kvno, &serverKey);
347         if (code)
348             return RXKADUNKNOWNKEY;     /* invalid kvno */
349         code =
350             tkt_DecodeTicket(tix, tlen, &serverKey, client.name,
351                              client.instance, client.cell, &sessionkey, &host,
352                              &start, &end);
353         if (code)
354             return code;
355     }
356     code = tkt_CheckTimes(start, end, time(0));
357     if (code == 0) 
358         return RXKADNOAUTH;
359     else if (code == -1)
360         return RXKADEXPIRED;
361     else if (code < -1)
362         return RXKADBADTICKET;
363
364     code = fc_keysched(&sessionkey, sconn->keysched);
365     if (code)
366         return RXKADBADKEY;
367     memcpy(sconn->ivec, &sessionkey, sizeof(sconn->ivec));
368
369     if (sconn->cksumSeen) {
370         /* using v2 response */
371         afs_uint32 cksum;       /* observed cksum */
372         struct rxkad_endpoint endpoint; /* connections endpoint */
373         int i;
374         afs_uint32 xor[2];
375
376         memcpy(xor, sconn->ivec, 2 * sizeof(afs_int32));
377         fc_cbc_encrypt(&v2r.encrypted, &v2r.encrypted, sizeof(v2r.encrypted),
378                        sconn->keysched, xor, DECRYPT);
379         cksum = rxkad_CksumChallengeResponse(&v2r);
380         if (cksum != v2r.encrypted.endpoint.cksum)
381             return RXKADSEALEDINCON;
382         (void)rxkad_SetupEndpoint(aconn, &endpoint);
383         v2r.encrypted.endpoint.cksum = 0;
384         if (memcmp(&endpoint, &v2r.encrypted.endpoint, sizeof(endpoint)) != 0)
385             return RXKADSEALEDINCON;
386         for (i = 0; i < RX_MAXCALLS; i++) {
387             v2r.encrypted.callNumbers[i] =
388                 ntohl(v2r.encrypted.callNumbers[i]);
389             if (v2r.encrypted.callNumbers[i] < 0)
390                 return RXKADSEALEDINCON;
391         }
392
393         (void)rxi_SetCallNumberVector(aconn, v2r.encrypted.callNumbers);
394         incChallengeID = ntohl(v2r.encrypted.incChallengeID);
395         level = ntohl(v2r.encrypted.level);
396     } else {
397         /* expect old format response */
398         fc_ecb_encrypt(&oldr.encrypted, &oldr.encrypted, sconn->keysched,
399                        DECRYPT);
400         incChallengeID = ntohl(oldr.encrypted.incChallengeID);
401         level = ntohl(oldr.encrypted.level);
402     }
403     if (incChallengeID != sconn->challengeID + 1)
404         return RXKADOUTOFSEQUENCE;      /* replay attempt */
405     if ((level < sconn->level) || (level > rxkad_crypt))
406         return RXKADLEVELFAIL;
407     sconn->level = level;
408     rxkad_SetLevel(aconn, sconn->level);
409     INC_RXKAD_STATS(responses[rxkad_LevelIndex(sconn->level)]);
410     /* now compute endpoint-specific info used for computing 16 bit checksum */
411     rxkad_DeriveXORInfo(aconn, sconn->keysched, sconn->ivec, sconn->preSeq);
412
413     /* otherwise things are ok */
414     sconn->expirationTime = end;
415     sconn->authenticated = 1;
416
417     if (tsp->user_ok) {
418         code = tsp->user_ok(client.name, client.instance, client.cell, kvno);
419         if (code)
420             return RXKADNOAUTH;
421     } else {                    /* save the info for later retreival */
422         int size = sizeof(struct rxkad_serverinfo);
423         rock = (struct rxkad_serverinfo *)osi_Alloc(size);
424         memset(rock, 0, size);
425         rock->kvno = kvno;
426         memcpy(&rock->client, &client, sizeof(rock->client));
427         sconn->rock = rock;
428     }
429     return 0;
430 }
431
432 /* return useful authentication info about a server-side connection */
433
434 afs_int32
435 rxkad_GetServerInfo(struct rx_connection * aconn, rxkad_level * level,
436                     afs_uint32 * expiration, char *name, char *instance,
437                     char *cell, afs_int32 * kvno)
438 {
439     struct rxkad_sconn *sconn;
440
441     sconn = (struct rxkad_sconn *)aconn->securityData;
442     if (sconn && sconn->authenticated && sconn->rock
443         && (time(0) < sconn->expirationTime)) {
444         if (level)
445             *level = sconn->level;
446         if (expiration)
447             *expiration = sconn->expirationTime;
448         if (name)
449             strcpy(name, sconn->rock->client.name);
450         if (instance)
451             strcpy(instance, sconn->rock->client.instance);
452         if (cell)
453             strcpy(cell, sconn->rock->client.cell);
454         if (kvno)
455             *kvno = sconn->rock->kvno;
456         return 0;
457     } else
458         return RXKADNOAUTH;
459 }
460
461 /* Set security object configuration variables */
462 afs_int32 rxkad_SetConfiguration(struct rx_securityClass *aobj,
463                                  struct rx_connection *aconn, 
464                                  rx_securityConfigVariables atype,
465                                          void * avalue, void **currentValue)
466 {
467     struct rxkad_sprivate *private = 
468     (struct rxkad_sprivate *) aobj->privateData;
469
470     switch (atype) {
471     case RXS_CONFIG_FLAGS:
472         if (currentValue) {
473             *((afs_uint32 *)currentValue) = private->flags;
474         } else { 
475             private->flags = (afs_uint32) avalue;
476         }
477         break;
478     default:
479         break;
480     }
481     return 0;
482 }