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