Annotation of src/usr.bin/telnet/auth.c, Revision 1.1
1.1 ! deraadt 1: /* $OpenBSD: auth.c,v 1.2 2003/06/02 19:38:24 millert Exp $ */
! 2:
! 3: /*-
! 4: * Copyright (c) 1991, 1993
! 5: * The Regents of the University of California. All rights reserved.
! 6: *
! 7: * Redistribution and use in source and binary forms, with or without
! 8: * modification, are permitted provided that the following conditions
! 9: * are met:
! 10: * 1. Redistributions of source code must retain the above copyright
! 11: * notice, this list of conditions and the following disclaimer.
! 12: * 2. Redistributions in binary form must reproduce the above copyright
! 13: * notice, this list of conditions and the following disclaimer in the
! 14: * documentation and/or other materials provided with the distribution.
! 15: * 3. Neither the name of the University nor the names of its contributors
! 16: * may be used to endorse or promote products derived from this software
! 17: * without specific prior written permission.
! 18: *
! 19: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
! 20: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 21: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 22: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
! 23: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 24: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 25: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 26: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 27: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 28: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 29: * SUCH DAMAGE.
! 30: */
! 31:
! 32: #ifndef lint
! 33: /* from: static char sccsid[] = "@(#)auth.c 8.3 (Berkeley) 5/30/95" */
! 34: /* from: static char *rcsid = "$NetBSD: auth.c,v 1.5 1996/02/24 01:15:17 jtk Exp $"; */
! 35: #endif /* not lint */
! 36:
! 37: /*
! 38: * This source code is no longer held under any constraint of USA
! 39: * `cryptographic laws' since it was exported legally. The cryptographic
! 40: * functions were removed from the code and a "Bones" distribution was
! 41: * made. A Commodity Jurisdiction Request #012-94 was filed with the
! 42: * USA State Department, who handed it to the Commerce department. The
! 43: * code was determined to fall under General License GTDA under ECCN 5D96G,
! 44: * and hence exportable. The cryptographic interfaces were re-added by Eric
! 45: * Young, and then KTH proceeded to maintain the code in the free world.
! 46: *
! 47: */
! 48:
! 49: /*
! 50: * Copyright (C) 1990 by the Massachusetts Institute of Technology
! 51: *
! 52: * Export of this software from the United States of America is assumed
! 53: * to require a specific license from the United States Government.
! 54: * It is the responsibility of any person or organization contemplating
! 55: * export to obtain such a license before exporting.
! 56: *
! 57: * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
! 58: * distribute this software and its documentation for any purpose and
! 59: * without fee is hereby granted, provided that the above copyright
! 60: * notice appear in all copies and that both that copyright notice and
! 61: * this permission notice appear in supporting documentation, and that
! 62: * the name of M.I.T. not be used in advertising or publicity pertaining
! 63: * to distribution of the software without specific, written prior
! 64: * permission. M.I.T. makes no representations about the suitability of
! 65: * this software for any purpose. It is provided "as is" without express
! 66: * or implied warranty.
! 67: */
! 68:
! 69: /* "$KTH: auth.c,v 1.23 2000/01/18 03:09:34 assar Exp $" */
! 70:
! 71: #if defined(AUTHENTICATION)
! 72: #include <stdio.h>
! 73: #include <sys/types.h>
! 74: #include <unistd.h>
! 75: #include <signal.h>
! 76: #define AUTH_NAMES
! 77: #include <arpa/telnet.h>
! 78: #include <stdlib.h>
! 79: #include <string.h>
! 80:
! 81: #include "encrypt.h"
! 82: #include "auth.h"
! 83: #include "misc-proto.h"
! 84: #include "auth-proto.h"
! 85:
! 86: #define typemask(x) (1<<((x)-1))
! 87:
! 88: #ifdef KRB4_ENCPWD
! 89: extern krb4encpwd_init();
! 90: extern krb4encpwd_send();
! 91: extern krb4encpwd_is();
! 92: extern krb4encpwd_reply();
! 93: extern krb4encpwd_status();
! 94: extern krb4encpwd_printsub();
! 95: #endif
! 96:
! 97: #ifdef RSA_ENCPWD
! 98: extern rsaencpwd_init();
! 99: extern rsaencpwd_send();
! 100: extern rsaencpwd_is();
! 101: extern rsaencpwd_reply();
! 102: extern rsaencpwd_status();
! 103: extern rsaencpwd_printsub();
! 104: #endif
! 105:
! 106: int auth_debug_mode = 0;
! 107: int auth_has_failed = 0;
! 108: int auth_enable_encrypt = 0;
! 109: static const char *Name = "Noname";
! 110: static int Server = 0;
! 111: static Authenticator *authenticated = 0;
! 112: static int authenticating = 0;
! 113: static int validuser = 0;
! 114: static unsigned char _auth_send_data[256];
! 115: static unsigned char *auth_send_data;
! 116: static int auth_send_cnt = 0;
! 117:
! 118: /*
! 119: * Authentication types supported. Plese note that these are stored
! 120: * in priority order, i.e. try the first one first.
! 121: */
! 122: Authenticator authenticators[] = {
! 123: #ifdef UNSAFE
! 124: { AUTHTYPE_UNSAFE, AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY,
! 125: unsafe_init,
! 126: unsafe_send,
! 127: unsafe_is,
! 128: unsafe_reply,
! 129: unsafe_status,
! 130: unsafe_printsub },
! 131: #endif
! 132: #ifdef SRA
! 133: { AUTHTYPE_SRA, AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY,
! 134: sra_init,
! 135: sra_send,
! 136: sra_is,
! 137: sra_reply,
! 138: sra_status,
! 139: sra_printsub },
! 140: #endif
! 141: #ifdef SPX
! 142: { AUTHTYPE_SPX, AUTH_WHO_CLIENT|AUTH_HOW_MUTUAL,
! 143: spx_init,
! 144: spx_send,
! 145: spx_is,
! 146: spx_reply,
! 147: spx_status,
! 148: spx_printsub },
! 149: { AUTHTYPE_SPX, AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY,
! 150: spx_init,
! 151: spx_send,
! 152: spx_is,
! 153: spx_reply,
! 154: spx_status,
! 155: spx_printsub },
! 156: #endif
! 157: #ifdef KRB5
! 158: { AUTHTYPE_KERBEROS_V5, AUTH_WHO_CLIENT|AUTH_HOW_MUTUAL,
! 159: kerberos5_init,
! 160: kerberos5_send_mutual,
! 161: kerberos5_is,
! 162: kerberos5_reply,
! 163: kerberos5_status,
! 164: kerberos5_printsub },
! 165: { AUTHTYPE_KERBEROS_V5, AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY,
! 166: kerberos5_init,
! 167: kerberos5_send_oneway,
! 168: kerberos5_is,
! 169: kerberos5_reply,
! 170: kerberos5_status,
! 171: kerberos5_printsub },
! 172: #endif
! 173: #ifdef KRB4
! 174: { AUTHTYPE_KERBEROS_V4, AUTH_WHO_CLIENT|AUTH_HOW_MUTUAL,
! 175: kerberos4_init,
! 176: kerberos4_send_mutual,
! 177: kerberos4_is,
! 178: kerberos4_reply,
! 179: kerberos4_status,
! 180: kerberos4_printsub },
! 181: { AUTHTYPE_KERBEROS_V4, AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY,
! 182: kerberos4_init,
! 183: kerberos4_send_oneway,
! 184: kerberos4_is,
! 185: kerberos4_reply,
! 186: kerberos4_status,
! 187: kerberos4_printsub },
! 188: #endif
! 189: #ifdef KRB4_ENCPWD
! 190: { AUTHTYPE_KRB4_ENCPWD, AUTH_WHO_CLIENT|AUTH_HOW_MUTUAL,
! 191: krb4encpwd_init,
! 192: krb4encpwd_send,
! 193: krb4encpwd_is,
! 194: krb4encpwd_reply,
! 195: krb4encpwd_status,
! 196: krb4encpwd_printsub },
! 197: #endif
! 198: #ifdef RSA_ENCPWD
! 199: { AUTHTYPE_RSA_ENCPWD, AUTH_WHO_CLIENT|AUTH_HOW_ONE_WAY,
! 200: rsaencpwd_init,
! 201: rsaencpwd_send,
! 202: rsaencpwd_is,
! 203: rsaencpwd_reply,
! 204: rsaencpwd_status,
! 205: rsaencpwd_printsub },
! 206: #endif
! 207: { 0, },
! 208: };
! 209:
! 210: static Authenticator NoAuth = { 0 };
! 211:
! 212: static int i_support = 0;
! 213: static int i_wont_support = 0;
! 214:
! 215: Authenticator *
! 216: findauthenticator(int type, int way)
! 217: {
! 218: Authenticator *ap = authenticators;
! 219:
! 220: while (ap->type && (ap->type != type || ap->way != way))
! 221: ++ap;
! 222: return(ap->type ? ap : 0);
! 223: }
! 224:
! 225: void
! 226: auth_init(const char *name, int server)
! 227: {
! 228: Authenticator *ap = authenticators;
! 229:
! 230: Server = server;
! 231: Name = name;
! 232:
! 233: i_support = 0;
! 234: authenticated = 0;
! 235: authenticating = 0;
! 236: while (ap->type) {
! 237: if (!ap->init || (*ap->init)(ap, server)) {
! 238: i_support |= typemask(ap->type);
! 239: if (auth_debug_mode)
! 240: printf(">>>%s: I support auth type %d %d\r\n",
! 241: Name,
! 242: ap->type, ap->way);
! 243: }
! 244: else if (auth_debug_mode)
! 245: printf(">>>%s: Init failed: auth type %d %d\r\n",
! 246: Name, ap->type, ap->way);
! 247: ++ap;
! 248: }
! 249: }
! 250:
! 251: void
! 252: auth_disable_name(char *name)
! 253: {
! 254: int x;
! 255: for (x = 0; x < AUTHTYPE_CNT; ++x) {
! 256: if (!strcasecmp(name, AUTHTYPE_NAME(x))) {
! 257: i_wont_support |= typemask(x);
! 258: break;
! 259: }
! 260: }
! 261: }
! 262:
! 263: int
! 264: getauthmask(char *type, int *maskp)
! 265: {
! 266: int x;
! 267:
! 268: if (!strcasecmp(type, AUTHTYPE_NAME(0))) {
! 269: *maskp = -1;
! 270: return(1);
! 271: }
! 272:
! 273: for (x = 1; x < AUTHTYPE_CNT; ++x) {
! 274: if (!strcasecmp(type, AUTHTYPE_NAME(x))) {
! 275: *maskp = typemask(x);
! 276: return(1);
! 277: }
! 278: }
! 279: return(0);
! 280: }
! 281:
! 282: int
! 283: auth_enable(char *type)
! 284: {
! 285: return(auth_onoff(type, 1));
! 286: }
! 287:
! 288: int
! 289: auth_disable(char *type)
! 290: {
! 291: return(auth_onoff(type, 0));
! 292: }
! 293:
! 294: int
! 295: auth_onoff(char *type, int on)
! 296: {
! 297: int i, mask = -1;
! 298: Authenticator *ap;
! 299:
! 300: if (!strcasecmp(type, "?") || !strcasecmp(type, "help")) {
! 301: printf("auth %s 'type'\n", on ? "enable" : "disable");
! 302: printf("Where 'type' is one of:\n");
! 303: printf("\t%s\n", AUTHTYPE_NAME(0));
! 304: mask = 0;
! 305: for (ap = authenticators; ap->type; ap++) {
! 306: if ((mask & (i = typemask(ap->type))) != 0)
! 307: continue;
! 308: mask |= i;
! 309: printf("\t%s\n", AUTHTYPE_NAME(ap->type));
! 310: }
! 311: return(0);
! 312: }
! 313:
! 314: if (!getauthmask(type, &mask)) {
! 315: printf("%s: invalid authentication type\n", type);
! 316: return(0);
! 317: }
! 318: if (on)
! 319: i_wont_support &= ~mask;
! 320: else
! 321: i_wont_support |= mask;
! 322: return(1);
! 323: }
! 324:
! 325: int
! 326: auth_togdebug(int on)
! 327: {
! 328: if (on < 0)
! 329: auth_debug_mode ^= 1;
! 330: else
! 331: auth_debug_mode = on;
! 332: printf("auth debugging %s\n", auth_debug_mode ? "enabled" : "disabled");
! 333: return(1);
! 334: }
! 335:
! 336: int
! 337: auth_status(void)
! 338: {
! 339: Authenticator *ap;
! 340: int i, mask;
! 341:
! 342: if (i_wont_support == -1)
! 343: printf("Authentication disabled\n");
! 344: else
! 345: printf("Authentication enabled\n");
! 346:
! 347: mask = 0;
! 348: for (ap = authenticators; ap->type; ap++) {
! 349: if ((mask & (i = typemask(ap->type))) != 0)
! 350: continue;
! 351: mask |= i;
! 352: printf("%s: %s\n", AUTHTYPE_NAME(ap->type),
! 353: (i_wont_support & typemask(ap->type)) ?
! 354: "disabled" : "enabled");
! 355: }
! 356: return(1);
! 357: }
! 358:
! 359: /*
! 360: * This routine is called by the server to start authentication
! 361: * negotiation.
! 362: */
! 363: void
! 364: auth_request(void)
! 365: {
! 366: static unsigned char str_request[64] = { IAC, SB,
! 367: TELOPT_AUTHENTICATION,
! 368: TELQUAL_SEND, };
! 369: Authenticator *ap = authenticators;
! 370: unsigned char *e = str_request + 4;
! 371:
! 372: if (!authenticating) {
! 373: authenticating = 1;
! 374: while (ap->type) {
! 375: if (i_support & ~i_wont_support & typemask(ap->type)) {
! 376: if (auth_debug_mode) {
! 377: printf(">>>%s: Sending type %d %d\r\n",
! 378: Name, ap->type, ap->way);
! 379: }
! 380: *e++ = ap->type;
! 381: *e++ = ap->way;
! 382: }
! 383: ++ap;
! 384: }
! 385: *e++ = IAC;
! 386: *e++ = SE;
! 387: telnet_net_write(str_request, e - str_request);
! 388: printsub('>', &str_request[2], e - str_request - 2);
! 389: }
! 390: }
! 391:
! 392: /*
! 393: * This is called when an AUTH SEND is received.
! 394: * It should never arrive on the server side (as only the server can
! 395: * send an AUTH SEND).
! 396: * You should probably respond to it if you can...
! 397: *
! 398: * If you want to respond to the types out of order (i.e. even
! 399: * if he sends LOGIN KERBEROS and you support both, you respond
! 400: * with KERBEROS instead of LOGIN (which is against what the
! 401: * protocol says)) you will have to hack this code...
! 402: */
! 403: void
! 404: auth_send(unsigned char *data, int cnt)
! 405: {
! 406: Authenticator *ap;
! 407: static unsigned char str_none[] = { IAC, SB, TELOPT_AUTHENTICATION,
! 408: TELQUAL_IS, AUTHTYPE_NULL, 0,
! 409: IAC, SE };
! 410: if (Server) {
! 411: if (auth_debug_mode) {
! 412: printf(">>>%s: auth_send called!\r\n", Name);
! 413: }
! 414: return;
! 415: }
! 416:
! 417: if (auth_debug_mode) {
! 418: printf(">>>%s: auth_send got:", Name);
! 419: printd(data, cnt); printf("\r\n");
! 420: }
! 421:
! 422: /*
! 423: * Save the data, if it is new, so that we can continue looking
! 424: * at it if the authorization we try doesn't work
! 425: */
! 426: if (data < _auth_send_data ||
! 427: data > _auth_send_data + sizeof(_auth_send_data)) {
! 428: auth_send_cnt = cnt > sizeof(_auth_send_data)
! 429: ? sizeof(_auth_send_data)
! 430: : cnt;
! 431: memmove(_auth_send_data, data, auth_send_cnt);
! 432: auth_send_data = _auth_send_data;
! 433: } else {
! 434: /*
! 435: * This is probably a no-op, but we just make sure
! 436: */
! 437: auth_send_data = data;
! 438: auth_send_cnt = cnt;
! 439: }
! 440: while ((auth_send_cnt -= 2) >= 0) {
! 441: if (auth_debug_mode)
! 442: printf(">>>%s: He supports %d\r\n",
! 443: Name, *auth_send_data);
! 444: if ((i_support & ~i_wont_support) & typemask(*auth_send_data)) {
! 445: ap = findauthenticator(auth_send_data[0],
! 446: auth_send_data[1]);
! 447: if (ap && ap->send) {
! 448: if (auth_debug_mode)
! 449: printf(">>>%s: Trying %d %d\r\n",
! 450: Name, auth_send_data[0],
! 451: auth_send_data[1]);
! 452: if ((*ap->send)(ap)) {
! 453: /*
! 454: * Okay, we found one we like
! 455: * and did it.
! 456: * we can go home now.
! 457: */
! 458: if (auth_debug_mode)
! 459: printf(">>>%s: Using type %d\r\n",
! 460: Name, *auth_send_data);
! 461: auth_send_data += 2;
! 462: return;
! 463: }
! 464: }
! 465: /* else
! 466: * just continue on and look for the
! 467: * next one if we didn't do anything.
! 468: */
! 469: }
! 470: auth_send_data += 2;
! 471: }
! 472: telnet_net_write(str_none, sizeof(str_none));
! 473: printsub('>', &str_none[2], sizeof(str_none) - 2);
! 474: if (auth_debug_mode)
! 475: printf(">>>%s: Sent failure message\r\n", Name);
! 476: auth_finished(0, AUTH_REJECT);
! 477: auth_has_failed = 1;
! 478: #ifdef KANNAN
! 479: /*
! 480: * We requested strong authentication, however no mechanisms worked.
! 481: * Therefore, exit on client end.
! 482: */
! 483: printf("Unable to securely authenticate user ... exit\n");
! 484: exit(0);
! 485: #endif /* KANNAN */
! 486: }
! 487:
! 488: void
! 489: auth_send_retry(void)
! 490: {
! 491: /*
! 492: * if auth_send_cnt <= 0 then auth_send will end up rejecting
! 493: * the authentication and informing the other side of this.
! 494: */
! 495: auth_send(auth_send_data, auth_send_cnt);
! 496: }
! 497:
! 498: void
! 499: auth_is(unsigned char *data, int cnt)
! 500: {
! 501: Authenticator *ap;
! 502:
! 503: if (cnt < 2)
! 504: return;
! 505:
! 506: if (data[0] == AUTHTYPE_NULL) {
! 507: auth_finished(0, AUTH_REJECT);
! 508: return;
! 509: }
! 510:
! 511: if ((ap = findauthenticator(data[0], data[1]))) {
! 512: if (ap->is)
! 513: (*ap->is)(ap, data+2, cnt-2);
! 514: } else if (auth_debug_mode)
! 515: printf(">>>%s: Invalid authentication in IS: %d\r\n",
! 516: Name, *data);
! 517: }
! 518:
! 519: void
! 520: auth_reply(unsigned char *data, int cnt)
! 521: {
! 522: Authenticator *ap;
! 523:
! 524: if (cnt < 2)
! 525: return;
! 526:
! 527: if ((ap = findauthenticator(data[0], data[1]))) {
! 528: if (ap->reply)
! 529: (*ap->reply)(ap, data+2, cnt-2);
! 530: } else if (auth_debug_mode)
! 531: printf(">>>%s: Invalid authentication in SEND: %d\r\n",
! 532: Name, *data);
! 533: }
! 534:
! 535: void
! 536: auth_name(unsigned char *data, int cnt)
! 537: {
! 538: char savename[256];
! 539:
! 540: if (cnt < 1) {
! 541: if (auth_debug_mode)
! 542: printf(">>>%s: Empty name in NAME\r\n", Name);
! 543: return;
! 544: }
! 545: if (cnt > sizeof(savename) - 1) {
! 546: if (auth_debug_mode)
! 547: printf(">>>%s: Name in NAME (%d) exceeds %lu length\r\n",
! 548: Name, cnt, (unsigned long)(sizeof(savename)-1));
! 549: return;
! 550: }
! 551: memmove(savename, data, cnt);
! 552: savename[cnt] = '\0'; /* Null terminate */
! 553: if (auth_debug_mode)
! 554: printf(">>>%s: Got NAME [%s]\r\n", Name, savename);
! 555: auth_encrypt_user(savename);
! 556: }
! 557:
! 558: int
! 559: auth_sendname(unsigned char *cp, int len)
! 560: {
! 561: static unsigned char str_request[256+6]
! 562: = { IAC, SB, TELOPT_AUTHENTICATION, TELQUAL_NAME, };
! 563: unsigned char *e = str_request + 4;
! 564: unsigned char *ee = &str_request[sizeof(str_request)-2];
! 565:
! 566: while (--len >= 0) {
! 567: if ((*e++ = *cp++) == IAC)
! 568: *e++ = IAC;
! 569: if (e >= ee)
! 570: return(0);
! 571: }
! 572: *e++ = IAC;
! 573: *e++ = SE;
! 574: telnet_net_write(str_request, e - str_request);
! 575: printsub('>', &str_request[2], e - &str_request[2]);
! 576: return(1);
! 577: }
! 578:
! 579: void
! 580: auth_finished(Authenticator *ap, int result)
! 581: {
! 582: if (!(authenticated = ap))
! 583: authenticated = &NoAuth;
! 584: validuser = result;
! 585: }
! 586:
! 587: /* ARGSUSED */
! 588: static void
! 589: auth_intr(int sig)
! 590: {
! 591: auth_finished(0, AUTH_REJECT);
! 592: }
! 593:
! 594: int
! 595: auth_wait(char *name, size_t name_sz)
! 596: {
! 597: if (auth_debug_mode)
! 598: printf(">>>%s: in auth_wait.\r\n", Name);
! 599:
! 600: if (Server && !authenticating)
! 601: return(0);
! 602:
! 603: signal(SIGALRM, auth_intr);
! 604: alarm(30);
! 605: while (!authenticated)
! 606: if (telnet_spin())
! 607: break;
! 608: alarm(0);
! 609: signal(SIGALRM, SIG_DFL);
! 610:
! 611: /*
! 612: * Now check to see if the user is valid or not
! 613: */
! 614: if (!authenticated || authenticated == &NoAuth)
! 615: return(AUTH_REJECT);
! 616:
! 617: if (validuser == AUTH_VALID)
! 618: validuser = AUTH_USER;
! 619:
! 620: if (authenticated->status)
! 621: validuser = (*authenticated->status)(authenticated,
! 622: name, name_sz,
! 623: validuser);
! 624: return(validuser);
! 625: }
! 626:
! 627: void
! 628: auth_debug(int mode)
! 629: {
! 630: auth_debug_mode = mode;
! 631: }
! 632:
! 633: void
! 634: auth_printsub(unsigned char *data, int cnt, unsigned char *buf, int buflen)
! 635: {
! 636: Authenticator *ap;
! 637:
! 638: if ((ap = findauthenticator(data[1], data[2])) && ap->printsub)
! 639: (*ap->printsub)(data, cnt, buf, buflen);
! 640: else
! 641: auth_gen_printsub(data, cnt, buf, buflen);
! 642: }
! 643:
! 644: void
! 645: auth_gen_printsub(unsigned char *data, int cnt, unsigned char *buf, int buflen)
! 646: {
! 647: unsigned char *cp;
! 648: unsigned char tbuf[16];
! 649:
! 650: cnt -= 3;
! 651: data += 3;
! 652: buf[buflen-1] = '\0';
! 653: buf[buflen-2] = '*';
! 654: buflen -= 2;
! 655: for (; cnt > 0; cnt--, data++) {
! 656: snprintf(tbuf, sizeof(tbuf), " %d", *data);
! 657: for (cp = tbuf; *cp && buflen > 0; --buflen)
! 658: *buf++ = *cp++;
! 659: if (buflen <= 0)
! 660: return;
! 661: }
! 662: *buf = '\0';
! 663: }
! 664: #endif