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