1 /* $OpenBSD: login_tis.c,v 1.3 2005/03/08 22:02:08 cloder Exp $ */
2
3 /*
4 * Copyright (c) 2004 Todd C. Miller <Todd.Miller@courtesan.com>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <sys/param.h>
20 #include <sys/socket.h>
21 #include <sys/stat.h>
22 #include <sys/time.h>
23 #include <sys/uio.h>
24 #include <sys/resource.h>
25
26 #include <ctype.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <netdb.h>
30 #include <paths.h>
31 #include <pwd.h>
32 #include <readpassphrase.h>
33 #include <signal.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <syslog.h>
38 #include <unistd.h>
39
40 #include <login_cap.h>
41 #include <bsd_auth.h>
42 #include <des.h> /* openssl/des.h */
43
44 #include "login_tis.h"
45
46 #define MODE_LOGIN 0
47 #define MODE_CHALLENGE 1
48 #define MODE_RESPONSE 2
49
50 enum response_type {
51 error,
52 password,
53 challenge,
54 chalnecho,
55 display
56 };
57
58 ssize_t tis_recv(struct tis_connection *, u_char *, size_t);
59 ssize_t tis_send(struct tis_connection *, u_char *, size_t);
60 void quit(int);
61 void send_fd(struct tis_connection *, int);
62 void tis_getconf(struct tis_connection *, char *);
63 int tis_decode(u_char *, size_t);
64 int tis_encode(u_char *, size_t, size_t);
65 int tis_getkey(struct tis_connection *);
66 int tis_open(struct tis_connection *, const char *, char *);
67 int tis_verify(struct tis_connection *, const char *, char *);
68 enum response_type tis_authorize(struct tis_connection *, const char *,
69 const char *, char *);
70
71 int
main(int argc,char * argv[])72 main(int argc, char *argv[])
73 {
74 struct tis_connection tc;
75 struct passwd *pw;
76 struct sigaction sa;
77 sigset_t sigset;
78 uid_t uid;
79 enum response_type rtype = error;
80 FILE *back = NULL;
81 char *user = NULL, *class = NULL, *cp, *ep;
82 char chalbuf[TIS_BUFSIZ], respbuf[TIS_BUFSIZ], ebuf[TIS_BUFSIZ];
83 int ch, mode = MODE_LOGIN;
84
85 (void)setpriority(PRIO_PROCESS, 0, 0);
86
87 (void)sigemptyset(&sigset);
88 (void)sigaddset(&sigset, SIGSTOP);
89 (void)sigaddset(&sigset, SIGTSTP);
90 (void)sigaddset(&sigset, SIGTTIN);
91 (void)sigaddset(&sigset, SIGTTOU);
92 (void)sigprocmask(SIG_BLOCK, &sigset, NULL);
93
94 memset(&sa, 0, sizeof(sa));
95 (void)sigemptyset(&sa.sa_mask);
96 sa.sa_handler = quit;
97 (void)sigaction(SIGINT, &sa, NULL);
98 (void)sigaction(SIGQUIT, &sa, NULL);
99 (void)sigaction(SIGALRM, &sa, NULL);
100
101 openlog(NULL, LOG_ODELAY, LOG_AUTH);
102
103 tc.fd = -1;
104 while ((ch = getopt(argc, argv, "ds:v:")) != -1) {
105 switch (ch) {
106 case 'd':
107 back = stdout;
108 break;
109 case 's': /* service */
110 if (strcmp(optarg, "login") == 0)
111 mode = MODE_LOGIN;
112 else if (strcmp(optarg, "challenge") == 0)
113 mode = MODE_CHALLENGE;
114 else if (strcmp(optarg, "response") == 0)
115 mode = MODE_RESPONSE;
116 else {
117 syslog(LOG_ERR, "%s: invalid service", optarg);
118 exit(1);
119 }
120 break;
121 case 'v':
122 if (strncmp(optarg, "fd=", 3) == 0) {
123 const char *errstr;
124 tc.fd =
125 strtonum(optarg + 3, 0, INT_MAX, &errstr);
126 if (errstr != NULL) {
127 syslog(LOG_ERR, "fd is %s: %s",
128 errstr, optarg + 3);
129 tc.fd = -1;
130 }
131 }
132 /* silently ignore unsupported variables */
133 break;
134 default:
135 syslog(LOG_ERR, "usage error");
136 exit(1);
137 }
138 }
139 argc -= optind;
140 argv += optind;
141
142 switch (argc) {
143 case 2:
144 user = argv[0];
145 class = argv[1];
146 break;
147 case 1:
148 /* class is not really optional so check passwd(5) entry */
149 user = argv[0];
150 if ((pw = getpwnam(user)) != NULL && pw->pw_class != NULL)
151 class = strdup(pw->pw_class);
152 break;
153 default:
154 syslog(LOG_ERR, "usage error");
155 exit(1);
156 }
157
158 if (back == NULL && (back = fdopen(3, "r+")) == NULL) {
159 syslog(LOG_ERR, "reopening back channel: %m");
160 exit(1);
161 }
162
163 tis_getconf(&tc, class);
164 if (tc.keyfile != NULL && tis_getkey(&tc) != 0)
165 exit(1);
166
167 /* no longer need special perms */
168 if ((uid = getuid()) != geteuid()) {
169 seteuid(uid);
170 setuid(uid);
171 }
172
173 if (tc.fd == -1) {
174 if (tis_open(&tc, tc.server, ebuf) == -1 && (!tc.altserver ||
175 tis_open(&tc, tc.altserver, ebuf) == -1)) {
176 syslog(LOG_ERR, "unable to connect to authsrv: %s",
177 ebuf);
178 exit(1);
179 }
180 if ((rtype = tis_authorize(&tc, user, class, chalbuf)) == error)
181 exit(1);
182 }
183
184 switch (mode) {
185 case MODE_LOGIN:
186 if (rtype == display) {
187 printf("%s", chalbuf);
188 exit(1);
189 }
190 alarm(TIS_PASSWD_TIMEOUT);
191 if (!readpassphrase(chalbuf, respbuf, sizeof(respbuf),
192 rtype == challenge ? RPP_ECHO_ON : RPP_ECHO_OFF))
193 exit(1);
194 alarm(0);
195 break;
196
197 case MODE_CHALLENGE:
198 switch (rtype) {
199 case display:
200 (void)fprintf(back, "value errormsg %s\n",
201 auth_mkvalue(chalbuf));
202 exit(1);
203 case password:
204 fprintf(back, BI_SILENT "\n");
205 break;
206 default:
207 /* XXX - should distinguish chalnecho from challenge */
208 fprintf(back, BI_VALUE " challenge %s\n",
209 auth_mkvalue(chalbuf));
210 fprintf(back, BI_CHALLENGE "\n");
211 }
212 fprintf(back, BI_FDPASS "\n");
213 fflush(back);
214 send_fd(&tc, fileno(back));
215 exit(0);
216
217 case MODE_RESPONSE:
218 /* read challenge from backchannel */
219 mode = -1;
220 cp = chalbuf;
221 ep = chalbuf + sizeof(chalbuf);
222 while (cp < ep && read(fileno(back), cp, 1) == 1) {
223 if (*cp++ == '\0') {
224 mode = MODE_CHALLENGE;
225 break;
226 }
227 }
228 if (mode != MODE_CHALLENGE) {
229 syslog(LOG_ERR,
230 "protocol error: bad/missing challenge");
231 exit(1);
232 }
233 if (rtype == error) {
234 /* didn't read the challenge ourselves so just guess */
235 if (strcmp(chalbuf, "Password: ") == 0)
236 rtype = password;
237 else
238 rtype = challenge;
239 }
240
241 /* read user's response from backchannel */
242 cp = respbuf;
243 ep = respbuf + sizeof(respbuf);
244 while (cp < ep && read(fileno(back), cp, 1) == 1) {
245 if (*cp++ == '\0') {
246 mode = MODE_RESPONSE;
247 break;
248 }
249 }
250 if (mode != MODE_RESPONSE) {
251 syslog(LOG_ERR,
252 "protocol error: bad/missing response");
253 exit(1);
254 }
255 break;
256 }
257
258 if (tis_verify(&tc, respbuf, ebuf) == 0) {
259 if (ebuf[0] != '\0')
260 (void)fprintf(back, "value errormsg %s\n",
261 auth_mkvalue(ebuf));
262 fprintf(back, BI_AUTH "\n");
263 if (rtype == challenge)
264 fprintf(back, BI_SECURE "\n");
265 exit(0);
266 }
267 if (ebuf[0] != '\0')
268 (void)fprintf(back, "value errormsg %s\n", auth_mkvalue(ebuf));
269 fprintf(back, BI_REJECT "\n");
270 exit(1);
271 }
272
273 void
quit(int signo)274 quit(int signo)
275 {
276 struct syslog_data data;
277
278 if (signo == SIGALRM)
279 syslog_r(LOG_ERR, &data, "timed out talking to authsrv");
280 _exit(1);
281 }
282
283 /*
284 * Send the file descriptor in struct tis_connection over a socketpair
285 * to the parent process to keep the connection to authsrv open.
286 */
287 void
send_fd(struct tis_connection * tc,int sock)288 send_fd(struct tis_connection *tc, int sock)
289 {
290 struct msghdr msg;
291 struct cmsghdr *cmp;
292 char cmsgbuf[CMSG_SPACE(sizeof(int))];
293
294 memset(&msg, 0, sizeof(msg));
295 msg.msg_control = cmsgbuf;
296 msg.msg_controllen = CMSG_LEN(sizeof(int));
297
298 cmp = CMSG_FIRSTHDR(&msg);
299 cmp->cmsg_len = CMSG_LEN(sizeof(int));
300 cmp->cmsg_level = SOL_SOCKET;
301 cmp->cmsg_type = SCM_RIGHTS;
302
303 *(int *)CMSG_DATA(cmp) = tc->fd;
304
305 if (sendmsg(sock, &msg, 0) < 0)
306 syslog(LOG_ERR, "sendmsg: %m");
307 }
308
309 /*
310 * Look up the given login class and populate struct tis_connection.
311 */
312 void
tis_getconf(struct tis_connection * tc,char * class)313 tis_getconf(struct tis_connection *tc, char *class)
314 {
315 login_cap_t *lc;
316
317 if ((lc = login_getclass(class)) == NULL) {
318 tc->port = TIS_DEFPORT;
319 tc->timeout = TIS_DEFTIMEOUT;
320 tc->server = TIS_DEFSERVER;
321 tc->altserver = NULL;
322 tc->keyfile = NULL;
323 return;
324 }
325
326 tc->port = login_getcapstr(lc, "tis-port", TIS_DEFPORT, TIS_DEFPORT);
327 tc->timeout = login_getcapnum(lc, "tis-timeout", TIS_DEFTIMEOUT,
328 TIS_DEFTIMEOUT);
329 tc->server = login_getcapstr(lc, "tis-server", TIS_DEFSERVER,
330 TIS_DEFSERVER);
331 tc->altserver = login_getcapstr(lc, "tis-server-alt", NULL, NULL);
332 tc->keyfile = login_getcapstr(lc, "tis-keyfile", NULL, NULL);
333 }
334
335 /*
336 * Read an ASCII string from a file and convert it to a DES key.
337 */
338 int
tis_getkey(struct tis_connection * tc)339 tis_getkey(struct tis_connection *tc)
340 {
341 size_t len;
342 struct stat sb;
343 des_cblock cblock;
344 char *key, *tbuf = NULL;
345 FILE *fp;
346 int error;
347
348 if ((fp = fopen(tc->keyfile, "r")) == NULL) {
349 syslog(LOG_ERR, "%s: %m", tc->keyfile);
350 return (-1);
351 }
352 if (fstat(fileno(fp), &sb) == -1) {
353 syslog(LOG_ERR, "%s: %m", tc->keyfile);
354 fclose(fp);
355 return (-1);
356 }
357 if (sb.st_uid != 0) {
358 syslog(LOG_ERR, "%s: not owned by root", tc->keyfile);
359 fclose(fp);
360 return (-1);
361 }
362 if (!S_ISREG(sb.st_mode)) {
363 syslog(LOG_ERR, "%s: not a regular file", tc->keyfile);
364 fclose(fp);
365 return (-1);
366 }
367 if (sb.st_mode & (S_IRWXG|S_IRWXO)) {
368 syslog(LOG_ERR, "%s: readable or writable by non-owner",
369 tc->keyfile);
370 fclose(fp);
371 return (-1);
372 }
373 if ((key = fgetln(fp, &len)) == NULL || (len == 1 && key[0] == '\n')) {
374 if (ferror(fp))
375 syslog(LOG_ERR, "%s: %m", tc->keyfile);
376 else
377 syslog(LOG_ERR, "%s: empty key file", tc->keyfile);
378 fclose(fp);
379 return (-1);
380 }
381 fclose(fp);
382 if (key[len - 1] == '\n')
383 key[--len] = '\0';
384 else {
385 if ((tbuf = malloc(len + 1)) == NULL) {
386 syslog(LOG_ERR, "%s: %m", tc->keyfile);
387 return (-1);
388 }
389 memcpy(tbuf, key, len);
390 tbuf[len] = '\0';
391 key = tbuf;
392 }
393 des_string_to_key(key, &cblock);
394 error = des_set_key(&cblock, tc->keysched);
395 memset(key, 0, len);
396 memset(&cblock, 0, sizeof(cblock));
397 free(tbuf);
398 return (error);
399 }
400
401 /*
402 * Open a connection to authsrv and read the welcom banner.
403 * Returns the file descriptor on success and -1 on error
404 * or unrecognized welcome banner.
405 */
406 int
tis_open(struct tis_connection * tc,const char * server,char * ebuf)407 tis_open(struct tis_connection *tc, const char *server, char *ebuf)
408 {
409 struct addrinfo hints, *res, *res0;
410 char buf[TIS_BUFSIZ];
411 int error;
412
413 ebuf[0] = '\0';
414 memset(&hints, 0, sizeof(hints));
415 hints.ai_socktype = SOCK_STREAM;
416 hints.ai_family = PF_UNSPEC;
417 hints.ai_flags = 0;
418 error = getaddrinfo(server, tc->port, &hints, &res0);
419 if (error) {
420 strlcpy(ebuf, gai_strerror(error), TIS_BUFSIZ);
421 freeaddrinfo(res0);
422 return (-1);
423 }
424
425 /* connect to the first address that succeeds */
426 for (res = res0; res != NULL; res = res->ai_next) {
427 tc->fd = socket(res->ai_family, res->ai_socktype,
428 res->ai_protocol);
429 if (tc->fd != -1) {
430 if (connect(tc->fd, res->ai_addr, res->ai_addrlen) == 0)
431 break;
432 close(tc->fd);
433 }
434 }
435 if (res == NULL) {
436 strlcpy(ebuf, strerror(errno), TIS_BUFSIZ);
437 freeaddrinfo(res0);
438 tc->fd = -1;
439 return (-1);
440 }
441 freeaddrinfo(res0);
442
443 /* read welcome banner */
444 if (tis_recv(tc, buf, sizeof(buf)) == -1) {
445 strlcpy(ebuf, strerror(errno), TIS_BUFSIZ);
446 close(tc->fd);
447 tc->fd = -1;
448 return (-1);
449 }
450 if (strncmp(buf, "Authsrv ready", 13) != 0) {
451 strlcpy(ebuf, buf, TIS_BUFSIZ);
452 close(tc->fd);
453 tc->fd = -1;
454 return (-1);
455 }
456
457 return (tc->fd);
458 }
459
460 /*
461 * Read a one-line response from authsrv.
462 * On success, returns 0. On error, returns non-zero and calls syslog().
463 */
464 ssize_t
tis_recv(struct tis_connection * tc,u_char * buf,size_t bufsiz)465 tis_recv(struct tis_connection *tc, u_char *buf, size_t bufsiz)
466 {
467 des_key_schedule ks;
468 des_cblock iv;
469 ssize_t nread;
470 size_t len;
471 u_char *cp, *ep, tbuf[TIS_BUFSIZ];
472
473 for (cp = buf, ep = buf + bufsiz; cp < ep; cp++) {
474 alarm(tc->timeout);
475 nread = read(tc->fd, cp, 1);
476 alarm(0);
477 if (nread != 1) {
478 if (nread == -1)
479 syslog(LOG_ERR,
480 "error reading data from authsrv: %m");
481 else
482 syslog(LOG_ERR, "EOF reading data from authsrv");
483 return (nread);
484 }
485 if (*cp == '\n')
486 break;
487 }
488 if (*cp != '\n') {
489 syslog(LOG_ERR, "server response too large");
490 return (-1);
491 }
492 *cp = '\0';
493 len = cp - buf;
494
495 if (tc->keyfile != NULL) {
496 if ((len = tis_decode(buf, len)) < 0) {
497 syslog(LOG_ERR, "improperly encoded data from authsrv");
498 return (-1);
499 }
500 if (len > sizeof(tbuf)) {
501 syslog(LOG_ERR, "encrypted data too large to store");
502 return (-1);
503 }
504 memcpy(ks, tc->keysched, sizeof(ks));
505 memset(iv, 0, sizeof(iv));
506 des_ncbc_encrypt((des_cblock *)buf, (des_cblock *)tbuf,
507 len, ks, &iv, DES_DECRYPT);
508 if (strlcpy(buf, tbuf, bufsiz) >= bufsiz) {
509 syslog(LOG_ERR, "unencrypted data too large to store");
510 memset(tbuf, 0, sizeof(tbuf));
511 return (-1);
512 }
513 memset(tbuf, 0, sizeof(tbuf));
514 }
515 return (len);
516 }
517
518 /*
519 * Send a line to authsrv.
520 * On success, returns 0. On error, returns non-zero and calls syslog().
521 */
522 ssize_t
tis_send(struct tis_connection * tc,u_char * buf,size_t len)523 tis_send(struct tis_connection *tc, u_char *buf, size_t len)
524 {
525 struct iovec iov[2];
526 des_key_schedule ks;
527 des_cblock iv;
528 ssize_t nwritten;
529 size_t n;
530 u_char cbuf[TIS_BUFSIZ];
531
532 if (tc->keyfile != NULL) {
533 memcpy(ks, tc->keysched, sizeof(ks));
534 memset(iv, 0, sizeof(iv));
535
536 len++; /* we need to encrypt the NUL */
537 if ((n = len % 8) != 0)
538 len += 8 - n; /* make multiple of 8 bytes */
539 if (len * 2 > sizeof(cbuf)) {
540 syslog(LOG_ERR, "encoded data too large to store");
541 return (-1);
542 }
543 des_ncbc_encrypt((des_cblock *)buf, (des_cblock *)cbuf, len,
544 ks, &iv, DES_ENCRYPT);
545 len = tis_encode(cbuf, len, sizeof(cbuf));
546 buf = cbuf;
547 }
548 iov[0].iov_base = buf;
549 iov[0].iov_len = len;
550 iov[1].iov_base = "\n";
551 iov[1].iov_len = 1;
552
553 alarm(tc->timeout);
554 nwritten = writev(tc->fd, iov, 2);
555 alarm(0);
556 if (nwritten != len + 1) {
557 if (nwritten < 0)
558 syslog(LOG_ERR, "error writing to authsrv: %m");
559 else
560 syslog(LOG_ERR, "short write sending to authsrv");
561 return (-1);
562 }
563 return (nwritten - 1); /* don't include the newline */
564 }
565
566 /*
567 * Convert a stream of bytes hex digits to hex octects in place.
568 * The passed in buffer must have space for len*2 bytes
569 * plus a NUL.
570 */
571 int
tis_encode(u_char * buf,size_t inlen,size_t bufsiz)572 tis_encode(u_char *buf, size_t inlen, size_t bufsiz)
573 {
574 u_char *in, *out;
575 size_t outlen;
576 const static char hextab[] = "0123456789ABCDEF";
577
578 outlen = inlen * 2;
579 if (bufsiz <= outlen)
580 return (-1);
581
582 /* convert from the end -> beginning so we can do it in place */
583 for (in = &buf[inlen - 1], out = &buf[outlen - 1]; in >= buf; in--) {
584 *out-- = hextab[*in & 0x0f];
585 *out-- = hextab[*in >> 4];
586 }
587 buf[outlen] = '\0';
588
589 return (outlen);
590 }
591
592 /*
593 * Convert a stream of hex digits to bytes in place.
594 */
595 int
tis_decode(u_char * buf,size_t len)596 tis_decode(u_char *buf, size_t len)
597 {
598 u_char *end, *in, *out;
599 int byte;
600
601 if (len & 1)
602 return (-1); /* len must be even */
603
604 for (in = out = buf, end = buf + len; in < end; in += 2) {
605 if (in[1] >= 'A' && in[1] <= 'F')
606 byte = in[1] - 'A' + 10;
607 else
608 byte = in[1] - '0';
609 if (in[0] >= 'A' && in[0] <= 'F')
610 byte += (in[0] - 'A' + 10) * 16;
611 else
612 byte += (in[0] - '0') * 16;
613 if (byte > 0xff || byte < 0)
614 return (-1);
615 *out++ = byte;
616 }
617 *out = '\0';
618 return (out - buf);
619 }
620
621 /*
622 * Send an authorization string to authsrv and check the result.
623 * Returns the type of response and an output buffer.
624 */
625 enum response_type
tis_authorize(struct tis_connection * tc,const char * user,const char * class,char * obuf)626 tis_authorize(struct tis_connection *tc, const char *user,
627 const char *class, char *obuf)
628 {
629 enum response_type resp;
630 u_char buf[TIS_BUFSIZ];
631 int len;
632
633 *obuf = '\0';
634 /* class is not used by authsrv (it is effectively a comment) */
635 len = snprintf(buf, sizeof(buf), "authenticate %s %s", user, class);
636 if (len == -1 || len >= sizeof(buf)) {
637 syslog(LOG_ERR, "user/class too large");
638 resp = error;
639 } else if (tis_send(tc, buf, len) < 0)
640 resp = error;
641 else if (tis_recv(tc, buf, sizeof(buf)) < 0)
642 resp = error;
643 else if (strncmp(buf, "password", 8) == 0) {
644 strlcpy(obuf, "Password: ", TIS_BUFSIZ);
645 resp = password;
646 } else if (strncmp(buf, "challenge ", 10) == 0) {
647 strlcpy(obuf, buf + 10, TIS_BUFSIZ);
648 resp = challenge;
649 } else if (strncmp(buf, "chalnecho ", 10) == 0) {
650 strlcpy(obuf, buf + 10, TIS_BUFSIZ);
651 resp = chalnecho;
652 } else if (strncmp(buf, "display ", 8) == 0) {
653 strlcpy(obuf, buf, TIS_BUFSIZ);
654 resp = display;
655 } else {
656 syslog(LOG_ERR, "unexpected response from authsrv: %s", obuf);
657 resp = error;
658 }
659 memset(buf, 0, sizeof(buf));
660
661 return (resp);
662 }
663
664 /*
665 * Send a response string to authsrv and check the result.
666 * Returns the type of response, and an error buffer.
667 */
668 int
tis_verify(struct tis_connection * tc,const char * response,char * ebuf)669 tis_verify(struct tis_connection *tc, const char *response, char *ebuf)
670 {
671 u_char buf[TIS_BUFSIZ];
672 int len;
673
674 ebuf[0] = '\0';
675 len = snprintf(buf, sizeof(buf), "response '%s'", response);
676 if (len == -1 || len >= sizeof(buf)) {
677 syslog(LOG_ERR, "response too large");
678 return (-1);
679 }
680 if (tis_send(tc, buf, len) < 0)
681 return (-1);
682 if (tis_recv(tc, buf, sizeof(buf)) < 0)
683 return (-1);
684 if (strncmp(buf, "ok", 2) == 0) {
685 if (buf[2] != '\0')
686 strlcpy(ebuf, buf + 3, TIS_BUFSIZ);
687 memset(buf, 0, sizeof(buf));
688 return (0);
689 }
690 strlcpy(ebuf, buf, TIS_BUFSIZ);
691 memset(buf, 0, sizeof(buf));
692 return (-1);
693 }
694