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