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>
21 #include <sys/types.h>
26 #include <netinet/in.h>
38 #include <afs/afsutil.h>
39 #include "private_data.h"
40 #define XPRT_RXKAD_SERVER
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.
47 afs_int32(*rxkad_AlternateTicketDecoder) ();
49 static struct rx_securityOps rxkad_server_ops = {
52 rxkad_PreparePacket, /* once per packet creation */
53 0, /* send packet (once per retrans) */
54 rxkad_CheckAuthentication,
55 rxkad_CreateChallenge,
59 rxkad_CheckPacket, /* check data packet */
60 rxkad_DestroyConnection,
63 extern afs_uint32 rx_MyMaxSendSize;
65 /* Miscellaneous random number routines that use the fcrypt module and the
68 static fc_KeySchedule random_int32_schedule;
70 #ifdef AFS_PTHREAD_ENV
72 * This mutex protects the following global variables:
73 * random_int32_schedule
78 pthread_mutex_t rxkad_random_mutex;
79 #define LOCK_RM assert(pthread_mutex_lock(&rxkad_random_mutex)==0);
80 #define UNLOCK_RM assert(pthread_mutex_unlock(&rxkad_random_mutex)==0);
84 #endif /* AFS_PTHREAD_ENV */
87 init_random_int32(void)
91 gettimeofday(&key, NULL);
92 LOCK_RM fc_keysched(&key, random_int32_schedule);
96 get_random_int32(void)
98 static struct timeval seed;
101 LOCK_RM fc_ecb_encrypt(&seed, &seed, random_int32_schedule, ENCRYPT);
106 /* Called with four parameters. The first is the level of encryption, as
107 defined in the rxkad.h file. The second and third are a rock and a
108 procedure that is called with the key version number that accompanies the
109 ticket and returns a pointer to the server's decryption key. The fourth
110 argument, if not NULL, is a pointer to a function that will be called for
111 every new connection with the name, instance and cell of the client. The
112 routine should return zero if the user is NOT acceptible to the server. If
113 this routine is not supplied, the server can call rxkad_GetServerInfo with
114 the rx connection pointer passed to the RPC routine to obtain information
118 rxkad_level level; * minimum level *
119 char *get_key_rock; * rock for get_key implementor *
120 int (*get_key)(); * passed kvno & addr(key) to fill *
121 int (*user_ok)(); * passed name, inst, cell => bool *
124 struct rx_securityClass *
125 rxkad_NewServerSecurityObject(rxkad_level level, char *get_key_rock,
126 int (*get_key) (char *get_key_rock, int kvno,
127 struct ktc_encryptionKey *
129 int (*user_ok) (char *name, char *instance,
130 char *cell, afs_int32 kvno))
132 struct rx_securityClass *tsc;
133 struct rxkad_sprivate *tsp;
139 size = sizeof(struct rx_securityClass);
140 tsc = (struct rx_securityClass *)osi_Alloc(size);
141 memset(tsc, 0, size);
142 tsc->refCount = 1; /* caller has one reference */
143 tsc->ops = &rxkad_server_ops;
144 size = sizeof(struct rxkad_sprivate);
145 tsp = (struct rxkad_sprivate *)osi_Alloc(size);
146 memset(tsp, 0, size);
147 tsc->privateData = (char *)tsp;
149 tsp->type |= rxkad_server; /* so can identify later */
150 tsp->level = level; /* level of encryption */
151 tsp->get_key_rock = get_key_rock;
152 tsp->get_key = get_key; /* to get server ticket */
153 tsp->user_ok = user_ok; /* to inform server of client id. */
156 LOCK_RXKAD_STATS rxkad_stats_serverObjects++;
157 UNLOCK_RXKAD_STATS return tsc;
160 /* server: called to tell if a connection authenticated properly */
163 rxkad_CheckAuthentication(struct rx_securityClass *aobj,
164 struct rx_connection *aconn)
166 struct rxkad_sconn *sconn;
168 /* first make sure the object exists */
169 if (!aconn->securityData)
170 return RXKADINCONSISTENCY;
172 sconn = (struct rxkad_sconn *)aconn->securityData;
173 return !sconn->authenticated;
176 /* server: put the current challenge in the connection structure for later use
180 rxkad_CreateChallenge(struct rx_securityClass *aobj,
181 struct rx_connection *aconn)
183 struct rxkad_sconn *sconn;
184 struct rxkad_sprivate *tsp;
186 sconn = (struct rxkad_sconn *)aconn->securityData;
187 sconn->challengeID = get_random_int32();
188 sconn->authenticated = 0; /* conn unauth. 'til we hear back */
189 /* initialize level from object's minimum acceptable level */
190 tsp = (struct rxkad_sprivate *)aobj->privateData;
191 sconn->level = tsp->level;
195 /* server: fill in a challenge in the packet */
198 rxkad_GetChallenge(struct rx_securityClass *aobj, struct rx_connection *aconn,
199 struct rx_packet *apacket)
201 struct rxkad_sconn *sconn;
204 struct rxkad_v2Challenge c_v2; /* version 2 */
205 struct rxkad_oldChallenge c_old; /* old style */
207 sconn = (struct rxkad_sconn *)aconn->securityData;
208 if (rx_IsUsingPktCksum(aconn))
209 sconn->cksumSeen = 1;
211 if (sconn->cksumSeen) {
212 memset(&c_v2, 0, sizeof(c_v2));
213 c_v2.version = htonl(RXKAD_CHALLENGE_PROTOCOL_VERSION);
214 c_v2.challengeID = htonl(sconn->challengeID);
215 c_v2.level = htonl((afs_int32) sconn->level);
217 challenge = (char *)&c_v2;
218 challengeSize = sizeof(c_v2);
220 memset(&c_old, 0, sizeof(c_old));
221 c_old.challengeID = htonl(sconn->challengeID);
222 c_old.level = htonl((afs_int32) sconn->level);
223 challenge = (char *)&c_old;
224 challengeSize = sizeof(c_old);
226 if (rx_MyMaxSendSize < challengeSize)
227 return RXKADPACKETSHORT; /* not enough space */
229 rx_packetwrite(apacket, 0, challengeSize, challenge);
230 rx_SetDataSize(apacket, challengeSize);
232 LOCK_RXKAD_STATS rxkad_stats.challengesSent++;
233 UNLOCK_RXKAD_STATS return 0;
236 /* server: process a response to a challenge packet */
237 /* XXX this does some copying of data in and out of the packet, but I'll bet it
238 * could just do it in place, especially if I used rx_Pullup...
241 rxkad_CheckResponse(struct rx_securityClass *aobj,
242 struct rx_connection *aconn, struct rx_packet *apacket)
244 struct rxkad_sconn *sconn;
245 struct rxkad_sprivate *tsp;
246 struct ktc_encryptionKey serverKey;
247 struct rxkad_oldChallengeResponse oldr; /* response format */
248 struct rxkad_v2ChallengeResponse v2r;
249 afs_int32 tlen; /* ticket len */
250 afs_int32 kvno; /* key version of ticket */
251 char tix[MAXKTCTICKETLEN];
252 afs_int32 incChallengeID;
255 /* ticket contents */
256 struct ktc_principal client;
257 struct ktc_encryptionKey sessionkey;
262 struct rxkad_serverinfo *rock;
264 sconn = (struct rxkad_sconn *)aconn->securityData;
265 tsp = (struct rxkad_sprivate *)aobj->privateData;
267 if (sconn->cksumSeen) {
268 /* expect v2 response, leave fields in v2r in network order for cksum
269 * computation which follows decryption. */
270 if (rx_GetDataSize(apacket) < sizeof(v2r))
271 return RXKADPACKETSHORT;
272 rx_packetread(apacket, 0, sizeof(v2r), &v2r);
276 kvno = ntohl(v2r.kvno);
277 tlen = ntohl(v2r.ticketLen);
278 if (rx_GetDataSize(apacket) < sizeof(v2r) + tlen)
279 return RXKADPACKETSHORT;
281 /* expect old format response */
282 if (rx_GetDataSize(apacket) < sizeof(oldr))
283 return RXKADPACKETSHORT;
284 rx_packetread(apacket, 0, sizeof(oldr), &oldr);
287 kvno = ntohl(oldr.kvno);
288 tlen = ntohl(oldr.ticketLen);
289 if (rx_GetDataSize(apacket) != sizeof(oldr) + tlen)
290 return RXKADPACKETSHORT;
292 if ((tlen < MINKTCTICKETLEN) || (tlen > MAXKTCTICKETLEN))
293 return RXKADTICKETLEN;
295 rx_packetread(apacket, pos, tlen, tix); /* get ticket */
298 * We allow the ticket to be optionally decoded by an alternate
299 * ticket decoder, if the function variable
300 * rxkad_AlternateTicketDecoder is set. That function should
301 * return a code of -1 if it wants the ticket to be decoded by
302 * the standard decoder.
304 if (rxkad_AlternateTicketDecoder) {
306 rxkad_AlternateTicketDecoder(kvno, tix, tlen, client.name,
307 client.instance, client.cell,
308 &sessionkey, &host, &start, &end);
309 if (code && code != -1) {
313 code = -1; /* No alternate ticket decoder present */
317 * If the alternate decoder is not present, or returns -1, then
318 * assume the ticket is of the default style.
320 if (code == -1 && (kvno == RXKAD_TKT_TYPE_KERBEROS_V5)
321 || (kvno == RXKAD_TKT_TYPE_KERBEROS_V5_ENCPART_ONLY)) {
323 tkt_DecodeTicket5(tix, tlen, tsp->get_key, tsp->get_key_rock,
324 kvno, client.name, client.instance, client.cell,
325 &sessionkey, &host, &start, &end);
331 * If the alternate decoder/kerberos 5 decoder is not present, or
332 * returns -1, then assume the ticket is of the default style.
335 /* get ticket's key */
336 code = (*tsp->get_key) (tsp->get_key_rock, kvno, &serverKey);
338 return RXKADUNKNOWNKEY; /* invalid kvno */
340 tkt_DecodeTicket(tix, tlen, &serverKey, client.name,
341 client.instance, client.cell, &sessionkey, &host,
344 return RXKADBADTICKET;
346 code = tkt_CheckTimes(start, end, time(0));
352 code = fc_keysched(&sessionkey, sconn->keysched);
355 memcpy(sconn->ivec, &sessionkey, sizeof(sconn->ivec));
357 if (sconn->cksumSeen) {
358 /* using v2 response */
359 afs_uint32 cksum; /* observed cksum */
360 struct rxkad_endpoint endpoint; /* connections endpoint */
364 memcpy(xor, sconn->ivec, 2 * sizeof(afs_int32));
365 fc_cbc_encrypt(&v2r.encrypted, &v2r.encrypted, sizeof(v2r.encrypted),
366 sconn->keysched, xor, DECRYPT);
367 cksum = rxkad_CksumChallengeResponse(&v2r);
368 if (cksum != v2r.encrypted.endpoint.cksum)
369 return RXKADSEALEDINCON;
370 (void)rxkad_SetupEndpoint(aconn, &endpoint);
371 v2r.encrypted.endpoint.cksum = 0;
372 if (memcmp(&endpoint, &v2r.encrypted.endpoint, sizeof(endpoint)) != 0)
373 return RXKADSEALEDINCON;
374 for (i = 0; i < RX_MAXCALLS; i++) {
375 v2r.encrypted.callNumbers[i] =
376 ntohl(v2r.encrypted.callNumbers[i]);
377 if (v2r.encrypted.callNumbers[i] < 0)
378 return RXKADSEALEDINCON;
381 (void)rxi_SetCallNumberVector(aconn, v2r.encrypted.callNumbers);
382 incChallengeID = ntohl(v2r.encrypted.incChallengeID);
383 level = ntohl(v2r.encrypted.level);
385 /* expect old format response */
386 fc_ecb_encrypt(&oldr.encrypted, &oldr.encrypted, sconn->keysched,
388 incChallengeID = ntohl(oldr.encrypted.incChallengeID);
389 level = ntohl(oldr.encrypted.level);
391 if (incChallengeID != sconn->challengeID + 1)
392 return RXKADOUTOFSEQUENCE; /* replay attempt */
393 if ((level < sconn->level) || (level > rxkad_crypt))
394 return RXKADLEVELFAIL;
395 sconn->level = level;
396 rxkad_SetLevel(aconn, sconn->level);
397 LOCK_RXKAD_STATS rxkad_stats.responses[rxkad_LevelIndex(sconn->level)]++;
399 /* now compute endpoint-specific info used for computing 16 bit checksum */
400 rxkad_DeriveXORInfo(aconn, sconn->keysched, sconn->ivec,
403 /* otherwise things are ok */
404 sconn->expirationTime = end;
405 sconn->authenticated = 1;
408 code = tsp->user_ok(client.name, client.instance, client.cell, kvno);
411 } else { /* save the info for later retreival */
412 int size = sizeof(struct rxkad_serverinfo);
413 rock = (struct rxkad_serverinfo *)osi_Alloc(size);
414 memset(rock, 0, size);
416 memcpy(&rock->client, &client, sizeof(rock->client));
422 /* return useful authentication info about a server-side connection */
425 rxkad_GetServerInfo(struct rx_connection * aconn, rxkad_level * level,
426 afs_uint32 * expiration, char *name, char *instance,
427 char *cell, afs_int32 * kvno)
429 struct rxkad_sconn *sconn;
431 sconn = (struct rxkad_sconn *)aconn->securityData;
432 if (sconn && sconn->authenticated && sconn->rock
433 && (time(0) < sconn->expirationTime)) {
435 *level = sconn->level;
437 *expiration = sconn->expirationTime;
439 strcpy(name, sconn->rock->client.name);
441 strcpy(instance, sconn->rock->client.instance);
443 strcpy(cell, sconn->rock->client.cell);
445 *kvno = sconn->rock->kvno;