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