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