Implement the rxgk server security object routines
[openafs.git] / src / rxgk / rxgk_server.c
1 /* rxgk/rxgk_server.c - server-specific security object routines */
2 /*
3  * Copyright (C) 2013, 2014 by the Massachusetts Institute of Technology.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * * Redistributions of source code must retain the above copyright
11  *   notice, this list of conditions and the following disclaimer.
12  *
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
16  *   distribution.
17  *
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.
30  */
31
32 /*
33  * Server-specific security object routines.
34  */
35
36 #include <afsconfig.h>
37 #include <afs/param.h>
38 #include <afs/stds.h>
39
40 #include <roken.h>
41
42 #include <afs/opr.h>
43 #include <rx/rx.h>
44 #include <rx/xdr.h>
45 #include <rx/rx_packet.h>
46 #include <rx/rxgk.h>
47
48 #include "rxgk_private.h"
49
50 /*
51  * Increment the reference count on the security object secobj.
52  */
53 static_inline void
54 obj_ref(struct rx_securityClass *secobj)
55 {
56     secobj->refCount++;
57 }
58
59 /*
60  * Decrement the reference count on the security object secobj.
61  * If the reference count falls to zero, release the underlying storage.
62  */
63 static void
64 obj_rele(struct rx_securityClass *secobj)
65 {
66     struct rxgk_sprivate *sp;
67
68     secobj->refCount--;
69     if (secobj->refCount > 0) {
70         /* still in use */
71         return;
72     }
73
74     sp = secobj->privateData;
75     rxi_Free(secobj, sizeof(*secobj));
76     rxi_Free(sp, sizeof(*sp));
77     return;
78 }
79
80 /* Release a server security object. */
81 static int
82 rxgk_ServerClose(struct rx_securityClass *aobj)
83 {
84     obj_rele(aobj);
85     return 0;
86 }
87
88 /* Set fields in 'sc' to invalid/uninitialized values, so we don't accidentally
89  * use blank/zeroed values later. */
90 static void
91 sconn_set_noauth(struct rxgk_sconn *sc)
92 {
93     rxgk_release_key(&sc->k0);
94     if (sc->client != NULL)
95         rx_identity_free(&sc->client);
96     sc->start_time = 0;
97     sc->auth = 0;
98
99     /*
100      * The values here should never be seen; set some bogus values. For
101      * 'expiration' and 'level', values of 0 are not bogus, so we explicitly
102      * set some nonzero values that are sure to be invalid, just in case they
103      * get used.
104      */
105     sc->expiration = 1;
106     sc->level = RXGK_LEVEL_BOGUS;
107 }
108
109 /*
110  * Create a new rx connection on this given server security object.
111  */
112 static int
113 rxgk_NewServerConnection(struct rx_securityClass *aobj,
114                          struct rx_connection *aconn)
115 {
116     struct rxgk_sconn *sc;
117
118     if (rx_GetSecurityData(aconn) != NULL)
119         goto error;
120
121     sc = rxi_Alloc(sizeof(*sc));
122     if (sc == NULL)
123         goto error;
124
125     sconn_set_noauth(sc);
126     rx_SetSecurityData(aconn, sc);
127     obj_ref(aobj);
128     return 0;
129
130  error:
131     return RXGK_INCONSISTENCY;
132 }
133
134 /*
135  * Server-specific packet preparation routine. All the interesting bits are in
136  * rxgk_packet.c; all we have to do here is extract data from the security data
137  * on the connection and use the proper key usage.
138  */
139 static int
140 rxgk_ServerPreparePacket(struct rx_securityClass *aobj, struct rx_call *acall,
141                          struct rx_packet *apacket)
142 {
143     struct rxgk_sconn *sc;
144     struct rx_connection *aconn;
145     rxgk_key tk;
146     afs_uint32 lkvno;
147     afs_uint16 wkvno, len;
148     int ret;
149
150     aconn = rx_ConnectionOf(acall);
151     sc = rx_GetSecurityData(aconn);
152
153     if (sc->expiration < RXGK_NOW() && sc->expiration != RXGK_NEVERDATE)
154         return RXGK_EXPIRED;
155
156     len = rx_GetDataSize(apacket);
157     lkvno = sc->key_number;
158     sc->stats.psent++;
159     sc->stats.bsent += len;
160     wkvno = (afs_uint16)lkvno;
161     rx_SetPacketCksum(apacket, wkvno);
162
163     if (sc->level == RXGK_LEVEL_CLEAR)
164         return 0;
165
166     ret = rxgk_derive_tk(&tk, sc->k0, rx_GetConnectionEpoch(aconn),
167                          rx_GetConnectionId(aconn), sc->start_time, lkvno);
168     if (ret != 0)
169         return ret;
170
171     switch(sc->level) {
172         case RXGK_LEVEL_AUTH:
173             ret = rxgk_mic_packet(tk, RXGK_SERVER_MIC_PACKET, aconn, apacket);
174             break;
175         case RXGK_LEVEL_CRYPT:
176             ret = rxgk_enc_packet(tk, RXGK_SERVER_ENC_PACKET, aconn, apacket);
177             break;
178         default:
179             ret = RXGK_INCONSISTENCY;
180             break;
181     }
182
183     rxgk_release_key(&tk);
184     return ret;
185 }
186
187 /* Did a connection properly authenticate? */
188 static int
189 rxgk_CheckAuthentication(struct rx_securityClass *aobj,
190                          struct rx_connection *aconn)
191 {
192     struct rxgk_sconn *sc;
193
194     sc = rx_GetSecurityData(aconn);
195     if (sc == NULL)
196         return RXGK_INCONSISTENCY;
197
198     if (sc->auth == 0)
199         return RXGK_NOTAUTH;
200
201     return 0;
202 }
203
204 /* Generate a challenge to be used later. */
205 static int
206 rxgk_CreateChallenge(struct rx_securityClass *aobj,
207                      struct rx_connection *aconn)
208 {
209     struct rxgk_sconn *sc;
210     struct rx_opaque buf = RX_EMPTY_OPAQUE;
211     opr_StaticAssert(sizeof(sc->challenge) == RXGK_CHALLENGE_NONCE_LEN);
212
213     sc = rx_GetSecurityData(aconn);
214     if (sc == NULL)
215         return RXGK_INCONSISTENCY;
216     sc->auth = 0;
217
218     /* The challenge is a 20-byte random nonce. */
219     if (rxgk_nonce(&buf, RXGK_CHALLENGE_NONCE_LEN) != 0)
220         return RXGK_INCONSISTENCY;
221
222     opr_Assert(buf.len == RXGK_CHALLENGE_NONCE_LEN);
223     memcpy(&sc->challenge, buf.val, RXGK_CHALLENGE_NONCE_LEN);
224     rx_opaque_freeContents(&buf);
225     sc->challenge_valid = 1;
226     return 0;
227 }
228
229 /*
230  * Read the challenge stored in 'sc', performing some sanity checks. Always go
231  * through this function to access the challenge, and never read from
232  * sc->challenge directly.
233  */
234 static int
235 read_challenge(struct rxgk_sconn *sc, void *buf, int len)
236 {
237     opr_StaticAssert(sizeof(sc->challenge) == RXGK_CHALLENGE_NONCE_LEN);
238
239     if (len != RXGK_CHALLENGE_NONCE_LEN) {
240         return RXGK_INCONSISTENCY;
241     }
242     if (!sc->challenge_valid) {
243         return RXGK_INCONSISTENCY;
244     }
245     memcpy(buf, sc->challenge, RXGK_CHALLENGE_NONCE_LEN);
246     return 0;
247 }
248
249 /* Incorporate a challenge into a packet */
250 static int
251 rxgk_GetChallenge(struct rx_securityClass *aobj, struct rx_connection *aconn,
252                   struct rx_packet *apacket)
253 {
254     XDR xdrs;
255     struct rxgk_sconn *sc;
256     void *data = NULL;
257     RXGK_Challenge challenge;
258     int ret;
259     u_int len = 0;
260     opr_StaticAssert(sizeof(challenge.nonce) == RXGK_CHALLENGE_NONCE_LEN);
261
262     memset(&xdrs, 0, sizeof(xdrs));
263     memset(&challenge, 0, sizeof(challenge));
264
265     sc = rx_GetSecurityData(aconn);
266     if (sc == NULL) {
267         ret = RXGK_INCONSISTENCY;
268         goto done;
269     }
270     ret = read_challenge(sc, challenge.nonce, sizeof(challenge.nonce));
271     if (ret)
272         goto done;
273
274     xdrlen_create(&xdrs);
275     if (!xdr_RXGK_Challenge(&xdrs, &challenge)) {
276         ret = RXGEN_SS_MARSHAL;
277         goto done;
278     }
279     len = xdr_getpos(&xdrs);
280
281     data = rxi_Alloc(len);
282     if (data == NULL) {
283         ret = RXGK_INCONSISTENCY;
284         goto done;
285     }
286     xdr_destroy(&xdrs);
287     xdrmem_create(&xdrs, data, len, XDR_ENCODE);
288     if (!xdr_RXGK_Challenge(&xdrs, &challenge)) {
289         ret = RXGEN_SS_MARSHAL;
290         goto done;
291     }
292     opr_Assert(len <= 0xffffu);
293     rx_packetwrite(apacket, 0, len, data);
294     rx_SetDataSize(apacket, len);
295
296     /* Nothing should really pay attention to the checksum of a challenge
297      * packet, but just set it to 0 so it's always set to _something_. */
298     rx_SetPacketCksum(apacket, 0);
299
300     ret = 0;
301
302  done:
303     rxi_Free(data, len);
304     if (xdrs.x_ops)
305         xdr_destroy(&xdrs);
306     return ret;
307 }
308
309 /*
310  * Helper functions for CheckResponse.
311  */
312
313 /**
314  * The XDR token format uses the XDR PrAuthName type to store identities.
315  * However, there is an existing rx_identity type used in libauth, so
316  * we convert from the wire type to the internal type as soon as possible
317  * in order to be able to use the most library code. 'a_identity' will contain
318  * a single identity on success, not an array.
319  *
320  * @return rxgk error codes
321  */
322 static int
323 prnames_to_identity(struct rx_identity **a_identity, PrAuthName *namelist,
324                     size_t nnames)
325 {
326     rx_identity_kind kind;
327     size_t len;
328     char *display;
329
330     *a_identity = NULL;
331
332     /* Could grab the acceptor identity from ServiceSpecific if wanted. */
333     if (nnames == 0) {
334         *a_identity = rx_identity_new(RX_ID_SUPERUSER, "<printed token>", "",
335                                       0);
336         return 0;
337
338     } else if (nnames > 1) {
339         /* Compound identities are not supported yet. */
340         return RXGK_INCONSISTENCY;
341     }
342
343     if (namelist[0].kind == PRAUTHTYPE_KRB4)
344         kind = RX_ID_KRB4;
345     else if (namelist[0].kind == PRAUTHTYPE_GSS)
346         kind = RX_ID_GSS;
347     else
348         return RXGK_INCONSISTENCY;
349     len = namelist[0].display.len;
350     display = rxi_Alloc(len + 1);
351     if (display == NULL)
352         return RXGK_INCONSISTENCY;
353     memcpy(display, namelist[0].display.val, len);
354     display[len] = '\0';
355     *a_identity = rx_identity_new(kind, display, namelist[0].data.val,
356                                   namelist[0].data.len);
357     rxi_Free(display, len + 1);
358     return 0;
359 }
360
361 /*
362  * Unpack, decrypt, and extract information from a token.
363  * Store the relevant bits in the connection security data.
364  */
365 static int
366 process_token(RXGK_Data *tc, struct rxgk_sprivate *sp, struct rxgk_sconn *sc)
367 {
368     RXGK_Token token;
369     int ret;
370
371     memset(&token, 0, sizeof(token));
372
373     ret = rxgk_extract_token(tc, &token, sp->getkey, sp->rock);
374     if (ret != 0)
375         goto done;
376
377     /* Stash the token master key in the per-connection data. */
378     rxgk_release_key(&sc->k0);
379     ret = rxgk_make_key(&sc->k0, token.K0.val, token.K0.len, token.enctype);
380     if (ret != 0)
381         goto done;
382
383     sc->level = token.level;
384     sc->expiration = token.expirationtime;
385     /*
386      * TODO: note that we currently ignore the bytelife and lifetime in
387      * 'token'. In the future, we should of course actually remember these and
388      * potentially alter our rekeying frequency according to them.
389      */
390
391     if (sc->client != NULL)
392         rx_identity_free(&sc->client);
393     ret = prnames_to_identity(&sc->client, token.identities.val,
394                               token.identities.len);
395     if (ret != 0)
396         goto done;
397
398  done:
399     xdr_free((xdrproc_t)xdr_RXGK_Token, &token);
400     return ret;
401 }
402
403 static void
404 update_kvno(struct rxgk_sconn *sc, afs_uint32 kvno)
405 {
406     sc->key_number = kvno;
407
408     /* XXX Our statistics for tracking when to re-key the conn should be reset
409      * here. */
410 }
411
412 /* Caller is responsible for freeing 'out'. */
413 static int
414 decrypt_authenticator(RXGK_Authenticator *out, struct rx_opaque *in,
415                       struct rx_connection *aconn, struct rxgk_sconn *sc,
416                       afs_uint16 wkvno)
417 {
418     XDR xdrs;
419     struct rx_opaque packauth = RX_EMPTY_OPAQUE;
420     rxgk_key tk = NULL;
421     afs_uint32 lkvno, kvno = 0;
422     int ret;
423
424     memset(&xdrs, 0, sizeof(xdrs));
425
426     lkvno = sc->key_number;
427     ret = rxgk_key_number(wkvno, lkvno, &kvno);
428     if (ret != 0)
429         goto done;
430     ret = rxgk_derive_tk(&tk, sc->k0, rx_GetConnectionEpoch(aconn),
431                          rx_GetConnectionId(aconn), sc->start_time, kvno);
432     if (ret != 0)
433         goto done;
434     ret = rxgk_decrypt_in_key(tk, RXGK_CLIENT_ENC_RESPONSE, in, &packauth);
435     if (ret != 0) {
436         goto done;
437     }
438     if (kvno > lkvno)
439         update_kvno(sc, kvno);
440
441     xdrmem_create(&xdrs, packauth.val, packauth.len, XDR_DECODE);
442     if (!xdr_RXGK_Authenticator(&xdrs, out)) {
443         ret = RXGEN_SS_UNMARSHAL;
444         goto done;
445     }
446     ret = 0;
447
448  done:
449     rx_opaque_freeContents(&packauth);
450     rxgk_release_key(&tk);
451     if (xdrs.x_ops)
452         xdr_destroy(&xdrs);
453     return ret;
454 }
455
456 /*
457  * Make the authenticator do its job with channel binding and nonce
458  * verification.
459  */
460 static int
461 check_authenticator(RXGK_Authenticator *authenticator,
462                     struct rx_connection *aconn, struct rxgk_sconn *sc)
463 {
464     /*
465      * To check the data in the authenticator, we could simply check
466      * if (got_value == expected_value) for each field we care about. But since
467      * this is a security-sensitive check, we should try to do this check in
468      * constant time to avoid timing-based attacks. So to do that, we construct
469      * a small structure of the values we got and the expected values, and run
470      * ct_memcmp on the whole thing at the end.
471      */
472
473     int code;
474     struct {
475         unsigned char challenge[RXGK_CHALLENGE_NONCE_LEN];
476         RXGK_Level level;
477         afs_uint32 epoch;
478         afs_uint32 cid;
479         int calls_len;
480     } auth_got, auth_exp;
481
482     opr_StaticAssert(sizeof(auth_got.challenge) == RXGK_CHALLENGE_NONCE_LEN);
483     opr_StaticAssert(sizeof(auth_exp.challenge) == RXGK_CHALLENGE_NONCE_LEN);
484     opr_StaticAssert(sizeof(authenticator->nonce) == RXGK_CHALLENGE_NONCE_LEN);
485
486     memset(&auth_got, 0, sizeof(auth_got));
487     memset(&auth_exp, 0, sizeof(auth_exp));
488
489     memcpy(auth_got.challenge, authenticator->nonce, RXGK_CHALLENGE_NONCE_LEN);
490     code = read_challenge(sc, auth_exp.challenge, sizeof(auth_exp.challenge));
491     if (code)
492         return code;
493
494     auth_got.level = authenticator->level;
495     auth_exp.level = sc->level;
496
497     auth_got.epoch = authenticator->epoch;
498     auth_exp.epoch = rx_GetConnectionEpoch(aconn);
499
500     auth_got.cid = authenticator->cid;
501     auth_exp.cid = rx_GetConnectionId(aconn);
502
503     auth_got.calls_len = authenticator->call_numbers.len;
504     auth_exp.calls_len = RX_MAXCALLS;
505
506     /* XXX We do nothing with the appdata for now. */
507
508     if (ct_memcmp(&auth_got, &auth_exp, sizeof(auth_got)) != 0) {
509         return RXGK_BADCHALLENGE;
510     }
511     return 0;
512 }
513
514 /* Process the response packet to a challenge */
515 static int
516 rxgk_CheckResponse(struct rx_securityClass *aobj,
517                    struct rx_connection *aconn, struct rx_packet *apacket)
518 {
519     struct rxgk_sprivate *sp;
520     struct rxgk_sconn *sc;
521     XDR xdrs;
522     RXGK_Response response;
523     RXGK_Authenticator authenticator;
524     int ret;
525
526     memset(&xdrs, 0, sizeof(xdrs));
527     memset(&response, 0, sizeof(response));
528     memset(&authenticator, 0, sizeof(authenticator));
529
530     sp = aobj->privateData;
531     sc = rx_GetSecurityData(aconn);
532
533     /*
534      * This assumes that the entire response is in a contiguous data block in
535      * the packet. rx in general can store packet data in multiple different
536      * buffers (pointed to by apacket->wirevec[N]), but the payload when
537      * receiving a Response packet should all be in one buffer (so we can just
538      * reference it directly via rx_DataOf()). If this assumption turns out to
539      * be wrong, then we'll just see a truncated response blob and this
540      * function will likely return an error; there should be no danger of
541      * buffer overrun or anything scary like that.
542      */
543     xdrmem_create(&xdrs, rx_DataOf(apacket), rx_Contiguous(apacket),
544                   XDR_DECODE);
545     if (!xdr_RXGK_Response(&xdrs, &response)) {
546         ret = RXGEN_SS_UNMARSHAL;
547         goto done;
548     }
549
550     /* Stash useful bits from the token in sc. */
551     ret = process_token(&response.token, sp, sc);
552     if (ret != 0)
553         goto done;
554     if (sc->expiration < RXGK_NOW() && sc->expiration != RXGK_NEVERDATE) {
555         ret = RXGK_EXPIRED;
556         goto done;
557     }
558
559     /*
560      * Cache the client-provided start_time. If this is wrong, we cannot derive
561      * the correct transport key and the authenticator decryption will fail.
562      */
563     sc->start_time = response.start_time;
564
565     /* Try to decrypt the authenticator. */
566     ret = decrypt_authenticator(&authenticator, &response.authenticator, aconn,
567                                 sc, rx_GetPacketCksum(apacket));
568     if (ret != 0)
569         goto done;
570     ret = check_authenticator(&authenticator, aconn, sc);
571     if (ret != 0)
572         goto done;
573     ret = rxgk_security_overhead(aconn, sc->level, sc->k0);
574     if (ret != 0)
575         goto done;
576     if (rxi_SetCallNumberVector(aconn, (afs_int32 *)authenticator.call_numbers.val) != 0) {
577         ret = RXGK_INCONSISTENCY;
578         goto done;
579     }
580     /* Success! */
581     sc->auth = 1;
582     sc->challenge_valid = 0;
583
584  done:
585     if (ret != 0)
586         sconn_set_noauth(sc);
587     if (xdrs.x_ops)
588         xdr_destroy(&xdrs);
589     xdr_free((xdrproc_t)xdr_RXGK_Response, &response);
590     xdr_free((xdrproc_t)xdr_RXGK_Authenticator, &authenticator);
591     return ret;
592 }
593
594 /*
595  * Server-specific packet receipt routine.
596  * The interesting bits are in rxgk_packet.c, we just extract data from the
597  * connection security data.
598  */
599 static int
600 rxgk_ServerCheckPacket(struct rx_securityClass *aobj, struct rx_call *acall,
601                        struct rx_packet *apacket)
602 {
603     struct rxgk_sconn *sc;
604     struct rx_connection *aconn;
605     afs_uint32 lkvno, kvno;
606     afs_uint16 len;
607     int ret;
608
609     aconn = rx_ConnectionOf(acall);
610     sc = rx_GetSecurityData(aconn);
611     if (sc == NULL)
612         return RXGK_INCONSISTENCY;
613
614     len = rx_GetDataSize(apacket);
615     sc->stats.precv++;
616     sc->stats.brecv += len;
617     if (sc->expiration < RXGK_NOW() && sc->expiration != RXGK_NEVERDATE)
618         return RXGK_EXPIRED;
619
620     lkvno = kvno = sc->key_number;
621     ret = rxgk_check_packet(1, aconn, apacket, sc->level, sc->start_time,
622                             &kvno, sc->k0);
623     if (ret != 0)
624         return ret;
625
626     if (kvno > lkvno)
627         update_kvno(sc, kvno);
628
629     return ret;
630 }
631
632 /*
633  * Perform server-side connection-specific teardown.
634  */
635 static void
636 rxgk_DestroyServerConnection(struct rx_securityClass *aobj,
637                              struct rx_connection *aconn)
638 {
639     struct rxgk_sconn *sc;
640
641     sc = rx_GetSecurityData(aconn);
642     if (sc == NULL) {
643         return;
644     }
645     rx_SetSecurityData(aconn, NULL);
646
647     rxgk_release_key(&sc->k0);
648     if (sc->client != NULL)
649         rx_identity_free(&sc->client);
650     rxi_Free(sc, sizeof(*sc));
651     obj_rele(aobj);
652 }
653
654 /*
655  * Get statistics about this connection.
656  */
657 static int
658 rxgk_ServerGetStats(struct rx_securityClass *aobj, struct rx_connection *aconn,
659                     struct rx_securityObjectStats *astats)
660 {
661     struct rxgkStats *stats;
662     struct rxgk_sconn *sc;
663
664     astats->type = RX_SECTYPE_GK;
665     sc = rx_GetSecurityData(aconn);
666     if (sc == NULL) {
667         astats->flags |= RXGK_STATS_UNALLOC;
668         return 0;
669     }
670
671     stats = &sc->stats;
672     astats->level = sc->level;
673     if (sc->auth)
674         astats->flags |= RXGK_STATS_AUTH;
675     astats->expires = (afs_uint32)rxgkTimeToSeconds(sc->expiration);
676
677     astats->packetsReceived = stats->precv;
678     astats->packetsSent = stats->psent;
679     astats->bytesReceived = stats->brecv;
680     astats->bytesSent = stats->bsent;
681
682     return 0;
683 }
684
685 /*
686  * Get some information about this connection, in particular the security
687  * level, expiry time, and the remote user's identity.
688  */
689 afs_int32
690 rxgk_GetServerInfo(struct rx_connection *conn, RXGK_Level *level,
691                    rxgkTime *expiry, struct rx_identity **identity)
692 {
693     struct rxgk_sconn *sconn;
694
695     if (rx_SecurityClassOf(conn) != RX_SECIDX_GK) {
696         return EINVAL;
697     }
698
699     sconn = rx_GetSecurityData(conn);
700     if (sconn == NULL)
701         return RXGK_INCONSISTENCY;
702     if (identity != NULL) {
703         *identity = rx_identity_copy(sconn->client);
704         if (*identity == NULL)
705             return RXGK_INCONSISTENCY;
706     }
707     if (level != NULL)
708         *level = sconn->level;
709     if (expiry != NULL)
710         *expiry = sconn->expiration;
711     return 0;
712 }
713
714 static struct rx_securityOps rxgk_server_ops = {
715     rxgk_ServerClose,
716     rxgk_NewServerConnection,
717     rxgk_ServerPreparePacket,           /* once per packet creation */
718     0,                                  /* send packet (once per retrans) */
719     rxgk_CheckAuthentication,
720     rxgk_CreateChallenge,
721     rxgk_GetChallenge,
722     0,
723     rxgk_CheckResponse,
724     rxgk_ServerCheckPacket,             /* check data packet */
725     rxgk_DestroyServerConnection,
726     rxgk_ServerGetStats,
727     0,
728     0,                          /* spare 1 */
729     0,                          /* spare 2 */
730 };
731
732 /**
733  * The low-level routine to generate a new server security object.
734  *
735  * Takes a getkey function and its rock.
736  *
737  * It is not expected that most callers will use this function, as
738  * we provide helpers that do other setup, setting service-specific
739  * data and such.
740  */
741 struct rx_securityClass *
742 rxgk_NewServerSecurityObject(void *getkey_rock, rxgk_getkey_func getkey)
743 {
744     struct rx_securityClass *sc;
745     struct rxgk_sprivate *sp;
746
747     sc = rxi_Alloc(sizeof(*sc));
748     if (sc == NULL)
749         return NULL;
750     sp = rxi_Alloc(sizeof(*sp));
751     if (sp == NULL) {
752         rxi_Free(sc, sizeof(*sc));
753         return NULL;
754     }
755     sc->ops = &rxgk_server_ops;
756     sc->refCount = 1;
757     sc->privateData = sp;
758
759     /* Now set the server-private data. */
760     sp->rock = getkey_rock;
761     sp->getkey = getkey;
762
763     return sc;
764 }