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