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