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