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>
19 #include <sys/types.h>
20 #if (defined(AFS_AIX_ENV) && defined(KERNEL) && !defined(UKERNEL)) || defined(AFS_AUX_ENV) || defined(AFS_SUN5_ENV)
21 #include <sys/systm.h>
27 #include <netinet/in.h>
33 #include <afs/afsutil.h>
34 #include <des/stats.h>
38 #include "private_data.h"
39 #define XPRT_RXKAD_SERVER
42 * This can be set to allow alternate ticket decoding.
43 * Currently only used by the AFS/DFS protocol translator to recognize
44 * Kerberos V5 tickets. The actual code to do that is provided externally.
46 afs_int32(*rxkad_AlternateTicketDecoder) (afs_int32, char *, afs_int32,
47 char *, char *, char *,
48 struct ktc_encryptionKey *,
49 afs_int32 *, afs_uint32 *,
52 static struct rx_securityOps rxkad_server_ops = {
55 rxkad_PreparePacket, /* once per packet creation */
56 0, /* send packet (once per retrans) */
57 rxkad_CheckAuthentication,
58 rxkad_CreateChallenge,
62 rxkad_CheckPacket, /* check data packet */
63 rxkad_DestroyConnection,
65 rxkad_SetConfiguration,
69 extern afs_uint32 rx_MyMaxSendSize;
71 /* Miscellaneous random number routines that use the fcrypt module and the
74 static fc_KeySchedule random_int32_schedule;
76 #ifdef AFS_PTHREAD_ENV
78 * This mutex protects the following global variables:
79 * random_int32_schedule
84 pthread_mutex_t rxkad_random_mutex
85 #ifdef PTHREAD_MUTEX_INITIALIZER
86 = PTHREAD_MUTEX_INITIALIZER
89 #define LOCK_RM assert(pthread_mutex_lock(&rxkad_random_mutex)==0)
90 #define UNLOCK_RM assert(pthread_mutex_unlock(&rxkad_random_mutex)==0)
94 #endif /* AFS_PTHREAD_ENV */
97 init_random_int32(void)
101 gettimeofday(&key, NULL);
103 fc_keysched((struct ktc_encryptionKey*)&key, random_int32_schedule);
108 get_random_int32(void)
110 static struct timeval seed;
114 fc_ecb_encrypt(&seed, &seed, random_int32_schedule, ENCRYPT);
120 /* Called with four parameters. The first is the level of encryption, as
121 defined in the rxkad.h file. The second and third are a rock and a
122 procedure that is called with the key version number that accompanies the
123 ticket and returns a pointer to the server's decryption key. The fourth
124 argument, if not NULL, is a pointer to a function that will be called for
125 every new connection with the name, instance and cell of the client. The
126 routine should return zero if the user is NOT acceptible to the server. If
127 this routine is not supplied, the server can call rxkad_GetServerInfo with
128 the rx connection pointer passed to the RPC routine to obtain information
132 rxkad_level level; * minimum level *
133 char *get_key_rock; * rock for get_key implementor *
134 int (*get_key)(); * passed kvno & addr(key) to fill *
135 int (*user_ok)(); * passed name, inst, cell => bool *
138 struct rx_securityClass *
139 rxkad_NewServerSecurityObject(rxkad_level level, void *get_key_rock,
140 int (*get_key) (void *get_key_rock, int kvno,
141 struct ktc_encryptionKey *
143 int (*user_ok) (char *name, char *instance,
144 char *cell, afs_int32 kvno))
146 struct rx_securityClass *tsc;
147 struct rxkad_sprivate *tsp;
153 size = sizeof(struct rx_securityClass);
154 tsc = (struct rx_securityClass *)osi_Alloc(size);
155 memset(tsc, 0, size);
156 tsc->refCount = 1; /* caller has one reference */
157 tsc->ops = &rxkad_server_ops;
158 size = sizeof(struct rxkad_sprivate);
159 tsp = (struct rxkad_sprivate *)osi_Alloc(size);
160 memset(tsp, 0, size);
161 tsc->privateData = (char *)tsp;
163 tsp->type |= rxkad_server; /* so can identify later */
164 tsp->level = level; /* level of encryption */
165 tsp->get_key_rock = get_key_rock;
166 tsp->get_key = get_key; /* to get server ticket */
167 tsp->user_ok = user_ok; /* to inform server of client id. */
170 INC_RXKAD_STATS(serverObjects);
174 /* server: called to tell if a connection authenticated properly */
177 rxkad_CheckAuthentication(struct rx_securityClass *aobj,
178 struct rx_connection *aconn)
180 struct rxkad_sconn *sconn;
182 /* first make sure the object exists */
183 if (!aconn->securityData)
184 return RXKADINCONSISTENCY;
186 sconn = (struct rxkad_sconn *)aconn->securityData;
187 return !sconn->authenticated;
190 /* server: put the current challenge in the connection structure for later use
194 rxkad_CreateChallenge(struct rx_securityClass *aobj,
195 struct rx_connection *aconn)
197 struct rxkad_sconn *sconn;
198 struct rxkad_sprivate *tsp;
200 sconn = (struct rxkad_sconn *)aconn->securityData;
201 sconn->challengeID = get_random_int32();
202 sconn->authenticated = 0; /* conn unauth. 'til we hear back */
203 /* initialize level from object's minimum acceptable level */
204 tsp = (struct rxkad_sprivate *)aobj->privateData;
205 sconn->level = tsp->level;
209 /* server: fill in a challenge in the packet */
212 rxkad_GetChallenge(struct rx_securityClass *aobj, struct rx_connection *aconn,
213 struct rx_packet *apacket)
215 struct rxkad_sconn *sconn;
218 struct rxkad_v2Challenge c_v2; /* version 2 */
219 struct rxkad_oldChallenge c_old; /* old style */
221 sconn = (struct rxkad_sconn *)aconn->securityData;
222 if (rx_IsUsingPktCksum(aconn))
223 sconn->cksumSeen = 1;
225 if (sconn->cksumSeen) {
226 memset(&c_v2, 0, sizeof(c_v2));
227 c_v2.version = htonl(RXKAD_CHALLENGE_PROTOCOL_VERSION);
228 c_v2.challengeID = htonl(sconn->challengeID);
229 c_v2.level = htonl((afs_int32) sconn->level);
231 challenge = (char *)&c_v2;
232 challengeSize = sizeof(c_v2);
234 memset(&c_old, 0, sizeof(c_old));
235 c_old.challengeID = htonl(sconn->challengeID);
236 c_old.level = htonl((afs_int32) sconn->level);
237 challenge = (char *)&c_old;
238 challengeSize = sizeof(c_old);
240 if (rx_MyMaxSendSize < challengeSize)
241 return RXKADPACKETSHORT; /* not enough space */
243 rx_packetwrite(apacket, 0, challengeSize, challenge);
244 rx_SetDataSize(apacket, challengeSize);
246 INC_RXKAD_STATS(challengesSent);
250 /* server: process a response to a challenge packet */
251 /* XXX this does some copying of data in and out of the packet, but I'll bet it
252 * could just do it in place, especially if I used rx_Pullup...
255 rxkad_CheckResponse(struct rx_securityClass *aobj,
256 struct rx_connection *aconn, struct rx_packet *apacket)
258 struct rxkad_sconn *sconn;
259 struct rxkad_sprivate *tsp;
260 struct ktc_encryptionKey serverKey;
261 struct rxkad_oldChallengeResponse oldr; /* response format */
262 struct rxkad_v2ChallengeResponse v2r;
263 afs_int32 tlen; /* ticket len */
264 afs_int32 kvno; /* key version of ticket */
265 char tix[MAXKTCTICKETLEN];
266 afs_int32 incChallengeID;
269 /* ticket contents */
270 struct ktc_principal client;
271 struct ktc_encryptionKey sessionkey;
276 struct rxkad_serverinfo *rock;
278 sconn = (struct rxkad_sconn *)aconn->securityData;
279 tsp = (struct rxkad_sprivate *)aobj->privateData;
281 if (sconn->cksumSeen) {
282 /* expect v2 response, leave fields in v2r in network order for cksum
283 * computation which follows decryption. */
284 if (rx_GetDataSize(apacket) < sizeof(v2r))
285 return RXKADPACKETSHORT;
286 rx_packetread(apacket, 0, sizeof(v2r), &v2r);
290 kvno = ntohl(v2r.kvno);
291 tlen = ntohl(v2r.ticketLen);
292 if (rx_GetDataSize(apacket) < sizeof(v2r) + tlen)
293 return RXKADPACKETSHORT;
295 /* expect old format response */
296 if (rx_GetDataSize(apacket) < sizeof(oldr))
297 return RXKADPACKETSHORT;
298 rx_packetread(apacket, 0, sizeof(oldr), &oldr);
301 kvno = ntohl(oldr.kvno);
302 tlen = ntohl(oldr.ticketLen);
303 if (rx_GetDataSize(apacket) != sizeof(oldr) + tlen)
304 return RXKADPACKETSHORT;
306 if ((tlen < MINKTCTICKETLEN) || (tlen > MAXKTCTICKETLEN))
307 return RXKADTICKETLEN;
309 rx_packetread(apacket, pos, tlen, tix); /* get ticket */
312 * We allow the ticket to be optionally decoded by an alternate
313 * ticket decoder, if the function variable
314 * rxkad_AlternateTicketDecoder is set. That function should
315 * return a code of -1 if it wants the ticket to be decoded by
316 * the standard decoder.
318 if (rxkad_AlternateTicketDecoder) {
320 rxkad_AlternateTicketDecoder(kvno, tix, tlen, client.name,
321 client.instance, client.cell,
322 &sessionkey, &host, &start, &end);
323 if (code && code != -1) {
327 code = -1; /* No alternate ticket decoder present */
331 * If the alternate decoder is not present, or returns -1, then
332 * assume the ticket is of the default style.
334 if (code == -1 && ((kvno == RXKAD_TKT_TYPE_KERBEROS_V5)
335 || (kvno == RXKAD_TKT_TYPE_KERBEROS_V5_ENCPART_ONLY))) {
337 tkt_DecodeTicket5(tix, tlen, tsp->get_key, tsp->get_key_rock,
338 kvno, client.name, client.instance, client.cell,
339 &sessionkey, &host, &start, &end,
340 tsp->flags & RXS_CONFIG_FLAGS_DISABLE_DOTCHECK);
346 * If the alternate decoder/kerberos 5 decoder is not present, or
347 * returns -1, then assume the ticket is of the default style.
350 /* get ticket's key */
351 code = (*tsp->get_key) (tsp->get_key_rock, kvno, &serverKey);
353 return RXKADUNKNOWNKEY; /* invalid kvno */
355 tkt_DecodeTicket(tix, tlen, &serverKey, client.name,
356 client.instance, client.cell, &sessionkey, &host,
361 code = tkt_CheckTimes(start, end, time(0));
367 return RXKADBADTICKET;
369 code = fc_keysched(&sessionkey, sconn->keysched);
372 memcpy(sconn->ivec, &sessionkey, sizeof(sconn->ivec));
374 if (sconn->cksumSeen) {
375 /* using v2 response */
376 afs_uint32 cksum; /* observed cksum */
377 struct rxkad_endpoint endpoint; /* connections endpoint */
381 memcpy(xor, sconn->ivec, 2 * sizeof(afs_int32));
382 fc_cbc_encrypt(&v2r.encrypted, &v2r.encrypted, sizeof(v2r.encrypted),
383 sconn->keysched, xor, DECRYPT);
384 cksum = rxkad_CksumChallengeResponse(&v2r);
385 if (cksum != v2r.encrypted.endpoint.cksum)
386 return RXKADSEALEDINCON;
387 (void)rxkad_SetupEndpoint(aconn, &endpoint);
388 v2r.encrypted.endpoint.cksum = 0;
389 if (memcmp(&endpoint, &v2r.encrypted.endpoint, sizeof(endpoint)) != 0)
390 return RXKADSEALEDINCON;
391 for (i = 0; i < RX_MAXCALLS; i++) {
392 v2r.encrypted.callNumbers[i] =
393 ntohl(v2r.encrypted.callNumbers[i]);
394 if (v2r.encrypted.callNumbers[i] < 0)
395 return RXKADSEALEDINCON;
398 (void)rxi_SetCallNumberVector(aconn, v2r.encrypted.callNumbers);
399 incChallengeID = ntohl(v2r.encrypted.incChallengeID);
400 level = ntohl(v2r.encrypted.level);
402 /* expect old format response */
403 fc_ecb_encrypt(&oldr.encrypted, &oldr.encrypted, sconn->keysched,
405 incChallengeID = ntohl(oldr.encrypted.incChallengeID);
406 level = ntohl(oldr.encrypted.level);
408 if (incChallengeID != sconn->challengeID + 1)
409 return RXKADOUTOFSEQUENCE; /* replay attempt */
410 if ((level < sconn->level) || (level > rxkad_crypt))
411 return RXKADLEVELFAIL;
412 sconn->level = level;
413 rxkad_SetLevel(aconn, sconn->level);
414 INC_RXKAD_STATS(responses[rxkad_LevelIndex(sconn->level)]);
415 /* now compute endpoint-specific info used for computing 16 bit checksum */
416 rxkad_DeriveXORInfo(aconn, &sconn->keysched, (char *)sconn->ivec, (char *)sconn->preSeq);
418 /* otherwise things are ok */
419 sconn->expirationTime = end;
420 sconn->authenticated = 1;
423 code = tsp->user_ok(client.name, client.instance, client.cell, kvno);
426 } else { /* save the info for later retreival */
427 int size = sizeof(struct rxkad_serverinfo);
428 rock = (struct rxkad_serverinfo *)osi_Alloc(size);
429 memset(rock, 0, size);
431 memcpy(&rock->client, &client, sizeof(rock->client));
437 /* return useful authentication info about a server-side connection */
440 rxkad_GetServerInfo(struct rx_connection * aconn, rxkad_level * level,
441 afs_uint32 * expiration, char *name, char *instance,
442 char *cell, afs_int32 * kvno)
444 struct rxkad_sconn *sconn;
446 sconn = (struct rxkad_sconn *)aconn->securityData;
447 if (sconn && sconn->authenticated && sconn->rock
448 && (time(0) < sconn->expirationTime)) {
450 *level = sconn->level;
452 *expiration = sconn->expirationTime;
454 strcpy(name, sconn->rock->client.name);
456 strcpy(instance, sconn->rock->client.instance);
458 strcpy(cell, sconn->rock->client.cell);
460 *kvno = sconn->rock->kvno;
466 /* Set security object configuration variables */
467 afs_int32 rxkad_SetConfiguration(struct rx_securityClass *aobj,
468 struct rx_connection *aconn,
469 rx_securityConfigVariables atype,
470 void * avalue, void **currentValue)
472 struct rxkad_sprivate *private =
473 (struct rxkad_sprivate *) aobj->privateData;
476 case RXS_CONFIG_FLAGS:
478 *((afs_uint32 *)currentValue) = private->flags;
480 private->flags = (intptr_t)avalue;