xref: /illumos-gate/usr/src/cmd/bnu/in.uucpd.c (revision 2a8bcb4e)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * 4.2BSD, 2.9BSD, or ATTSVR4 TCP/IP server for uucico
28  * uucico's TCP channel causes this server to be run at the remote end.
29  */
30 
31 #include "uucp.h"
32 #include <netdb.h>
33 #ifdef BSD2_9
34 #include <sys/localopts.h>
35 #include <sys/file.h>
36 #endif	/* BSD2_9 */
37 #include <signal.h>
38 #include <errno.h>
39 #include <sys/socket.h>
40 #include <netinet/in.h>
41 #include <sys/wait.h>
42 #ifdef ATTSVTTY
43 #include <sys/termio.h>
44 #else
45 #include <sys/ioctl.h>
46 #endif
47 #include <pwd.h>
48 #ifdef ATTSVR4
49 #include <shadow.h>
50 #endif
51 #include <lastlog.h>
52 
53 #include <security/pam_appl.h>
54 
55 static int uucp_conv();
56 struct pam_conv conv = {uucp_conv, NULL };
57 pam_handle_t    *pamh;
58 
59 #if !defined(BSD4_2) && !defined(BSD2_9) && !defined(ATTSVR4)
60 --- You must have either BSD4_2, BSD2_9, or ATTSVR4 defined for this to work
61 #endif	/* !BSD4_2 && !BSD2_9 */
62 #if defined(BSD4_2) && defined(BSD2_9)
63 --- You may not have both BSD4_2 and BSD2_9 defined for this to work
64 #endif	/* check for stupidity */
65 
66 char lastlog[] = "/var/adm/lastlog";
67 struct	passwd nouser = {
68 	"", "nope", (uid_t)-1, (gid_t)-1, "", "", "", "", "" };
69 #ifdef ATTSVR4
70 struct	spwd noupass = { "", "nope" };
71 #endif
72 struct	sockaddr_in hisctladdr;
73 socklen_t hisaddrlen = (socklen_t)sizeof (hisctladdr);
74 struct	sockaddr_in myctladdr;
75 int nolog;		/* don't log in utmp or wtmp */
76 
77 char Username[64];
78 char Loginname[64];
79 char *nenv[] = {
80 	Username,
81 	Loginname,
82 	NULL,
83 };
84 extern char **environ;
85 
86 static void doit(struct sockaddr_in *);
87 static void dologout(void);
88 
89 int
main(argc,argv)90 main(argc, argv)
91 int argc;
92 char **argv;
93 {
94 #ifndef BSDINETD
95 	int s, tcp_socket;
96 	struct servent *sp;
97 #endif	/* !BSDINETD */
98 	extern int errno;
99 
100 	if (argc > 1 && strcmp(argv[1], "-n") == 0)
101 		nolog = 1;
102 	environ = nenv;
103 #ifdef BSDINETD
104 	close(1); close(2);
105 	dup(0); dup(0);
106 	hisaddrlen = (socklen_t)sizeof (hisctladdr);
107 	if (getpeername(0, (struct sockaddr *)&hisctladdr, &hisaddrlen) < 0) {
108 		fprintf(stderr, "%s: ", argv[0]);
109 		perror("getpeername");
110 		_exit(1);
111 	}
112 	if (fork() == 0)
113 		doit(&hisctladdr);
114 	dologout();
115 	exit(1);
116 #else	/* !BSDINETD */
117 	sp = getservbyname("uucp", "tcp");
118 	if (sp == NULL) {
119 		perror("uucpd: getservbyname");
120 		exit(1);
121 	}
122 	if (fork())
123 		exit(0);
124 #ifdef ATTSVR4
125 	setsid();
126 #else
127 	if ((s = open("/dev/tty", 2)) >= 0) {
128 		ioctl(s, TIOCNOTTY, (char *)0);
129 		close(s);
130 	}
131 #endif
132 
133 #ifdef ATTSVR4
134 	memset((void *)&myctladdr, 0, sizeof (myctladdr));
135 #else
136 	bzero((char *)&myctladdr, sizeof (myctladdr));
137 #endif
138 	myctladdr.sin_family = AF_INET;
139 	myctladdr.sin_port = sp->s_port;
140 #if defined(BSD4_2) || defined(ATTSVR4)
141 	tcp_socket = socket(AF_INET, SOCK_STREAM, 0);
142 	if (tcp_socket < 0) {
143 		perror("uucpd: socket");
144 		exit(1);
145 	}
146 	if (bind(tcp_socket, (char *)&myctladdr, sizeof (myctladdr)) < 0) {
147 		perror("uucpd: bind");
148 		exit(1);
149 	}
150 	listen(tcp_socket, 3);	/* at most 3 simultaneuos uucp connections */
151 	signal(SIGCHLD, dologout);
152 
153 	for (;;) {
154 		s = accept(tcp_socket, &hisctladdr, &hisaddrlen);
155 		if (s < 0) {
156 			if (errno == EINTR)
157 				continue;
158 			perror("uucpd: accept");
159 			exit(1);
160 		}
161 		if (fork() == 0) {
162 			close(0); close(1); close(2);
163 			dup(s); dup(s); dup(s);
164 			close(tcp_socket); close(s);
165 			doit(&hisctladdr);
166 			exit(1);
167 		}
168 		close(s);
169 	}
170 #endif	/* BSD4_2 */
171 
172 #ifdef BSD2_9
173 	for (;;) {
174 		signal(SIGCHLD, dologout);
175 		s = socket(SOCK_STREAM, 0,  &myctladdr,
176 			SO_ACCEPTCONN|SO_KEEPALIVE);
177 		if (s < 0) {
178 			perror("uucpd: socket");
179 			exit(1);
180 		}
181 		if (accept(s, &hisctladdr) < 0) {
182 			if (errno == EINTR) {
183 				close(s);
184 				continue;
185 			}
186 			perror("uucpd: accept");
187 			exit(1);
188 		}
189 		if (fork() == 0) {
190 			close(0); close(1); close(2);
191 			dup(s); dup(s); dup(s);
192 			close(s);
193 			doit(&hisctladdr);
194 			exit(1);
195 		}
196 	}
197 #endif	/* BSD2_9 */
198 #endif	/* !BSDINETD */
199 
200 	/* NOTREACHED */
201 }
202 
203 static void
doit(sinp)204 doit(sinp)
205 struct sockaddr_in *sinp;
206 {
207 	char user[64], passwd[64];
208 	struct passwd *pw, *getpwnam();
209 	int error;
210 
211 	alarm(60);
212 	printf("login: "); fflush(stdout);
213 	if (readline(user, sizeof (user)) < 0) {
214 		fprintf(stderr, "user read\n");
215 		return;
216 	}
217 
218 	/*
219 	 * Call pam_start to initiate a PAM authentication operation
220 	 */
221 
222 	if ((pam_start("uucp", user, &conv, &pamh)) != PAM_SUCCESS)
223 		return;
224 	if ((pam_set_item(pamh, PAM_TTY, ttyname(0))) != PAM_SUCCESS)
225 		return;
226 
227 	if (pam_authenticate(pamh, PAM_SILENT) != PAM_SUCCESS) {
228 		/* force a delay if passwd bad */
229 		sleep(4);
230 		fprintf(stderr, "Login incorrect.");
231 		pam_end(pamh, PAM_ABORT);
232 		return;
233 	}
234 
235 	if ((error = pam_acct_mgmt(pamh, PAM_SILENT)) != PAM_SUCCESS) {
236 		switch (error) {
237 		case PAM_NEW_AUTHTOK_REQD:
238 			fprintf(stderr, "Password Expired.");
239 			break;
240 		case PAM_PERM_DENIED:
241 			fprintf(stderr, "Account Expired.");
242 			break;
243 		case PAM_AUTHTOK_EXPIRED:
244 			fprintf(stderr, "Password Expired.");
245 			break;
246 		default:
247 			fprintf(stderr, "Login incorrect.");
248 			break;
249 		}
250 		pam_end(pamh, PAM_ABORT);
251 		return;
252 	}
253 
254 	if ((pw = getpwnam(user)) == NULL || strcmp(pw->pw_shell, UUCICO)) {
255 		/* force a delay if user bad */
256 		sleep(4);
257 		fprintf(stderr, "Login incorrect.");
258 		pam_end(pamh, PAM_USER_UNKNOWN);
259 		return;
260 	}
261 
262 	alarm(0);
263 
264 	sprintf(Username, "USER=%s", user);
265 	sprintf(Loginname, "LOGNAME=%s", user);
266 	if (!nolog)
267 		if (dologin(pw, sinp)) {
268 			pam_end(pamh, PAM_ABORT);
269 			_exit(1);
270 		}
271 
272 	/* set the real (and effective) GID */
273 	if (setgid(pw->pw_gid) == -1) {
274 		fprintf(stderr, "Login incorrect.");
275 		pam_end(pamh, PAM_PERM_DENIED);
276 		return;
277 	}
278 
279 	/*
280 	 * Initialize the supplementary group access list.
281 	 */
282 	if (initgroups(user, pw->pw_gid) == -1) {
283 		fprintf(stderr, "Login incorrect.");
284 		pam_end(pamh, PAM_PERM_DENIED);
285 		return;
286 	}
287 
288 	if (pam_setcred(pamh, PAM_ESTABLISH_CRED) != PAM_SUCCESS) {
289 		fprintf(stderr, "Login incorrect.");
290 		pam_end(pamh, PAM_CRED_INSUFFICIENT);
291 		return;
292 	}
293 
294 	/* set the real (and effective) UID */
295 	if (setuid(pw->pw_uid) == -1) {
296 		fprintf(stderr, "Login incorrect.");
297 		pam_end(pamh, PAM_CRED_ERR);
298 		return;
299 	}
300 
301 	chdir(pw->pw_dir);
302 
303 	pam_end(pamh, PAM_SUCCESS);
304 
305 #if defined(BSD4_2) || defined(ATTSVR4)
306 	execl(UUCICO, "uucico", "-u", user, (char *)0);
307 #endif	/* BSD4_2 */
308 #ifdef BSD2_9
309 	sprintf(passwd, "-h%s", inet_ntoa(sinp->sin_addr));
310 	execl(UUCICO, "uucico", passwd, (char *)0);
311 #endif	/* BSD2_9 */
312 	perror("uucico server: execl");
313 }
314 
315 int
readline(p,n)316 readline(p, n)
317 char *p;
318 int n;
319 {
320 	char c;
321 
322 	while (n-- > 0) {
323 		if (read(0, &c, 1) <= 0)
324 			return (-1);
325 		c &= 0177;
326 		if (c == '\n' || c == '\r') {
327 			*p = '\0';
328 			return (0);
329 		}
330 		*p++ = c;
331 	}
332 	return (-1);
333 }
334 
335 #ifdef ATTSVR4
336 #include <sac.h>	/* for SC_WILDC */
337 #include <utmpx.h>
338 #else	/* !ATTSVR4 */
339 #include <utmp.h>
340 #endif	/* !ATTSVR4 */
341 #if defined(BSD4_2) || defined(ATTSVR4)
342 #include <fcntl.h>
343 #endif	/* BSD4_2 */
344 
345 #ifdef BSD2_9
346 #define	O_APPEND	0 /* kludge */
347 #define	wait3(a, b, c)	wait2(a, b)
348 #endif	/* BSD2_9 */
349 
350 #define	SCPYN(a, b)	strncpy(a, b, sizeof (a))
351 
352 #ifdef ATTSVR4
353 struct	utmpx utmp;
354 #else	/* !ATTSVR4 */
355 struct	utmp utmp;
356 #endif	/* !ATTSVR4 */
357 
358 static void
dologout(void)359 dologout(void)
360 {
361 #ifdef ATTSVR4
362 	int status;
363 #else	/* !ATTSVR4 */
364 	union wait status;
365 #endif	/* !ATSVR4 */
366 	int pid, wtmp;
367 	/* the following 2 variables are needed for utmp mgmt */
368 	struct utmpx	ut;
369 
370 #ifdef BSDINETD
371 	while ((pid = wait(&status)) > 0) {
372 #else	/* !BSDINETD */
373 	while ((pid = wait3(&status, WNOHANG, 0)) > 0) {
374 #endif	/* !BSDINETD */
375 		if (nolog)
376 			continue;
377 #ifdef ATTSVR4
378 		/* clear out any residue from utmpx buffer */
379 		(void) memset((char *)&ut, 0, sizeof (ut));
380 
381 		SCPYN(utmp.ut_user, "");
382 		ut.ut_id[0] = 'u';
383 		ut.ut_id[1] = 'u';
384 		ut.ut_id[2] = SC_WILDC;
385 		ut.ut_id[3] = SC_WILDC;
386 		sprintf(ut.ut_line, "uucp%.4d", pid);
387 		ut.ut_pid  = getpid();
388 		ut.ut_type = DEAD_PROCESS;
389 		ut.ut_exit.e_termination = status & 0xFF;
390 		ut.ut_exit.e_exit = WEXITSTATUS(status);
391 		SCPYN(ut.ut_host, "");
392 		ut.ut_syslen = 1;
393 		(void) gettimeofday(&ut.ut_tv, NULL);
394 
395 		/*
396 		 * XXX: UUCPD does not do any pam session management.
397 		 *	There is no way for the parent process to close
398 		 *	the pam session after a child has exited.
399 		 */
400 
401 		updwtmpx(WTMPX_FILE, &ut);
402 #else	/* !ATTSVR4 */
403 		wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND);
404 		if (wtmp >= 0) {
405 			sprintf(utmp.ut_line, "uucp%.4d", pid);
406 			SCPYN(utmp.ut_name, "");
407 			SCPYN(utmp.ut_host, "");
408 			(void) time(&utmp.ut_time);
409 #ifdef BSD2_9
410 			(void) lseek(wtmp, 0L, 2);
411 #endif	/* BSD2_9 */
412 			(void) write(wtmp, (char *)&utmp, sizeof (utmp));
413 			(void) close(wtmp);
414 		}
415 #endif	/* !ATTSVR4 */
416 	}
417 }
418 
419 /*
420  * Record login in wtmp file.
421  */
422 int
423 dologin(pw, sin)
424 struct passwd *pw;
425 struct sockaddr_in *sin;
426 {
427 	char line[32];
428 	char remotehost[32];
429 	int wtmp;
430 	struct hostent *hp = gethostbyaddr((const char *)&sin->sin_addr,
431 		sizeof (struct in_addr), AF_INET);
432 	struct utmpx	ut;
433 
434 	if (hp) {
435 		strncpy(remotehost, hp->h_name, sizeof (remotehost));
436 		endhostent();
437 	} else
438 		strncpy(remotehost, (char *)inet_ntoa(sin->sin_addr),
439 		    sizeof (remotehost));
440 #ifdef ATTSVR4
441 	/* clear wtmpx entry */
442 	(void) memset((void *)&ut, 0, sizeof (ut));
443 
444 	SCPYN(ut.ut_user, pw->pw_name);
445 	ut.ut_id[0] = 'u';
446 	ut.ut_id[1] = 'u';
447 	ut.ut_id[2] = SC_WILDC;
448 	ut.ut_id[3] = SC_WILDC;
449 	/* hack, but must be unique and no tty line */
450 	sprintf(line, "uucp%.4d", getpid());
451 	SCPYN(ut.ut_line, line);
452 	ut.ut_pid = getpid();
453 	ut.ut_type = USER_PROCESS;
454 	ut.ut_exit.e_termination = 0;
455 	ut.ut_exit.e_exit = 0;
456 	SCPYN(ut.ut_host, remotehost);
457 	ut.ut_syslen = strlen(remotehost) + 1;
458 	(void) gettimeofday(&ut.ut_tv, 0);
459 	updwtmpx(WTMPX_FILE, &ut);
460 
461 	/*
462 	 * XXX:
463 	 * 	We no longer do session management in uucpd because
464 	 *	there is no way to do the "pam_close_session()".
465 	 *
466 	 *	Processes like "init" can do a pam_close_session()
467 	 *	because they can use the utmp entry to retrieve
468 	 *	the proper username, ttyname, etc. --
469 	 *	uucpd only writes to the wtmp file.
470 	 *
471 	 *	ftpd (which also only writes to the wtmp file)
472 	 *	can do a pam_close_session() because it doesn't fork().
473 	 *
474 	 *	if (pam_set_item(pamh, PAM_RHOST, remotehost) != PAM_SUCCESS)
475 	 *		return (1);
476 	 *	if (pam_set_item(pamh, PAM_TTY, line) != PAM_SUCCESS)
477 	 *		return (1);
478 	 *	if (pam_open_session(pamh, 0) != PAM_SUCCESS) {
479 	 *		return (1);
480 	 *	}
481 	 */
482 
483 #else	/* !ATTSVR4 */
484 	wtmp = open("/usr/adm/wtmp", O_WRONLY|O_APPEND);
485 	if (wtmp >= 0) {
486 		/* hack, but must be unique and no tty line */
487 		sprintf(line, "uucp%.4d", getpid());
488 		SCPYN(utmp.ut_line, line);
489 		SCPYN(utmp.ut_name, pw->pw_name);
490 		SCPYN(utmp.ut_host, remotehost);
491 		time(&utmp.ut_time);
492 #ifdef BSD2_9
493 		(void) lseek(wtmp, 0L, 2);
494 #endif	/* BSD2_9 */
495 		(void) write(wtmp, (char *)&utmp, sizeof (utmp));
496 		(void) close(wtmp);
497 	}
498 #endif	/* !ATTSVR4 */
499 
500 	return (0);
501 }
502 
503 /*
504  * uucp_conv	- This is the conv (conversation) function called from
505  *		a PAM authentication module to print error messages
506  *		or garner information from the user.
507  */
508 
509 static int
510 uucp_conv(num_msg, msg, response, appdata_ptr)
511 	int num_msg;
512 	struct pam_message **msg;
513 	struct pam_response **response;
514 	void *appdata_ptr;
515 {
516 	struct pam_message	*m;
517 	struct pam_response	*r;
518 	char			*temp;
519 	static char		passwd[64];
520 	int			k, i;
521 
522 	if (num_msg <= 0)
523 		return (PAM_CONV_ERR);
524 
525 	*response = (struct pam_response *)calloc(num_msg,
526 			sizeof (struct pam_response));
527 	if (*response == NULL)
528 		return (PAM_BUF_ERR);
529 
530 	k = num_msg;
531 	m = *msg;
532 	r = *response;
533 	while (k--) {
534 
535 		switch (m->msg_style) {
536 
537 		case PAM_PROMPT_ECHO_OFF:
538 			/*
539 			 * we do this instead of using passed in message
540 			 * to prevent possible breakage of uucp protocol.
541 			 */
542 			printf("Password: "); fflush(stdout);
543 			if (readline(passwd, sizeof (passwd)) < 0) {
544 				fprintf(stderr, "passwd read\n");
545 				return (PAM_SUCCESS);
546 			}
547 			temp = passwd;
548 			if (temp != NULL) {
549 				r->resp = strdup(temp);
550 				if (r->resp == NULL) {
551 					/* free responses */
552 					r = *response;
553 					for (i = 0; i < num_msg; i++, r++) {
554 						if (r->resp)
555 							free(r->resp);
556 					}
557 					free(*response);
558 					*response = NULL;
559 					return (PAM_BUF_ERR);
560 				}
561 			}
562 
563 			m++;
564 			r++;
565 			break;
566 
567 		case PAM_PROMPT_ECHO_ON:
568 			if (m->msg != NULL) {
569 				fputs(m->msg, stdout);
570 				fflush(stdout);
571 			}
572 			r->resp = (char *)malloc(PAM_MAX_RESP_SIZE);
573 			if (r->resp == NULL) {
574 				/* free the response */
575 				r = *response;
576 				for (i = 0; i < num_msg; i++, r++) {
577 					if (r->resp)
578 						free(r->resp);
579 				}
580 				free(*response);
581 				*response = NULL;
582 				return (PAM_BUF_ERR);
583 			}
584 			(void) fgets(r->resp, PAM_MAX_RESP_SIZE, stdin);
585 			m++;
586 			r++;
587 			break;
588 
589 		case PAM_ERROR_MSG:
590 			if (m->msg != NULL) {
591 				fputs(m->msg, stderr);
592 				fputs("\n", stderr);
593 			}
594 			m++;
595 			r++;
596 			break;
597 		case PAM_TEXT_INFO:
598 			if (m->msg != NULL) {
599 				fputs(m->msg, stdout);
600 				fputs("\n", stdout);
601 			}
602 			m++;
603 			r++;
604 			break;
605 
606 		default:
607 			break;
608 		}
609 	}
610 	return (PAM_SUCCESS);
611 }
612