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