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