2 * Copyright 2000, International Business Machines Corporation and others.
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
10 /* The rxkad security object. Authentication using a DES-encrypted
11 * Kerberos-style ticket. These are the server-only routines. */
14 #include <afsconfig.h>
15 #include <afs/param.h>
20 #if (defined(AFS_AIX_ENV) && defined(KERNEL) && !defined(UKERNEL)) || defined(AFS_AUX_ENV) || defined(AFS_SUN5_ENV)
21 #include <sys/systm.h>
26 #include <afs/afsutil.h>
29 #include "private_data.h"
30 #define XPRT_RXKAD_SERVER
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.
37 afs_int32(*rxkad_AlternateTicketDecoder) (afs_int32, char *, afs_int32,
38 char *, char *, char *,
39 struct ktc_encryptionKey *,
40 afs_int32 *, afs_uint32 *,
43 static struct rx_securityOps rxkad_server_ops = {
46 rxkad_PreparePacket, /* once per packet creation */
47 0, /* send packet (once per retrans) */
48 rxkad_CheckAuthentication,
49 rxkad_CreateChallenge,
53 rxkad_CheckPacket, /* check data packet */
54 rxkad_DestroyConnection,
56 rxkad_SetConfiguration,
60 extern afs_uint32 rx_MyMaxSendSize;
62 /* Miscellaneous random number routines that use the fcrypt module and the
65 static fc_KeySchedule random_int32_schedule;
67 #ifdef AFS_PTHREAD_ENV
69 * This mutex protects the following global variables:
70 * random_int32_schedule
74 pthread_mutex_t rxkad_random_mutex
75 #ifdef PTHREAD_MUTEX_INITIALIZER
76 = PTHREAD_MUTEX_INITIALIZER
79 #define LOCK_RM osi_Assert(pthread_mutex_lock(&rxkad_random_mutex)==0)
80 #define UNLOCK_RM osi_Assert(pthread_mutex_unlock(&rxkad_random_mutex)==0)
84 #endif /* AFS_PTHREAD_ENV */
87 init_random_int32(void)
91 gettimeofday(&key, NULL);
93 fc_keysched((struct ktc_encryptionKey*)&key, random_int32_schedule);
98 get_random_int32(void)
100 static struct timeval seed;
104 fc_ecb_encrypt(&seed, &seed, random_int32_schedule, ENCRYPT);
110 /* Called with four parameters. The first is the level of encryption, as
111 defined in the rxkad.h file. The second and third are a rock and a
112 procedure that is called with the key version number that accompanies the
113 ticket and returns a pointer to the server's decryption key. The fourth
114 argument, if not NULL, is a pointer to a function that will be called for
115 every new connection with the name, instance and cell of the client. The
116 routine should return zero if the user is NOT acceptible to the server. If
117 this routine is not supplied, the server can call rxkad_GetServerInfo with
118 the rx connection pointer passed to the RPC routine to obtain information
122 rxkad_level level; * minimum level *
123 char *get_key_rock; * rock for get_key implementor *
124 int (*get_key)(); * passed kvno & addr(key) to fill *
125 int (*user_ok)(); * passed name, inst, cell => bool *
128 struct rx_securityClass *
129 rxkad_NewServerSecurityObject(rxkad_level level, void *get_key_rock,
130 int (*get_key) (void *get_key_rock, int kvno,
131 struct ktc_encryptionKey *
133 int (*user_ok) (char *name, char *instance,
134 char *cell, afs_int32 kvno))
136 struct rx_securityClass *tsc;
137 struct rxkad_sprivate *tsp;
145 size = sizeof(struct rx_securityClass);
146 tsc = (struct rx_securityClass *)osi_Alloc(size);
147 memset(tsc, 0, size);
148 tsc->refCount = 1; /* caller has one reference */
149 tsc->ops = &rxkad_server_ops;
150 size = sizeof(struct rxkad_sprivate);
151 tsp = (struct rxkad_sprivate *)osi_Alloc(size);
152 memset(tsp, 0, size);
153 tsc->privateData = (char *)tsp;
155 tsp->type |= rxkad_server; /* so can identify later */
156 tsp->level = level; /* level of encryption */
157 tsp->get_key_rock = get_key_rock;
158 tsp->get_key = get_key; /* to get server ticket */
159 tsp->user_ok = user_ok; /* to inform server of client id. */
162 INC_RXKAD_STATS(serverObjects);
166 /* server: called to tell if a connection authenticated properly */
169 rxkad_CheckAuthentication(struct rx_securityClass *aobj,
170 struct rx_connection *aconn)
172 struct rxkad_sconn *sconn;
174 /* first make sure the object exists */
175 if (!aconn->securityData)
176 return RXKADINCONSISTENCY;
178 sconn = (struct rxkad_sconn *)aconn->securityData;
179 return !sconn->authenticated;
182 /* server: put the current challenge in the connection structure for later use
186 rxkad_CreateChallenge(struct rx_securityClass *aobj,
187 struct rx_connection *aconn)
189 struct rxkad_sconn *sconn;
190 struct rxkad_sprivate *tsp;
192 sconn = (struct rxkad_sconn *)aconn->securityData;
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;
201 /* server: fill in a challenge in the packet */
204 rxkad_GetChallenge(struct rx_securityClass *aobj, struct rx_connection *aconn,
205 struct rx_packet *apacket)
207 struct rxkad_sconn *sconn;
210 struct rxkad_v2Challenge c_v2; /* version 2 */
211 struct rxkad_oldChallenge c_old; /* old style */
213 sconn = (struct rxkad_sconn *)aconn->securityData;
214 if (rx_IsUsingPktCksum(aconn))
215 sconn->cksumSeen = 1;
217 if (sconn->cksumSeen) {
218 memset(&c_v2, 0, sizeof(c_v2));
219 c_v2.version = htonl(RXKAD_CHALLENGE_PROTOCOL_VERSION);
220 c_v2.challengeID = htonl(sconn->challengeID);
221 c_v2.level = htonl((afs_int32) sconn->level);
223 challenge = (char *)&c_v2;
224 challengeSize = sizeof(c_v2);
226 memset(&c_old, 0, sizeof(c_old));
227 c_old.challengeID = htonl(sconn->challengeID);
228 c_old.level = htonl((afs_int32) sconn->level);
229 challenge = (char *)&c_old;
230 challengeSize = sizeof(c_old);
232 if (rx_MyMaxSendSize < challengeSize)
233 return RXKADPACKETSHORT; /* not enough space */
235 rx_packetwrite(apacket, 0, challengeSize, challenge);
236 rx_SetDataSize(apacket, challengeSize);
238 INC_RXKAD_STATS(challengesSent);
242 /* server: process a response to a challenge packet */
243 /* XXX this does some copying of data in and out of the packet, but I'll bet it
244 * could just do it in place, especially if I used rx_Pullup...
247 rxkad_CheckResponse(struct rx_securityClass *aobj,
248 struct rx_connection *aconn, struct rx_packet *apacket)
250 struct rxkad_sconn *sconn;
251 struct rxkad_sprivate *tsp;
252 struct ktc_encryptionKey serverKey;
253 struct rxkad_oldChallengeResponse oldr; /* response format */
254 struct rxkad_v2ChallengeResponse v2r;
255 afs_int32 tlen; /* ticket len */
256 afs_int32 kvno; /* key version of ticket */
257 char tix[MAXKTCTICKETLEN];
258 afs_int32 incChallengeID;
261 /* ticket contents */
262 struct ktc_principal client;
263 struct ktc_encryptionKey sessionkey;
268 struct rxkad_serverinfo *rock;
270 sconn = (struct rxkad_sconn *)aconn->securityData;
271 tsp = (struct rxkad_sprivate *)aobj->privateData;
273 if (sconn->cksumSeen) {
274 /* expect v2 response, leave fields in v2r in network order for cksum
275 * computation which follows decryption. */
276 if (rx_GetDataSize(apacket) < sizeof(v2r))
277 return RXKADPACKETSHORT;
278 rx_packetread(apacket, 0, sizeof(v2r), &v2r);
282 kvno = ntohl(v2r.kvno);
283 tlen = ntohl(v2r.ticketLen);
284 if (rx_GetDataSize(apacket) < sizeof(v2r) + tlen)
285 return RXKADPACKETSHORT;
287 /* expect old format response */
288 if (rx_GetDataSize(apacket) < sizeof(oldr))
289 return RXKADPACKETSHORT;
290 rx_packetread(apacket, 0, sizeof(oldr), &oldr);
293 kvno = ntohl(oldr.kvno);
294 tlen = ntohl(oldr.ticketLen);
295 if (rx_GetDataSize(apacket) != sizeof(oldr) + tlen)
296 return RXKADPACKETSHORT;
298 if ((tlen < MINKTCTICKETLEN) || (tlen > MAXKTCTICKETLEN))
299 return RXKADTICKETLEN;
301 rx_packetread(apacket, pos, tlen, tix); /* get ticket */
304 * We allow the ticket to be optionally decoded by an alternate
305 * ticket decoder, if the function variable
306 * rxkad_AlternateTicketDecoder is set. That function should
307 * return a code of -1 if it wants the ticket to be decoded by
308 * the standard decoder.
310 if (rxkad_AlternateTicketDecoder) {
312 rxkad_AlternateTicketDecoder(kvno, tix, tlen, client.name,
313 client.instance, client.cell,
314 &sessionkey, &host, &start, &end);
315 if (code && code != -1) {
319 code = -1; /* No alternate ticket decoder present */
323 * If the alternate decoder is not present, or returns -1, then
324 * assume the ticket is of the default style.
326 if (code == -1 && ((kvno == RXKAD_TKT_TYPE_KERBEROS_V5)
327 || (kvno == RXKAD_TKT_TYPE_KERBEROS_V5_ENCPART_ONLY))) {
329 tkt_DecodeTicket5(tix, tlen, tsp->get_key, tsp->get_key_rock,
330 kvno, client.name, client.instance, client.cell,
331 &sessionkey, &host, &start, &end,
332 tsp->flags & RXS_CONFIG_FLAGS_DISABLE_DOTCHECK);
338 * If the alternate decoder/kerberos 5 decoder is not present, or
339 * returns -1, then assume the ticket is of the default style.
342 /* get ticket's key */
343 code = (*tsp->get_key) (tsp->get_key_rock, kvno, &serverKey);
345 return RXKADUNKNOWNKEY; /* invalid kvno */
347 tkt_DecodeTicket(tix, tlen, &serverKey, client.name,
348 client.instance, client.cell, &sessionkey, &host,
353 code = tkt_CheckTimes(start, end, time(0));
359 return RXKADBADTICKET;
361 code = fc_keysched(&sessionkey, sconn->keysched);
364 memcpy(sconn->ivec, &sessionkey, sizeof(sconn->ivec));
366 if (sconn->cksumSeen) {
367 /* using v2 response */
368 afs_uint32 cksum; /* observed cksum */
369 struct rxkad_endpoint endpoint; /* connections endpoint */
373 memcpy(xor, sconn->ivec, 2 * sizeof(afs_int32));
374 fc_cbc_encrypt(&v2r.encrypted, &v2r.encrypted, sizeof(v2r.encrypted),
375 sconn->keysched, xor, DECRYPT);
376 cksum = rxkad_CksumChallengeResponse(&v2r);
377 if (cksum != v2r.encrypted.endpoint.cksum)
378 return RXKADSEALEDINCON;
379 (void)rxkad_SetupEndpoint(aconn, &endpoint);
380 v2r.encrypted.endpoint.cksum = 0;
381 if (memcmp(&endpoint, &v2r.encrypted.endpoint, sizeof(endpoint)) != 0)
382 return RXKADSEALEDINCON;
383 for (i = 0; i < RX_MAXCALLS; i++) {
384 v2r.encrypted.callNumbers[i] =
385 ntohl(v2r.encrypted.callNumbers[i]);
386 if (v2r.encrypted.callNumbers[i] < 0)
387 return RXKADSEALEDINCON;
390 (void)rxi_SetCallNumberVector(aconn, v2r.encrypted.callNumbers);
391 incChallengeID = ntohl(v2r.encrypted.incChallengeID);
392 level = ntohl(v2r.encrypted.level);
394 /* expect old format response */
395 fc_ecb_encrypt(&oldr.encrypted, &oldr.encrypted, sconn->keysched,
397 incChallengeID = ntohl(oldr.encrypted.incChallengeID);
398 level = ntohl(oldr.encrypted.level);
400 if (incChallengeID != sconn->challengeID + 1)
401 return RXKADOUTOFSEQUENCE; /* replay attempt */
402 if ((level < sconn->level) || (level > rxkad_crypt))
403 return RXKADLEVELFAIL;
404 sconn->level = level;
405 rxkad_SetLevel(aconn, sconn->level);
406 INC_RXKAD_STATS(responses[rxkad_LevelIndex(sconn->level)]);
407 /* now compute endpoint-specific info used for computing 16 bit checksum */
408 rxkad_DeriveXORInfo(aconn, &sconn->keysched, (char *)sconn->ivec, (char *)sconn->preSeq);
410 /* otherwise things are ok */
411 sconn->expirationTime = end;
412 sconn->authenticated = 1;
415 code = tsp->user_ok(client.name, client.instance, client.cell, kvno);
418 } else { /* save the info for later retreival */
419 int size = sizeof(struct rxkad_serverinfo);
420 rock = (struct rxkad_serverinfo *)osi_Alloc(size);
421 memset(rock, 0, size);
423 memcpy(&rock->client, &client, sizeof(rock->client));
429 /* return useful authentication info about a server-side connection */
432 rxkad_GetServerInfo(struct rx_connection * aconn, rxkad_level * level,
433 afs_uint32 * expiration, char *name, char *instance,
434 char *cell, afs_int32 * kvno)
436 struct rxkad_sconn *sconn;
438 sconn = (struct rxkad_sconn *)aconn->securityData;
439 if (sconn && sconn->authenticated && sconn->rock
440 && (time(0) < sconn->expirationTime)) {
442 *level = sconn->level;
444 *expiration = sconn->expirationTime;
446 strcpy(name, sconn->rock->client.name);
448 strcpy(instance, sconn->rock->client.instance);
450 strcpy(cell, sconn->rock->client.cell);
452 *kvno = sconn->rock->kvno;
458 /* Set security object configuration variables */
459 afs_int32 rxkad_SetConfiguration(struct rx_securityClass *aobj,
460 struct rx_connection *aconn,
461 rx_securityConfigVariables atype,
462 void * avalue, void **currentValue)
464 struct rxkad_sprivate *private =
465 (struct rxkad_sprivate *) aobj->privateData;
468 case RXS_CONFIG_FLAGS:
470 *((afs_uint32 *)currentValue) = private->flags;
472 private->flags = (intptr_t)avalue;