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