1 /* rxgk/rxgk_client.c - Client-only security object routines */
3 * Copyright (C) 2013, 2014 by the Massachusetts Institute of Technology.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * * Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
23 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
29 * OF THE POSSIBILITY OF SUCH DAMAGE.
33 * Client-only security object routines.
36 #include <afsconfig.h>
37 #include <afs/param.h>
42 /* OS-specific system headers go here */
45 #include <rx/rx_packet.h>
46 #include <rx/rx_identity.h>
50 #include "rxgk_private.h"
53 cprivate_destroy(struct rxgk_cprivate *cp)
57 rxgk_release_key(&cp->k0);
58 rx_opaque_freeContents(&cp->token);
59 rxi_Free(cp, sizeof(*cp));
63 * Increment the reference count on the security object secobj.
66 obj_ref(struct rx_securityClass *secobj)
72 * Decrement the reference count on the security object secobj.
73 * If the reference count falls to zero, release the underlying storage.
76 obj_rele(struct rx_securityClass *secobj)
78 struct rxgk_cprivate *cp;
81 if (secobj->refCount > 0) {
86 cp = secobj->privateData;
88 rxi_Free(secobj, sizeof(*secobj));
93 rxgk_ClientClose(struct rx_securityClass *aobj)
100 rxgk_NewClientConnection(struct rx_securityClass *aobj,
101 struct rx_connection *aconn)
103 struct rxgk_cconn *cc = NULL;
104 struct rxgk_cprivate *cp;
106 if (rx_GetSecurityData(aconn) != NULL)
108 cp = aobj->privateData;
110 cc = rxi_Alloc(sizeof(*cc));
113 cc->start_time = RXGK_NOW();
114 /* Set the header and trailer size to be reserved for the security
115 * class in each packet. */
116 if (rxgk_security_overhead(aconn, cp->level, cp->k0) != 0)
118 rx_SetSecurityData(aconn, cc);
123 rxi_Free(cc, sizeof(*cc));
124 return RXGK_INCONSISTENCY;
128 rxgk_ClientPreparePacket(struct rx_securityClass *aobj, struct rx_call *acall,
129 struct rx_packet *apacket)
131 struct rxgk_cconn *cc;
132 struct rxgk_cprivate *cp;
133 struct rx_connection *aconn;
136 afs_uint16 wkvno, len;
139 aconn = rx_ConnectionOf(acall);
140 cc = rx_GetSecurityData(aconn);
141 cp = aobj->privateData;
143 len = rx_GetDataSize(apacket);
144 lkvno = cc->key_number;
146 cc->stats.bsent += len;
147 wkvno = (afs_uint16)lkvno;
148 rx_SetPacketCksum(apacket, wkvno);
150 if (cp->level == RXGK_LEVEL_CLEAR)
153 ret = rxgk_derive_tk(&tk, cp->k0, rx_GetConnectionEpoch(aconn),
154 rx_GetConnectionId(aconn), cc->start_time, lkvno);
159 case RXGK_LEVEL_AUTH:
160 ret = rxgk_mic_packet(tk, RXGK_CLIENT_MIC_PACKET, aconn, apacket);
162 case RXGK_LEVEL_CRYPT:
163 ret = rxgk_enc_packet(tk, RXGK_CLIENT_ENC_PACKET, aconn, apacket);
166 ret = RXGK_INCONSISTENCY;
170 rxgk_release_key(&tk);
175 * Helpers for GetResponse.
179 * Populate the RXGK_Authenticator structure.
180 * The caller is responsible for pre-zeroing the structure and freeing
181 * the resulting allocations, including partial allocations in the case
185 fill_authenticator(RXGK_Authenticator *authenticator, RXGK_Challenge *challenge,
186 struct rxgk_cprivate *cp, struct rx_connection *aconn)
188 afs_int32 call_numbers[RX_MAXCALLS];
190 opr_StaticAssert(sizeof(authenticator->nonce) == RXGK_CHALLENGE_NONCE_LEN);
191 opr_StaticAssert(sizeof(challenge->nonce) == RXGK_CHALLENGE_NONCE_LEN);
193 memset(&call_numbers, 0, sizeof(call_numbers));
195 memcpy(authenticator->nonce, challenge->nonce, sizeof(authenticator->nonce));
197 authenticator->level = cp->level;
198 authenticator->epoch = rx_GetConnectionEpoch(aconn);
199 authenticator->cid = rx_GetConnectionId(aconn);
200 /* Export the call numbers. */
201 ret = rxi_GetCallNumberVector(aconn, call_numbers);
204 authenticator->call_numbers.val = xdr_alloc(RX_MAXCALLS *
206 if (authenticator->call_numbers.val == NULL)
207 return RXGEN_CC_MARSHAL;
208 authenticator->call_numbers.len = RX_MAXCALLS;
209 for (call_i = 0; call_i < RX_MAXCALLS; call_i++)
210 authenticator->call_numbers.val[call_i] = (afs_uint32)call_numbers[call_i];
214 /* XDR-encode an authenticator and encrypt it. */
216 pack_wrap_authenticator(RXGK_Data *encdata, RXGK_Authenticator *authenticator,
217 struct rxgk_cprivate *cp, struct rxgk_cconn *cc)
220 struct rx_opaque data = RX_EMPTY_OPAQUE;
225 memset(&xdrs, 0, sizeof(xdrs));
227 xdrlen_create(&xdrs);
228 if (!xdr_RXGK_Authenticator(&xdrs, authenticator)) {
229 ret = RXGEN_CC_MARSHAL;
232 len = xdr_getpos(&xdrs);
233 ret = rx_opaque_alloc(&data, len);
237 xdrmem_create(&xdrs, data.val, len, XDR_ENCODE);
238 if (!xdr_RXGK_Authenticator(&xdrs, authenticator)) {
239 ret = RXGEN_CC_MARSHAL;
242 ret = rxgk_derive_tk(&tk, cp->k0, authenticator->epoch, authenticator->cid,
243 cc->start_time, cc->key_number);
246 ret = rxgk_encrypt_in_key(tk, RXGK_CLIENT_ENC_RESPONSE, &data, encdata);
254 rx_opaque_freeContents(&data);
255 rxgk_release_key(&tk);
259 /* XDR-encode an RXGK_Response structure to put it on the wire.
260 * The caller must free the contents of the out parameter. */
262 pack_response(RXGK_Data *out, RXGK_Response *response)
268 memset(&xdrs, 0, sizeof(xdrs));
270 xdrlen_create(&xdrs);
271 if (!xdr_RXGK_Response(&xdrs, response)) {
272 ret = RXGEN_CC_MARSHAL;
275 len = xdr_getpos(&xdrs);
276 ret = rx_opaque_alloc(out, len);
280 xdrmem_create(&xdrs, out->val, len, XDR_ENCODE);
281 if (!xdr_RXGK_Response(&xdrs, response)) {
282 rx_opaque_freeContents(out);
283 ret = RXGEN_CC_MARSHAL;
295 * Respond to a challenge packet.
296 * The data of the packet on entry is the XDR-encoded RXGK_Challenge.
297 * We decode it and reuse the packet structure to prepare a response.
300 rxgk_GetResponse(struct rx_securityClass *aobj, struct rx_connection *aconn,
301 struct rx_packet *apacket)
303 struct rxgk_cprivate *cp;
304 struct rxgk_cconn *cc;
306 RXGK_Challenge challenge;
307 RXGK_Response response;
308 RXGK_Authenticator authenticator;
309 struct rx_opaque packed = RX_EMPTY_OPAQUE;
312 memset(&xdrs, 0, sizeof(xdrs));
313 memset(&challenge, 0, sizeof(challenge));
314 memset(&response, 0, sizeof(response));
315 memset(&authenticator, 0, sizeof(authenticator));
317 cp = aobj->privateData;
318 cc = rx_GetSecurityData(aconn);
321 * Decode the challenge to get the nonce.
322 * This assumes that the entire challenge is in a contiguous data block in
323 * the packet. rx in general can store packet data in multiple different
324 * buffers (pointed to by apacket->wirevec[N]), but the payload when
325 * receiving a Challenge packet should all be in one buffer (so we can just
326 * reference it directly via rx_DataOf()). If this assumption turns out to
327 * be wrong, then we'll just see a truncated challenge blob and this
328 * function will likely return an error; there should be no danger of
329 * buffer overrun or anything scary like that.
331 if (rx_Contiguous(apacket) < RXGK_CHALLENGE_NONCE_LEN) {
332 ret = RXGK_PACKETSHORT;
335 xdrmem_create(&xdrs, rx_DataOf(apacket), rx_Contiguous(apacket),
337 if (!xdr_RXGK_Challenge(&xdrs, &challenge)) {
338 ret = RXGEN_CC_UNMARSHAL;
342 /* Start filling the response. */
343 response.start_time = cc->start_time;
344 if (rx_opaque_copy(&response.token, &cp->token) != 0) {
345 ret = RXGEN_CC_MARSHAL;
349 /* Fill up the authenticator */
350 ret = fill_authenticator(&authenticator, &challenge, cp, aconn);
353 /* Authenticator is full, now to pack and encrypt it. */
354 ret = pack_wrap_authenticator(&response.authenticator, &authenticator, cp, cc);
357 /* Put the kvno we used on the wire for the remote end. */
358 rx_SetPacketCksum(apacket, (afs_uint16)cc->key_number);
360 /* Response is ready, now to shove it in a packet. */
361 ret = pack_response(&packed, &response);
364 osi_Assert(packed.len <= 0xffffu);
365 rx_packetwrite(apacket, 0, packed.len, packed.val);
366 rx_SetDataSize(apacket, (afs_uint16)packed.len);
372 xdr_free((xdrproc_t)xdr_RXGK_Challenge, &challenge);
373 xdr_free((xdrproc_t)xdr_RXGK_Response, &response);
374 xdr_free((xdrproc_t)xdr_RXGK_Authenticator, &authenticator);
375 rx_opaque_freeContents(&packed);
380 update_kvno(struct rxgk_cconn *cc, afs_uint32 kvno)
382 cc->key_number = kvno;
384 /* XXX Our statistics for tracking when to re-key the conn should be reset
389 rxgk_ClientCheckPacket(struct rx_securityClass *aobj, struct rx_call *acall,
390 struct rx_packet *apacket)
392 struct rxgk_cconn *cc;
393 struct rxgk_cprivate *cp;
394 struct rx_connection *aconn;
395 afs_uint32 lkvno, kvno;
399 aconn = rx_ConnectionOf(acall);
400 cc = rx_GetSecurityData(aconn);
401 cp = aobj->privateData;
403 len = rx_GetDataSize(apacket);
405 cc->stats.brecv += len;
407 lkvno = kvno = cc->key_number;
408 ret = rxgk_check_packet(0, aconn, apacket, cp->level, cc->start_time,
414 update_kvno(cc, kvno);
420 rxgk_DestroyClientConnection(struct rx_securityClass *aobj,
421 struct rx_connection *aconn)
423 struct rxgk_cconn *cc;
425 cc = rx_GetSecurityData(aconn);
429 rx_SetSecurityData(aconn, NULL);
431 rxi_Free(cc, sizeof(*cc));
436 rxgk_ClientGetStats(struct rx_securityClass *aobj, struct rx_connection *aconn,
437 struct rx_securityObjectStats *astats)
439 struct rxgkStats *stats;
440 struct rxgk_cprivate *cp;
441 struct rxgk_cconn *cc;
443 astats->type = RX_SECTYPE_GK;
444 cc = rx_GetSecurityData(aconn);
446 astats->flags |= RXGK_STATS_UNALLOC;
451 cp = aobj->privateData;
452 astats->level = cp->level;
454 astats->packetsReceived = stats->precv;
455 astats->packetsSent = stats->psent;
456 astats->bytesReceived = stats->brecv;
457 astats->bytesSent = stats->bsent;
462 static struct rx_securityOps rxgk_client_ops = {
464 rxgk_NewClientConnection, /* every new connection */
465 rxgk_ClientPreparePacket, /* once per packet creation */
466 0, /* send packet (once per retrans) */
470 rxgk_GetResponse, /* respond to challenge packet */
472 rxgk_ClientCheckPacket, /* check data packet */
473 rxgk_DestroyClientConnection,
481 * Create an rxgk client security object
483 * @param[in] level The security level of the token; this must be the
484 * level used for all connections using this security
486 * @param[in] enctype The RFC 3961 enctype of k0.
487 * @param[in] k0 The master key of the token.
488 * @param[in] token The rxgk token used to authenticate the connection.
489 * @return NULL on failure, and a pointer to the security object on success.
491 struct rx_securityClass *
492 rxgk_NewClientSecurityObject(RXGK_Level level, afs_int32 enctype, rxgk_key k0,
495 struct rx_securityClass *sc = NULL;
496 struct rxgk_cprivate *cp = NULL;
498 sc = rxi_Alloc(sizeof(*sc));
501 cp = rxi_Alloc(sizeof(*cp));
504 sc->ops = &rxgk_client_ops;
506 sc->privateData = cp;
508 /* Now get the client-private data. */
509 if (rxgk_copy_key(k0, &cp->k0) != 0)
511 cp->enctype = enctype;
513 if (rx_opaque_copy(&cp->token, token) != 0)
519 rxi_Free(sc, sizeof(*sc));
520 cprivate_destroy(cp);