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