4 Copyright (c) 2000 The Regents of the University of Michigan.
7 Copyright (c) 2002 Bruce Fields <bfields@UMICH.EDU>
9 Copyright (c) 2014, Intel Corporation.
11 Redistribution and use in source and binary forms, with or without
12 modification, are permitted provided that the following conditions
15 1. Redistributions of source code must retain the above copyright
16 notice, this list of conditions and the following disclaimer.
17 2. Redistributions in binary form must reproduce the above copyright
18 notice, this list of conditions and the following disclaimer in the
19 documentation and/or other materials provided with the distribution.
20 3. Neither the name of the University nor the names of its
21 contributors may be used to endorse or promote products derived
22 from this software without specific prior written permission.
24 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
25 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
26 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27 DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
31 BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
33 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
34 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 #include <sys/param.h>
54 #include <lnet/nidstr.h>
64 #include <lustre/lustre_idl.h>
66 #define SVCGSSD_CONTEXT_CHANNEL "/proc/net/rpc/auth.sptlrpc.context/channel"
67 #define SVCGSSD_INIT_CHANNEL "/proc/net/rpc/auth.sptlrpc.init/channel"
69 #define TOKEN_BUF_SIZE 8192
81 struct svc_nego_data {
86 char nm_name[LUSTRE_NODEMAP_NAME_LENGTH + 1];
87 gss_buffer_desc in_tok;
88 gss_buffer_desc out_tok;
89 gss_buffer_desc in_handle;
90 gss_buffer_desc out_handle;
97 gss_buffer_desc ctx_token;
101 do_svc_downcall(gss_buffer_desc *out_handle, struct svc_cred *cred,
102 gss_OID mechoid, gss_buffer_desc *context_token)
105 const char *mechname;
108 printerr(2, "doing downcall\n");
109 mechname = gss_OID_mech_name(mechoid);
110 if (mechname == NULL)
112 f = fopen(SVCGSSD_CONTEXT_CHANNEL, "w");
114 printerr(0, "WARNING: unable to open downcall channel "
116 SVCGSSD_CONTEXT_CHANNEL, strerror(errno));
119 qword_printhex(f, out_handle->value, out_handle->length);
120 /* XXX are types OK for the rest of this? */
121 qword_printint(f, 3600); /* an hour should be sufficient */
122 qword_printint(f, cred->cr_remote);
123 qword_printint(f, cred->cr_usr_root);
124 qword_printint(f, cred->cr_usr_mds);
125 qword_printint(f, cred->cr_usr_oss);
126 qword_printint(f, cred->cr_mapped_uid);
127 qword_printint(f, cred->cr_uid);
128 qword_printint(f, cred->cr_gid);
129 qword_print(f, mechname);
130 qword_printhex(f, context_token->value, context_token->length);
135 printerr(0, "WARNING: downcall failed\n");
139 struct gss_verifier {
141 gss_buffer_desc body;
144 #define RPCSEC_GSS_SEQ_WIN 5
147 send_response(FILE *f, gss_buffer_desc *in_handle, gss_buffer_desc *in_token,
148 u_int32_t maj_stat, u_int32_t min_stat,
149 gss_buffer_desc *out_handle, gss_buffer_desc *out_token)
151 char buf[2 * TOKEN_BUF_SIZE];
153 int blen = sizeof(buf);
157 printerr(2, "sending reply\n");
158 qword_addhex(&bp, &blen, in_handle->value, in_handle->length);
159 qword_addhex(&bp, &blen, in_token->value, in_token->length);
160 qword_addint(&bp, &blen, 3600); /* an hour should be sufficient */
161 qword_adduint(&bp, &blen, maj_stat);
162 qword_adduint(&bp, &blen, min_stat);
163 qword_addhex(&bp, &blen, out_handle->value, out_handle->length);
164 qword_addhex(&bp, &blen, out_token->value, out_token->length);
165 qword_addeol(&bp, &blen);
167 printerr(0, "WARNING: send_response: message too long\n");
170 g = open(SVCGSSD_INIT_CHANNEL, O_WRONLY);
172 printerr(0, "WARNING: open %s failed: %s\n",
173 SVCGSSD_INIT_CHANNEL, strerror(errno));
177 printerr(3, "writing message: %s", buf);
178 if (write(g, buf, bp - buf) == -1) {
179 printerr(0, "WARNING: failed to write message\n");
187 #define rpc_auth_ok 0
188 #define rpc_autherr_badcred 1
189 #define rpc_autherr_rejectedcred 2
190 #define rpc_autherr_badverf 3
191 #define rpc_autherr_rejectedverf 4
192 #define rpc_autherr_tooweak 5
193 #define rpcsec_gsserr_credproblem 13
194 #define rpcsec_gsserr_ctxproblem 14
197 get_ids(gss_name_t client_name, gss_OID mech, struct svc_cred *cred,
198 lnet_nid_t nid, uint32_t lustre_svc)
200 u_int32_t maj_stat, min_stat;
201 gss_buffer_desc name;
202 char *sname, *host, *realm;
203 const int namebuf_size = 512;
204 char namebuf[namebuf_size];
206 gss_OID name_type = GSS_C_NO_OID;
210 cred->cr_usr_root = cred->cr_usr_mds = cred->cr_usr_oss = 0;
211 cred->cr_uid = cred->cr_mapped_uid = cred->cr_gid = -1;
213 maj_stat = gss_display_name(&min_stat, client_name, &name, &name_type);
214 if (maj_stat != GSS_S_COMPLETE) {
215 pgsserr("get_ids: gss_display_name",
216 maj_stat, min_stat, mech);
219 if (name.length >= 0xffff || /* be certain name.length+1 doesn't overflow */
220 !(sname = calloc(name.length + 1, 1))) {
221 printerr(0, "WARNING: get_ids: error allocating %zu bytes "
222 "for sname\n", name.length + 1);
223 gss_release_buffer(&min_stat, &name);
226 memcpy(sname, name.value, name.length);
227 sname[name.length] = '\0';
228 gss_release_buffer(&min_stat, &name);
230 if (lustre_svc == LUSTRE_GSS_SVC_MDS)
231 lookup_mapping(sname, nid, &cred->cr_mapped_uid);
233 cred->cr_mapped_uid = -1;
235 realm = strchr(sname, '@');
239 printerr(0, "ERROR: %s has no realm name\n", sname);
243 host = strchr(sname, '/');
247 if (strcmp(sname, GSSD_SERVICE_MGS) == 0) {
248 printerr(0, "forbid %s as a user name\n", sname);
252 /* 1. check host part */
254 if (lnet_nid2hostname(nid, namebuf, namebuf_size)) {
255 printerr(0, "ERROR: failed to resolve hostname for "
256 "%s/%s@%s from %016llx\n",
257 sname, host, realm, nid);
261 if (strcasecmp(host, namebuf)) {
262 printerr(0, "ERROR: %s/%s@%s claimed hostname doesn't "
263 "match %s, nid %016llx\n", sname, host, realm,
268 if (!strcmp(sname, GSSD_SERVICE_MDS) ||
269 !strcmp(sname, GSSD_SERVICE_OSS)) {
270 printerr(0, "ERROR: %s@%s from %016llx doesn't "
271 "bind with hostname\n", sname, realm, nid);
276 /* 2. check realm and user */
277 switch (lustre_svc) {
278 case LUSTRE_GSS_SVC_MDS:
279 if (strcasecmp(mds_local_realm, realm)) {
282 /* only allow mapped user from remote realm */
283 if (cred->cr_mapped_uid == -1) {
284 printerr(0, "ERROR: %s%s%s@%s from %016llx "
285 "is remote but without mapping\n",
286 sname, host ? "/" : "",
287 host ? host : "", realm, nid);
291 if (!strcmp(sname, LUSTRE_ROOT_NAME)) {
293 cred->cr_usr_root = 1;
294 } else if (!strcmp(sname, GSSD_SERVICE_MDS)) {
296 cred->cr_usr_mds = 1;
297 } else if (!strcmp(sname, GSSD_SERVICE_OSS)) {
299 cred->cr_usr_oss = 1;
301 pw = getpwnam(sname);
303 cred->cr_uid = pw->pw_uid;
304 printerr(2, "%s resolve to uid %u\n",
305 sname, cred->cr_uid);
306 } else if (cred->cr_mapped_uid != -1) {
307 printerr(2, "user %s from %016llx is "
308 "mapped to %u\n", sname, nid,
309 cred->cr_mapped_uid);
311 printerr(0, "ERROR: invalid user, "
312 "%s/%s@%s from %016llx\n",
313 sname, host, realm, nid);
321 case LUSTRE_GSS_SVC_MGS:
322 if (!strcmp(sname, GSSD_SERVICE_OSS)) {
324 cred->cr_usr_oss = 1;
327 case LUSTRE_GSS_SVC_OSS:
328 if (!strcmp(sname, LUSTRE_ROOT_NAME)) {
330 cred->cr_usr_root = 1;
331 } else if (!strcmp(sname, GSSD_SERVICE_MDS)) {
333 cred->cr_usr_mds = 1;
335 if (cred->cr_uid == -1) {
336 printerr(0, "ERROR: svc %d doesn't accept user %s "
337 "from %016llx\n", lustre_svc, sname, nid);
348 printerr(1, "%s: authenticated %s%s%s@%s from %016llx\n",
349 lustre_svc_name[lustre_svc], sname,
350 host ? "/" : "", host ? host : "", realm, nid);
355 typedef struct gss_union_ctx_id_t {
357 gss_ctx_id_t internal_ctx_id;
358 } gss_union_ctx_id_desc, *gss_union_ctx_id_t;
360 int handle_sk(struct svc_nego_data *snd)
362 #ifdef HAVE_OPENSSL_SSK
363 struct sk_cred *skc = NULL;
364 struct svc_cred cred;
365 gss_buffer_desc bufs[7];
366 gss_buffer_desc remote_pub_key = GSS_C_EMPTY_BUFFER;
368 uint32_t rc = GSS_S_FAILURE;
373 printerr(3, "Handling sk request\n");
375 /* See lgss_sk_using_cred() for client side token
376 * bufs returned are in this order:
379 * bufs[2] - remote_pub_key
381 * bufs[4] - nodemap_hash
384 i = sk_decode_netstring(bufs, numbufs, &snd->in_tok);
386 printerr(0, "Invalid netstring token received from peer\n");
387 rc = GSS_S_DEFECTIVE_TOKEN;
391 /* target must be a null terminated string */
392 i = bufs[3].length - 1;
393 target = bufs[3].value;
394 if (i >= 0 && target[i] != '\0') {
395 printerr(0, "Invalid target from netstring\n");
396 for (i = 0; i < numbufs; i++)
401 memcpy(&flags, bufs[5].value, sizeof(flags));
402 skc = sk_create_cred(target, snd->nm_name, be32toh(flags));
404 printerr(0, "Failed to create sk credentials\n");
405 for (i = 0; i < numbufs; i++)
410 /* Take control of all the allocated buffers from decoding */
411 skc->sc_kctx.skc_iv = bufs[0];
413 remote_pub_key = bufs[2];
414 skc->sc_nodemap_hash = bufs[4];
415 skc->sc_hmac = bufs[6];
417 /* Verify that the peer has used a key size greater to or equal
418 * the size specified by the key file */
419 if (skc->sc_flags & LGSS_SVC_PRIV &&
420 skc->sc_p.length < skc->sc_session_keylen) {
421 printerr(0, "Peer DH parameters do not meet the size required "
426 /* Verify HMAC from peer. Ideally this would happen before anything
427 * else but we don't have enough information to lookup key without the
428 * token (fsname and cluster_hash) so it's done shortly after. */
429 rc = sk_verify_hmac(skc, bufs, numbufs - 1, EVP_sha256(),
433 if (rc != GSS_S_COMPLETE) {
434 printerr(0, "HMAC verification error: 0x%x from peer %s\n",
435 rc, libcfs_nid2str((lnet_nid_t) snd->nid));
439 /* Check that the cluster hash matches the hash of nodemap name */
440 rc = sk_verify_hash(snd->nm_name, EVP_sha256(), &skc->sc_nodemap_hash);
441 if (rc != GSS_S_COMPLETE) {
442 printerr(0, "Cluster hash failed validation: 0x%x\n", rc);
446 rc = sk_gen_params(skc, false);
447 if (rc != GSS_S_COMPLETE) {
448 printerr(0, "Failed to generate DH params for responder\n");
451 if (sk_compute_key(skc, &remote_pub_key)) {
452 printerr(0, "Failed to compute session key from DH params\n");
455 if (sk_kdf(skc, snd->nid, &snd->in_tok)) {
456 printerr(0, "Failed to calulate derviced session key\n");
459 if (sk_serialize_kctx(skc, &snd->ctx_token)) {
460 printerr(0, "Failed to serialize context for kernel\n");
464 /* Server reply only contains the servers public key and HMAC */
465 bufs[0] = skc->sc_pub_key;
466 if (sk_sign_bufs(&skc->sc_kctx.skc_shared_key, bufs, 1, EVP_sha256(),
468 printerr(0, "Failed to sign parameters\n");
471 bufs[1] = skc->sc_hmac;
472 if (sk_encode_netstring(bufs, 2, &snd->out_tok)) {
473 printerr(0, "Failed to encode netstring for token\n");
477 printerr(2, "Created netstring of %zd bytes\n", snd->out_tok.length);
479 snd->out_handle.length = sizeof(snd->handle_seq);
480 memcpy(snd->out_handle.value, &snd->handle_seq,
481 sizeof(snd->handle_seq));
482 snd->maj_stat = GSS_S_COMPLETE;
484 /* fix credentials */
485 memset(&cred, 0, sizeof(cred));
486 cred.cr_mapped_uid = -1;
488 if (skc->sc_flags & LGSS_ROOT_CRED_ROOT)
489 cred.cr_usr_root = 1;
490 if (skc->sc_flags & LGSS_ROOT_CRED_MDT)
492 if (skc->sc_flags & LGSS_ROOT_CRED_OST)
495 do_svc_downcall(&snd->out_handle, &cred, snd->mech, &snd->ctx_token);
497 /* cleanup ctx_token, out_tok is cleaned up in handle_channel_req */
498 free(remote_pub_key.value);
499 free(snd->ctx_token.value);
500 snd->ctx_token.length = 0;
502 printerr(3, "sk returning success\n");
507 if (remote_pub_key.value)
508 free(remote_pub_key.value);
509 if (snd->ctx_token.value)
510 free(snd->ctx_token.value);
511 snd->ctx_token.length = 0;
515 printerr(3, "sk returning failure\n");
516 #else /* !HAVE_OPENSSL_SSK */
517 printerr(0, "ERROR: shared key subflavour is not enabled\n");
518 #endif /* HAVE_OPENSSL_SSK */
522 int handle_null(struct svc_nego_data *snd)
524 struct svc_cred cred;
528 /* null just uses the same token as the return token and for
529 * for sending to the kernel. It is a single uint64_t. */
530 if (snd->in_tok.length != sizeof(uint64_t)) {
531 snd->maj_stat = GSS_S_DEFECTIVE_TOKEN;
532 printerr(0, "Invalid token size (%zd) received\n",
536 snd->out_tok.length = snd->in_tok.length;
537 snd->out_tok.value = malloc(snd->out_tok.length);
538 if (!snd->out_tok.value) {
539 snd->maj_stat = GSS_S_FAILURE;
540 printerr(0, "Failed to allocate out_tok\n");
544 snd->ctx_token.length = snd->in_tok.length;
545 snd->ctx_token.value = malloc(snd->ctx_token.length);
546 if (!snd->ctx_token.value) {
547 snd->maj_stat = GSS_S_FAILURE;
548 printerr(0, "Failed to allocate ctx_token\n");
552 snd->out_handle.length = sizeof(snd->handle_seq);
553 memcpy(snd->out_handle.value, &snd->handle_seq,
554 sizeof(snd->handle_seq));
555 snd->maj_stat = GSS_S_COMPLETE;
557 memcpy(&tmp, snd->in_tok.value, sizeof(tmp));
559 flags = (uint32_t)(tmp & 0x00000000ffffffff);
560 memset(&cred, 0, sizeof(cred));
561 cred.cr_mapped_uid = -1;
563 if (flags & LGSS_ROOT_CRED_ROOT)
564 cred.cr_usr_root = 1;
565 if (flags & LGSS_ROOT_CRED_MDT)
567 if (flags & LGSS_ROOT_CRED_OST)
570 do_svc_downcall(&snd->out_handle, &cred, snd->mech, &snd->ctx_token);
572 /* cleanup ctx_token, out_tok is cleaned up in handle_channel_req */
573 free(snd->ctx_token.value);
574 snd->ctx_token.length = 0;
579 static int handle_krb(struct svc_nego_data *snd)
582 gss_name_t client_name;
583 gss_buffer_desc ignore_out_tok = {.value = NULL};
584 gss_OID mech = GSS_C_NO_OID;
585 gss_cred_id_t svc_cred;
586 u_int32_t ignore_min_stat;
587 struct svc_cred cred;
589 svc_cred = gssd_select_svc_cred(snd->lustre_svc);
591 printerr(0, "no service credential for svc %u\n",
596 snd->maj_stat = gss_accept_sec_context(&snd->min_stat, &snd->ctx,
597 svc_cred, &snd->in_tok,
598 GSS_C_NO_CHANNEL_BINDINGS,
600 &snd->out_tok, &ret_flags, NULL,
603 if (snd->maj_stat == GSS_S_CONTINUE_NEEDED) {
604 printerr(1, "gss_accept_sec_context GSS_S_CONTINUE_NEEDED\n");
606 /* Save the context handle for future calls */
607 snd->out_handle.length = sizeof(snd->ctx);
608 memcpy(snd->out_handle.value, &snd->ctx, sizeof(snd->ctx));
610 } else if (snd->maj_stat != GSS_S_COMPLETE) {
611 printerr(0, "WARNING: gss_accept_sec_context failed\n");
612 pgsserr("handle_krb: gss_accept_sec_context",
613 snd->maj_stat, snd->min_stat, mech);
617 if (get_ids(client_name, mech, &cred, snd->nid, snd->lustre_svc)) {
618 /* get_ids() prints error msg */
619 snd->maj_stat = GSS_S_BAD_NAME; /* XXX ? */
620 gss_release_name(&ignore_min_stat, &client_name);
623 gss_release_name(&ignore_min_stat, &client_name);
625 /* Context complete. Pass handle_seq in out_handle to use
626 * for context lookup in the kernel. */
627 snd->out_handle.length = sizeof(snd->handle_seq);
628 memcpy(snd->out_handle.value, &snd->handle_seq,
629 sizeof(snd->handle_seq));
631 /* kernel needs ctx to calculate verifier on null response, so
632 * must give it context before doing null call: */
633 if (serialize_context_for_kernel(snd->ctx, &snd->ctx_token, mech)) {
634 printerr(0, "WARNING: handle_krb: "
635 "serialize_context_for_kernel failed\n");
636 snd->maj_stat = GSS_S_FAILURE;
639 /* We no longer need the gss context */
640 gss_delete_sec_context(&ignore_min_stat, &snd->ctx, &ignore_out_tok);
641 do_svc_downcall(&snd->out_handle, &cred, mech, &snd->ctx_token);
646 if (snd->ctx != GSS_C_NO_CONTEXT)
647 gss_delete_sec_context(&ignore_min_stat, &snd->ctx,
654 * return -1 only if we detect error during reading from upcall channel,
655 * all other cases return 0.
657 int handle_channel_request(FILE *f)
659 char in_tok_buf[TOKEN_BUF_SIZE];
660 char in_handle_buf[15];
661 char out_handle_buf[15];
662 gss_buffer_desc ctx_token = {.value = NULL},
663 null_token = {.value = NULL};
664 uint32_t lustre_mech;
670 u_int32_t ignore_min_stat;
671 struct svc_nego_data snd = {
672 .in_tok.value = in_tok_buf,
673 .in_handle.value = in_handle_buf,
674 .out_handle.value = out_handle_buf,
675 .maj_stat = GSS_S_FAILURE,
676 .ctx = GSS_C_NO_CONTEXT,
679 printerr(2, "handling request\n");
680 if (readline(fileno(f), &lbuf, &lbuflen) != 1) {
681 printerr(0, "WARNING: failed reading request\n");
687 /* see rsi_request() for the format of data being input here */
688 qword_get(&cp, (char *)&snd.lustre_svc, sizeof(snd.lustre_svc));
690 /* lustre_svc is the svc and gss subflavor */
691 lustre_mech = (snd.lustre_svc & LUSTRE_GSS_MECH_MASK) >>
692 LUSTRE_GSS_MECH_SHIFT;
693 snd.lustre_svc = snd.lustre_svc & LUSTRE_GSS_SVC_MASK;
694 switch (lustre_mech) {
697 printerr(1, "WARNING: Request for kerberos but service "
698 "support not enabled\n");
705 printerr(1, "WARNING: Request for gssnull but service "
706 "support not enabled\n");
712 #ifdef HAVE_OPENSSL_SSK
714 printerr(1, "WARNING: Request for sk but service "
715 "support not enabled\n");
720 printerr(1, "ERROR: Request for sk but service "
721 "support not enabled\n");
725 printerr(0, "WARNING: invalid mechanism recevied: %d\n",
731 qword_get(&cp, (char *)&snd.nid, sizeof(snd.nid));
732 qword_get(&cp, (char *)&snd.handle_seq, sizeof(snd.handle_seq));
733 qword_get(&cp, snd.nm_name, sizeof(snd.nm_name));
734 printerr(2, "handling req: svc %u, nid %016llx, idx %"PRIx64" nodemap "
735 "%s\n", snd.lustre_svc, snd.nid, snd.handle_seq, snd.nm_name);
737 get_len = qword_get(&cp, snd.in_handle.value, sizeof(in_handle_buf));
739 printerr(0, "WARNING: failed parsing request\n");
742 snd.in_handle.length = (size_t)get_len;
744 printerr(3, "in_handle:\n");
745 print_hexl(3, snd.in_handle.value, snd.in_handle.length);
747 get_len = qword_get(&cp, snd.in_tok.value, sizeof(in_tok_buf));
749 printerr(0, "WARNING: failed parsing request\n");
752 snd.in_tok.length = (size_t)get_len;
754 printerr(3, "in_tok:\n");
755 print_hexl(3, snd.in_tok.value, snd.in_tok.length);
757 if (snd.in_handle.length != 0) { /* CONTINUE_INIT case */
758 if (snd.in_handle.length != sizeof(snd.ctx)) {
759 printerr(0, "WARNING: input handle has unexpected "
760 "length %zu\n", snd.in_handle.length);
763 /* in_handle is the context id stored in the out_handle
764 * for the GSS_S_CONTINUE_NEEDED case below. */
765 memcpy(&snd.ctx, snd.in_handle.value, snd.in_handle.length);
768 if (lustre_mech == LGSS_MECH_KRB5)
769 rc = handle_krb(&snd);
770 else if (lustre_mech == LGSS_MECH_SK)
771 rc = handle_sk(&snd);
772 else if (lustre_mech == LGSS_MECH_NULL)
773 rc = handle_null(&snd);
775 printerr(0, "WARNING: Received or request for"
776 "subflavor that is not enabled: %d\n", lustre_mech);
779 /* Failures send a null token */
781 send_response(f, &snd.in_handle, &snd.in_tok, snd.maj_stat,
782 snd.min_stat, &snd.out_handle, &snd.out_tok);
784 send_response(f, &snd.in_handle, &snd.in_tok, snd.maj_stat,
785 snd.min_stat, &null_token, &null_token);
787 /* cleanup buffers */
788 if (snd.ctx_token.value != NULL)
789 free(ctx_token.value);
790 if (snd.out_tok.value != NULL)
791 gss_release_buffer(&ignore_min_stat, &snd.out_tok);
793 /* For junk wire data just ignore */