a225474a1bac74975a0204d72715fb687231eaee
[openafs.git] / src / rxkad / rxkad_client.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 client-only routines.  They do not
12  * make any use of DES. */
13
14 #include <afsconfig.h>
15 #ifdef KERNEL
16 #include "afs/param.h"
17 #else
18 #include <afs/param.h>
19 #endif
20
21 RCSID("$Header$");
22
23 #ifdef KERNEL
24 #include "afs/stds.h"
25 #ifndef UKERNEL
26 #include "h/types.h"
27 #include "h/time.h"
28 #ifdef AFS_LINUX20_ENV
29 #include "h/socket.h"
30 #endif
31 #ifndef AFS_OBSD_ENV
32 #include "netinet/in.h"
33 #endif
34 #else /* !UKERNEL */
35 #include "afs/sysincludes.h"
36 #endif /* !UKERNEL */
37 #ifndef AFS_LINUX22_ENV
38 #include "rpc/types.h"
39 #include "rx/xdr.h"
40 #endif
41 #include "rx/rx.h"
42 #else /* ! KERNEL */
43 #include <afs/stds.h>
44 #include <sys/types.h>
45 #include <time.h>
46 #ifdef HAVE_STRING_H
47 #include <string.h>
48 #else
49 #ifdef HAVE_STRINGS_H
50 #include <strings.h>
51 #endif
52 #endif
53 #ifdef AFS_NT40_ENV
54 #include <winsock2.h>
55 #else
56 #include <netinet/in.h>
57 #endif
58 #include <rx/rx.h>
59 #include <rx/xdr.h>
60 #ifdef AFS_PTHREAD_ENV
61 #include "rxkad/rxkad.h"
62 #endif /* AFS_PTHREAD_ENV */
63 #endif /* KERNEL */
64
65 #include "private_data.h"
66 #define XPRT_RXKAD_CLIENT
67
68 char *rxi_Alloc();
69
70 #ifndef max
71 #define max(a,b)    ((a) < (b)? (b) : (a))
72 #endif /* max */
73
74 static struct rx_securityOps rxkad_client_ops = {
75     rxkad_Close,
76     rxkad_NewConnection,                /* every new connection */
77     rxkad_PreparePacket,                /* once per packet creation */
78     0,                                  /* send packet (once per retrans.) */
79     0,
80     0,
81     0,
82     rxkad_GetResponse,                  /* respond to challenge packet */
83     0,
84     rxkad_CheckPacket,                  /* check data packet */
85     rxkad_DestroyConnection,
86     rxkad_GetStats,
87     0,
88     0,
89     0,
90 };
91
92 /* To minimize changes to epoch, we set this Cuid once, and everyone (including
93  * rxnull) uses it after that.  This means that the Ksession of the first
94  * authencticated connection should be a good one. */
95
96 #ifdef AFS_PTHREAD_ENV
97 /*
98  * This mutex protects the following global variables:
99  * Cuid
100  * counter
101  * rxkad_EpochWasSet
102  */
103 #include <assert.h>
104 pthread_mutex_t rxkad_client_uid_mutex;
105 #define LOCK_CUID assert(pthread_mutex_lock(&rxkad_client_uid_mutex)==0);
106 #define UNLOCK_CUID assert(pthread_mutex_unlock(&rxkad_client_uid_mutex)==0);
107 #else
108 #define LOCK_CUID
109 #define UNLOCK_CUID
110 #endif /* AFS_PTHREAD_ENV */
111
112 static afs_int32 Cuid[2];                       /* set once and shared by all */
113 int rxkad_EpochWasSet = 0;              /* TRUE => we called rx_SetEpoch */
114
115 /* allocate a new connetion ID in place */
116 int rxkad_AllocCID(struct rx_securityClass *aobj, struct rx_connection *aconn)
117 {
118     struct rxkad_cprivate *tcp;
119     struct rxkad_cidgen tgen;
120     static afs_int32 counter = 0;               /* not used anymore */
121
122     LOCK_CUID
123     if (Cuid[0] == 0) {
124         afs_uint32 xor[2];
125         tgen.ipAddr = rxi_getaddr();    /* comes back in net order */
126         clock_GetTime(&tgen.time);      /* changes time1 and time2 */
127         tgen.time.sec = htonl(tgen.time.sec);
128         tgen.time.usec = htonl(tgen.time.usec);
129         tgen.counter = htonl(counter);
130         counter++;
131 #ifdef KERNEL
132         tgen.random1 = afs_random() & 0x7fffffff;       /* was "80000" */
133         tgen.random2 = afs_random() & 0x7fffffff;       /* was "htonl(100)" */
134 #else
135         tgen.random1 = htonl(getpid());
136         tgen.random2 = htonl(100);
137 #endif
138         if (aobj) {
139             /* block is ready for encryption with session key, let's go for it. */
140             tcp = (struct rxkad_cprivate *) aobj->privateData;
141             memcpy((void *)xor, (void *)tcp->ivec, 2*sizeof(afs_int32));
142             fc_cbc_encrypt((char *) &tgen, (char *) &tgen, sizeof(tgen),
143                            tcp->keysched, xor, ENCRYPT);
144         } else {
145             /* Create a session key so that we can encrypt it */
146
147         }
148         memcpy((void *)Cuid, ((char *)&tgen) + sizeof(tgen) - ENCRYPTIONBLOCKSIZE, ENCRYPTIONBLOCKSIZE);
149         Cuid[0] = (Cuid[0] & ~0x40000000) | 0x80000000;
150         Cuid[1] &= RX_CIDMASK;
151         rx_SetEpoch (Cuid[0]);          /* for future rxnull connections */
152         rxkad_EpochWasSet++;
153     }
154
155     if (!aconn) {
156         UNLOCK_CUID
157         return 0;
158     }
159     aconn->epoch = Cuid[0];
160     aconn->cid = Cuid[1];
161     Cuid[1] += 1<<RX_CIDSHIFT;
162     UNLOCK_CUID
163     return 0;
164 }
165
166 /* Allocate a new client security object.  Called with the encryption level,
167  * the session key and the ticket for the other side obtained from the
168  * AuthServer.  Refers to export control to determine level. */
169
170 struct rx_securityClass *rxkad_NewClientSecurityObject(
171         rxkad_level level, struct ktc_encryptionKey *sessionkey,
172         afs_int32 kvno, int ticketLen, char *ticket)
173 {   struct rx_securityClass *tsc;
174     struct rxkad_cprivate   *tcp;
175     int                      code;
176     int                      size;
177
178     size = sizeof(struct rx_securityClass);
179     tsc = (struct rx_securityClass *) rxi_Alloc (size);
180     memset((void *)tsc, 0, size);
181     tsc->refCount = 1;                  /* caller gets one for free */
182     tsc->ops = &rxkad_client_ops;
183
184     size = sizeof(struct rxkad_cprivate);
185     tcp = (struct rxkad_cprivate *) rxi_Alloc (size);
186     memset((void *)tcp, 0, size);
187     tsc->privateData = (char *) tcp;
188     tcp->type |= rxkad_client;
189     tcp->level = level;
190     code = fc_keysched (sessionkey, tcp->keysched);
191     if (code) return 0;                 /* bad key */
192     memcpy((void *)tcp->ivec, (void *)sessionkey, sizeof(tcp->ivec));
193     tcp->kvno = kvno;                   /* key version number */
194     tcp->ticketLen = ticketLen;         /* length of ticket */
195     if (tcp->ticketLen > MAXKTCTICKETLEN) return 0; /* bad key */
196     memcpy(tcp->ticket, ticket, ticketLen);
197
198     LOCK_RXKAD_STATS
199     rxkad_stats_clientObjects++;
200     UNLOCK_RXKAD_STATS
201     return tsc;
202 }
203
204 /* client: respond to a challenge packet */
205
206 int rxkad_GetResponse(struct rx_securityClass *aobj, 
207         struct rx_connection *aconn, struct rx_packet *apacket)
208 {   struct rxkad_cprivate *tcp;
209     char *tp;
210     int   v2;                           /* whether server is old style or v2 */
211     afs_int32  challengeID;
212     rxkad_level level;
213     char *response;
214     int   responseSize, missing;
215     struct rxkad_v2ChallengeResponse  r_v2;
216     struct rxkad_oldChallengeResponse r_old;
217
218     tcp = (struct rxkad_cprivate *) aobj->privateData;
219
220     if (!(tcp->type & rxkad_client)) return RXKADINCONSISTENCY;
221
222     v2 = (rx_Contiguous(apacket) > sizeof(struct rxkad_oldChallenge));
223     tp = rx_DataOf(apacket);
224
225     if (v2) {                                  /* v2 challenge */
226         struct rxkad_v2Challenge *c_v2;
227         if (rx_GetDataSize(apacket) < sizeof(struct rxkad_v2Challenge))
228            return RXKADPACKETSHORT;
229         c_v2 = (struct rxkad_v2Challenge *)tp;
230         challengeID = ntohl(c_v2->challengeID);
231         level       = ntohl(c_v2->level);
232     } else {                                   /* old format challenge */
233         struct rxkad_oldChallenge *c_old;
234         if (rx_GetDataSize(apacket) < sizeof(struct rxkad_oldChallenge))
235            return RXKADPACKETSHORT;
236         c_old = (struct rxkad_oldChallenge *)tp;
237         challengeID = ntohl(c_old->challengeID);
238         level       = ntohl(c_old->level);
239     }
240
241     if (level > tcp->level) return RXKADLEVELFAIL;
242     LOCK_RXKAD_STATS
243     rxkad_stats.challenges[rxkad_LevelIndex(tcp->level)]++;
244     UNLOCK_RXKAD_STATS
245
246     if (v2) {
247         int i;
248         afs_uint32 xor[2];
249         memset((void *)&r_v2, 0, sizeof(r_v2));
250         r_v2.version = htonl(RXKAD_CHALLENGE_PROTOCOL_VERSION);
251         r_v2.spare   = 0;
252         (void) rxkad_SetupEndpoint (aconn, &r_v2.encrypted.endpoint);
253         (void) rxi_GetCallNumberVector (aconn, r_v2.encrypted.callNumbers);
254         for (i=0; i<RX_MAXCALLS; i++) {
255             if (r_v2.encrypted.callNumbers[i] < 0) return RXKADINCONSISTENCY;
256             r_v2.encrypted.callNumbers[i] = htonl(r_v2.encrypted.callNumbers[i]);
257         }
258         r_v2.encrypted.incChallengeID = htonl(challengeID + 1);
259         r_v2.encrypted.level          = htonl((afs_int32)tcp->level);
260         r_v2.kvno                     = htonl(tcp->kvno);
261         r_v2.ticketLen                = htonl(tcp->ticketLen);
262         r_v2.encrypted.endpoint.cksum = rxkad_CksumChallengeResponse (&r_v2);
263         memcpy((void *)xor, (void *)tcp->ivec, 2*sizeof(afs_int32));
264         fc_cbc_encrypt (&r_v2.encrypted, &r_v2.encrypted, 
265                         sizeof(r_v2.encrypted), tcp->keysched, xor, ENCRYPT);
266         response     = (char *)&r_v2;
267         responseSize = sizeof(r_v2);
268     } else {
269         memset((void *)&r_old, 0, sizeof(r_old));
270         r_old.encrypted.incChallengeID = htonl(challengeID + 1);
271         r_old.encrypted.level          = htonl((afs_int32)tcp->level);
272         r_old.kvno                     = htonl(tcp->kvno);
273         r_old.ticketLen                = htonl(tcp->ticketLen);
274         fc_ecb_encrypt (&r_old.encrypted, &r_old.encrypted, tcp->keysched, ENCRYPT);
275         response     = (char *)&r_old;
276         responseSize = sizeof(r_old);
277     }
278
279     if (RX_MAX_PACKET_DATA_SIZE < responseSize + tcp->ticketLen)
280         return RXKADPACKETSHORT;        /* not enough space */
281
282     rx_computelen(apacket, missing);
283     missing = responseSize + tcp->ticketLen - missing;
284     if (missing > 0) 
285        if (rxi_AllocDataBuf(apacket, missing, RX_PACKET_CLASS_SEND) > 0) 
286           return RXKADPACKETSHORT;      /* not enough space */
287
288     /* copy response and ticket into packet */
289     rx_packetwrite(apacket, 0, responseSize, response);
290     rx_packetwrite(apacket, responseSize, tcp->ticketLen, tcp->ticket);
291
292     rx_SetDataSize (apacket, responseSize + tcp->ticketLen);
293     return 0;
294 }
295
296 void rxkad_ResetState(void)
297 {
298     LOCK_CUID
299     Cuid[0] = 0;
300     rxkad_EpochWasSet=0;
301     UNLOCK_CUID
302 }