rxkad-warning-cleanup-20011005
[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 ()
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 ()
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 struct rx_securityClass *
117 rxkad_NewServerSecurityObject (level, get_key_rock, get_key, user_ok)
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 {   struct rx_securityClass *tsc;
123     struct rxkad_sprivate   *tsp;
124     int size;
125
126     if (!get_key) return 0;
127     
128     size = sizeof(struct rx_securityClass);
129     tsc = (struct rx_securityClass *) osi_Alloc (size);
130     memset(tsc, 0, size);
131     tsc->refCount = 1;                  /* caller has one reference */
132     tsc->ops = &rxkad_server_ops;
133     size = sizeof(struct rxkad_sprivate);
134     tsp = (struct rxkad_sprivate *) osi_Alloc (size);
135     memset(tsp, 0, size);
136     tsc->privateData = (char *) tsp;
137
138     tsp->type |= rxkad_server;          /* so can identify later */
139     tsp->level = level;                 /* level of encryption */
140     tsp->get_key_rock = get_key_rock;
141     tsp->get_key = get_key;             /* to get server ticket */
142     tsp->user_ok = user_ok;             /* to inform server of client id. */
143     init_random_int32 ();
144
145     LOCK_RXKAD_STATS
146     rxkad_stats_serverObjects++;
147     UNLOCK_RXKAD_STATS
148     return tsc;
149 }
150
151 /* server: called to tell if a connection authenticated properly */
152
153 rxs_return_t rxkad_CheckAuthentication (aobj, aconn)
154   struct rx_securityClass *aobj;
155   struct rx_connection *aconn;
156 {   struct rxkad_sconn *sconn;
157
158     /* first make sure the object exists */
159     if (!aconn->securityData) return RXKADINCONSISTENCY;
160
161     sconn = (struct rxkad_sconn *) aconn->securityData;
162     return !sconn->authenticated;
163 }
164
165 /* server: put the current challenge in the connection structure for later use
166    by packet sender */
167
168 rxs_return_t rxkad_CreateChallenge(aobj, aconn)
169   struct rx_securityClass *aobj;
170   struct rx_connection *aconn;
171 {   struct rxkad_sconn *sconn;
172     struct rxkad_sprivate *tsp;
173
174     sconn = (struct rxkad_sconn *) aconn->securityData;
175     sconn->challengeID = get_random_int32 ();
176     sconn->authenticated = 0;           /* conn unauth. 'til we hear back */
177     /* initialize level from object's minimum acceptable level */
178     tsp = (struct rxkad_sprivate *)aobj->privateData;
179     sconn->level = tsp->level;
180     return 0;
181 }
182
183 /* server: fill in a challenge in the packet */
184
185 rxs_return_t rxkad_GetChallenge (aobj, aconn, apacket)
186   IN struct rx_securityClass *aobj;
187   IN struct rx_packet *apacket;
188   IN struct rx_connection *aconn;
189 {   struct rxkad_sconn *sconn;
190     char *challenge;
191     int challengeSize;
192     struct rxkad_v2Challenge  c_v2;   /* version 2 */
193     struct rxkad_oldChallenge c_old;  /* old style */
194
195     sconn = (struct rxkad_sconn *) aconn->securityData;
196     if (rx_IsUsingPktCksum(aconn)) sconn->cksumSeen = 1;
197
198     if (sconn->cksumSeen) {
199         memset(&c_v2, 0, sizeof(c_v2));
200         c_v2.version = htonl(RXKAD_CHALLENGE_PROTOCOL_VERSION);
201         c_v2.challengeID = htonl(sconn->challengeID);
202         c_v2.level = htonl((afs_int32)sconn->level);
203         c_v2.spare = 0;
204         challenge = (char *)&c_v2;
205         challengeSize = sizeof(c_v2);
206     } else {
207         memset(&c_old, 0, sizeof(c_old));
208         c_old.challengeID = htonl(sconn->challengeID);
209         c_old.level = htonl((afs_int32)sconn->level);
210         challenge = (char *)&c_old;
211         challengeSize = sizeof(c_old);
212     }
213     if (rx_MyMaxSendSize < challengeSize)
214         return RXKADPACKETSHORT;        /* not enough space */
215
216     rx_packetwrite(apacket, 0, challengeSize, challenge);
217     rx_SetDataSize (apacket, challengeSize);
218     sconn->tried = 1;
219     LOCK_RXKAD_STATS
220     rxkad_stats.challengesSent++;
221     UNLOCK_RXKAD_STATS
222     return 0;
223 }
224
225 /* server: process a response to a challenge packet */
226 /* XXX this does some copying of data in and out of the packet, but I'll bet it
227  * could just do it in place, especially if I used rx_Pullup...
228  */
229 rxs_return_t rxkad_CheckResponse (aobj, aconn, apacket)
230   struct rx_securityClass *aobj;
231   struct rx_packet *apacket;
232   struct rx_connection *aconn;
233 {   struct rxkad_sconn *sconn;
234     struct rxkad_sprivate *tsp;
235     struct ktc_encryptionKey serverKey;
236     struct rxkad_oldChallengeResponse oldr; /* response format */
237     struct rxkad_v2ChallengeResponse v2r;
238     afs_int32  tlen;                            /* ticket len */
239     afs_int32  kvno;                            /* key version of ticket */
240     char  tix[MAXKTCTICKETLEN];
241     afs_int32  incChallengeID;
242     rxkad_level level;
243     int   code;
244     /* ticket contents */
245     struct ktc_principal client;
246     struct ktc_encryptionKey sessionkey;
247     afs_int32     host;
248     afs_uint32 start;
249     afs_uint32 end;
250     unsigned int pos;
251     struct rxkad_serverinfo *rock;
252
253     sconn = (struct rxkad_sconn *) aconn->securityData;
254     tsp = (struct rxkad_sprivate *) aobj->privateData;
255
256     if (sconn->cksumSeen) {
257         /* expect v2 response, leave fields in v2r in network order for cksum
258          * computation which follows decryption. */
259         if (rx_GetDataSize(apacket) < sizeof(v2r)) 
260           return RXKADPACKETSHORT;
261         rx_packetread(apacket, 0, sizeof(v2r), &v2r);
262         pos = sizeof(v2r);
263         /* version == 2 */
264         /* ignore spare */
265         kvno = ntohl (v2r.kvno);
266         tlen = ntohl(v2r.ticketLen);
267         if (rx_GetDataSize(apacket) < sizeof(v2r) + tlen)
268             return RXKADPACKETSHORT;
269     } else {
270         /* expect old format response */
271         if (rx_GetDataSize(apacket) < sizeof(oldr)) return RXKADPACKETSHORT;
272         rx_packetread(apacket, 0, sizeof(oldr), &oldr);
273         pos = sizeof(oldr);
274
275         kvno = ntohl (oldr.kvno);
276         tlen = ntohl(oldr.ticketLen);
277         if (rx_GetDataSize(apacket) != sizeof(oldr) + tlen)
278             return RXKADPACKETSHORT;
279     }
280     if ((tlen < MINKTCTICKETLEN) || (tlen > MAXKTCTICKETLEN))
281         return RXKADTICKETLEN;
282
283     rx_packetread(apacket, pos, tlen, tix);     /* get ticket */
284
285     /*
286      * We allow the ticket to be optionally decoded by an alternate
287      * ticket decoder, if the function variable
288      * rxkad_AlternateTicketDecoder is set. That function should
289      * return a code of -1 if it wants the ticket to be decoded by
290      * the standard decoder.
291      */
292     if (rxkad_AlternateTicketDecoder) {
293         code = rxkad_AlternateTicketDecoder
294             (kvno, tix, tlen, client.name, client.instance, client.cell,
295              &sessionkey, &host, &start, &end);
296         if (code && code != -1) {
297             return code;
298         }
299     } else {
300         code = -1;    /* No alternate ticket decoder present */
301     }
302
303     /*
304      * If the alternate decoder is not present, or returns -1, then
305      * assume the ticket is of the default style.
306      */
307     if (code == -1) {
308         /* get ticket's key */
309         code = (*tsp->get_key)(tsp->get_key_rock, kvno, &serverKey);
310         if (code) return RXKADUNKNOWNKEY;       /* invalid kvno */
311         code = tkt_DecodeTicket (tix, tlen, &serverKey,
312                                  client.name, client.instance, client.cell,
313                                  &sessionkey, &host, &start, &end);
314         if (code) return RXKADBADTICKET;
315     }
316     code = tkt_CheckTimes (start, end, time(0));
317     if (code == -1) return RXKADEXPIRED;
318     else if (code <= 0) return RXKADNOAUTH;
319
320     code = fc_keysched (&sessionkey, sconn->keysched);
321     if (code) return RXKADBADKEY;
322     memcpy(sconn->ivec, &sessionkey, sizeof(sconn->ivec));
323
324     if (sconn->cksumSeen) {
325         /* using v2 response */
326         afs_uint32 cksum;                       /* observed cksum */
327         struct rxkad_endpoint endpoint; /* connections endpoint */
328         int i;
329         afs_uint32 xor[2];
330
331         memcpy(xor, sconn->ivec, 2*sizeof(afs_int32));
332         fc_cbc_encrypt (&v2r.encrypted, &v2r.encrypted, sizeof(v2r.encrypted),
333                         sconn->keysched, xor, DECRYPT);
334         cksum = rxkad_CksumChallengeResponse (&v2r);
335         if (cksum != v2r.encrypted.endpoint.cksum)
336             return RXKADSEALEDINCON;
337         (void) rxkad_SetupEndpoint (aconn, &endpoint);
338         v2r.encrypted.endpoint.cksum = 0;
339         if (memcmp (&endpoint, &v2r.encrypted.endpoint, sizeof(endpoint)) != 0)
340             return RXKADSEALEDINCON;
341         for (i=0; i<RX_MAXCALLS; i++) {
342             v2r.encrypted.callNumbers[i] = ntohl(v2r.encrypted.callNumbers[i]);
343             if (v2r.encrypted.callNumbers[i] < 0) return RXKADSEALEDINCON;
344         }
345
346         (void) rxi_SetCallNumberVector (aconn, v2r.encrypted.callNumbers);
347         incChallengeID = ntohl(v2r.encrypted.incChallengeID);
348         level = ntohl(v2r.encrypted.level);
349     } else {
350         /* expect old format response */
351         fc_ecb_encrypt(&oldr.encrypted, &oldr.encrypted,
352                        sconn->keysched, DECRYPT);
353         incChallengeID = ntohl(oldr.encrypted.incChallengeID);
354         level = ntohl(oldr.encrypted.level);
355     }
356     if (incChallengeID != sconn->challengeID+1)
357         return RXKADOUTOFSEQUENCE;      /* replay attempt */
358     if ((level < sconn->level) || (level > rxkad_crypt)) return RXKADLEVELFAIL;
359     sconn->level = level;
360     rxkad_SetLevel (aconn, sconn->level);
361     LOCK_RXKAD_STATS
362     rxkad_stats.responses[rxkad_LevelIndex(sconn->level)]++;
363     UNLOCK_RXKAD_STATS
364
365     /* now compute endpoint-specific info used for computing 16 bit checksum */
366     rxkad_DeriveXORInfo(aconn, sconn->keysched, sconn->ivec, sconn->preSeq);
367
368     /* otherwise things are ok */
369     sconn->expirationTime = end;
370     sconn->authenticated = 1;
371
372     if (tsp->user_ok) {
373         code = tsp->user_ok (client.name, client.instance, client.cell, kvno);
374         if (code) return RXKADNOAUTH;
375     }
376     else {                              /* save the info for later retreival */
377         int size = sizeof(struct rxkad_serverinfo);
378         rock = (struct rxkad_serverinfo *) osi_Alloc (size);
379         memset(rock, 0, size);
380         rock->kvno = kvno;
381         memcpy(&rock->client, &client, sizeof(rock->client));
382         sconn->rock = rock;
383     }
384     return 0;
385 }
386
387 /* return useful authentication info about a server-side connection */
388
389 afs_int32 rxkad_GetServerInfo (aconn, level, expiration,
390                                  name, instance, cell, kvno)
391   struct rx_connection *aconn;
392   rxkad_level    *level;
393   afs_uint32  *expiration;
394   char           *name;
395   char           *instance;
396   char           *cell;
397   afs_int32              *kvno;
398 {
399     struct rxkad_sconn *sconn;
400
401     sconn = (struct rxkad_sconn *) aconn->securityData;
402     if (sconn && sconn->authenticated && sconn->rock &&
403         (time(0) < sconn->expirationTime)) {
404         if (level)      *level = sconn->level;
405         if (expiration) *expiration = sconn->expirationTime;
406         if (name)       strcpy (name, sconn->rock->client.name);
407         if (instance)   strcpy (instance, sconn->rock->client.instance);
408         if (cell)       strcpy (cell, sconn->rock->client.cell);
409         if (kvno)       *kvno = sconn->rock->kvno;
410         return 0;
411     }
412     else return RXKADNOAUTH;
413 }