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