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