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
57 static int uucp_conv();
58 struct pam_conv conv = {uucp_conv, NULL };
59 pam_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
68 char lastlog[] = "/var/adm/lastlog";
69 struct passwd nouser = {
70 "", "nope", (uid_t)-1, (gid_t)-1, "", "", "", "", "" };
71 #ifdef ATTSVR4
72 struct spwd noupass = { "", "nope" };
73 #endif
74 struct sockaddr_in hisctladdr;
75 socklen_t hisaddrlen = (socklen_t)sizeof (hisctladdr);
76 struct sockaddr_in myctladdr;
77 int nolog; /* don't log in utmp or wtmp */
78
79 char Username[64];
80 char Loginname[64];
81 char *nenv[] = {
82 Username,
83 Loginname,
84 NULL,
85 };
86 extern char **environ;
87
88 static void doit(struct sockaddr_in *);
89 static void dologout(void);
90
91 int
main(argc,argv)92 main(argc, argv)
93 int argc;
94 char **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
205 static void
doit(sinp)206 doit(sinp)
207 struct 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
317 int
readline(p,n)318 readline(p, n)
319 char *p;
320 int 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
355 struct utmpx utmp;
356 #else /* !ATTSVR4 */
357 struct utmp utmp;
358 #endif /* !ATTSVR4 */
359
360 static void
dologout(void)361 dologout(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 */
424 int
425 dologin(pw, sin)
426 struct passwd *pw;
427 struct 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 retrieve
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
511 static int
512 uucp_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