1 /* $XConsortium: solaris.c /main/4 1995/10/27 16:15:38 rswiston $ */
2 /*******************************************************************************
4 ** solaris.c 1.15 95/09/10
6 ** Copyright 1993, 1994, 1995 Sun Microsystems, Inc. All rights reserved.
8 ** This file contains procedures specific to Sun Solaris login
10 *******************************************************************************/
12 * (c) Copyright 1993, 1994 Hewlett-Packard Company *
13 * (c) Copyright 1993, 1994 International Business Machines Corp. *
14 * (c) Copyright 1993, 1994 Sun Microsystems, Inc. *
15 * (c) Copyright 1993, 1994 Novell, Inc. *
17 /*******************************************************************************
19 ** RESTRICTED CONFIDENTIAL INFORMATION:
21 ** The information in this document is subject to special
22 ** restrictions in a confidential disclosure agreement between
23 ** HP, IBM, Sun, USL, SCO and Univel. Do not distribute this
24 ** document outside HP, IBM, Sun, USL, SCO, or Univel without
25 ** Sun's specific written approval. This document and all copies
26 ** and derivative works thereof must be returned or destroyed at
29 ** Copyright 1994 Sun Microsystems, Inc. All rights reserved.
31 *******************************************************************************/
38 #include <sys/types.h>
44 #include <sys/param.h>
49 #include <security/ia_appl.h>
54 * Local function declarations, structures, and variables
57 static char* create_devname(char* short_devname);
58 static void dir_dev_acc(char *, uid_t, gid_t, mode_t, char *);
59 static void logindevperm(char *, uid_t, gid_t);
60 static void resetdevperm(char *);
63 static int login_conv(int conv_id, int num_msg, struct ia_message **msg,
64 struct ia_response **response, void *appdata_ptr);
65 static void end_conv();
67 static struct ia_conv ia_conv = {login_conv, login_conv, end_conv, NULL};
68 static char *saved_user_passwd;
72 /****************************************************************************
75 * Modify permission of devices as specified in Solaris /etc/logindevperm file
76 ****************************************************************************/
79 solaris_setdevperm(char* gettyLine, uid_t uid, gid_t gid)
81 char *devname = create_devname(gettyLine);
83 if (devname == NULL) {
84 Debug ("solaris_setdevperm: NULL devname\n");
88 logindevperm(devname, uid, gid);
94 /****************************************************************************
95 * solaris_resetdevperm
97 * Reset permission on devices specified in /etc/logindevperm file
98 ****************************************************************************/
101 solaris_resetdevperm(char* gettyLine)
103 char *devname = create_devname(gettyLine);
105 if (devname == NULL) {
106 Debug ("solaris_resetdevperm: NULL devname\n");
110 resetdevperm(devname);
117 /****************************************************************************
118 * solaris_authentication
120 * Authenticate that user / password combination is legal for this system
122 ****************************************************************************/
125 solaris_authenticate ( char* prog_name,
132 struct ia_status out;
133 void *iah; /* PAM authentication handle */
134 char* user_str = user ? user : "NULL";
135 char* line_str = line ? line : "NULL";
136 char *line_dev = create_devname(line_str);
138 Debug("solaris_authenticate: prog_name=%s\n", prog_name);
139 Debug("solaris_authenticate: display_name=%s\n", display_name);
140 Debug("solaris_authenticate: user=%s\n", user_str);
141 Debug("solaris_authenticate: line=%s\n", line_str);
142 Debug("solaris_authenticate: line_dev=%s\n", line_dev);
145 if (strlen(user_passwd) == 0) {
146 Debug("solaris_authenticate: user passwd empty\n");
148 Debug("solaris_authenticate: user passwd present\n");
152 Debug("solaris_authenticate: user passwd NULL\n");
154 /* Password challenge required for solaris authentication */
155 return(IA_AUTHTEST_FAIL);
158 /* Solaris BSM Audit trail */
160 audit_login_save_host(display_name);
161 audit_login_save_ttyn(line_dev);
162 audit_login_save_port();
164 /* Open Solaris PAM (Plugable Authentication module ) connection */
166 status = ia_start( prog_name, user, line_dev,
167 display_name, &ia_conv, &iah );
170 int flags = AU_CHECK_PASSWD | AU_CONTINUE;
173 saved_user_passwd = user_passwd;
174 status = ia_auth_user( iah, flags, &pwd, &out );
176 audit_login_save_pw(pwd);
179 audit_login_bad_pw();
180 if (status == IA_MAXTRYS) {
181 audit_login_maxtrys();
187 Debug("solaris_authenticate: PAM error=%d\n", status);
198 /****************************************************************************
201 * Updates "utmp/utmpx: and "wtmp/wtmpx" system accounting entries
203 ****************************************************************************/
206 solaris_accounting( char* prog_name,
215 int session_type, status;
216 struct ia_status out;
217 void *iah; /* PAM authentication handle */
218 char* user_str = user ? user : "NULL";
219 char* line_str = line ? line : "NULL";
220 char *line_dev = create_devname(line_str);
222 Debug("solaris_accounting: prog_name=%s\n", prog_name);
223 Debug("solaris_accounting: display_name=%s\n", display_name);
224 Debug("solaris_accounting: entry_id=%c %c %c %c\n", entry_id[0],
225 entry_id[1], entry_id[2], entry_id[3]);
226 Debug("solaris_accounting: user=%s\n", user_str);
227 Debug("solaris_accounting: line=%s\n", line_str);
228 Debug("solaris_accounting: line_dev=%s\n", line_dev);
229 Debug("solaris_accounting: pid=%d\n", pid);
230 Debug("solaris_accounting: entry_type=%d\n", entry_type);
231 Debug("solaris_accounting: exitcode=%d\n", exitcode);
234 /* Open Solaris PAM (Plugable Authentication module ) connection */
236 if (entry_type == ACCOUNTING) {
237 status = ia_start(prog_name, user, line,
238 display_name, &ia_conv, &iah);
241 status = ia_start(prog_name, user, line_dev,
242 display_name, &ia_conv, &iah);
246 /* Session accounting */
248 if (status != IA_SUCCESS) {
249 Debug("solaris_accounting: PAM error=%d)\n", status);
255 /* New user session, open session accounting logs */
256 session_type = IS_LOGIN;
257 status = ia_open_session(iah, session_type,
258 entry_type, entry_id, &out);
259 if (status == IA_SUCCESS) audit_login_success();
265 * User session has terminated, mark it DEAD and close
266 * the sessions accounting logs.
268 entry_type = DEAD_PROCESS;
269 session_type = IS_NOLOG;
270 status = ia_open_session(iah, session_type,
271 entry_type, entry_id, &out);
272 if (status != IA_SUCCESS) {
273 Debug("solaris_accounting: PAM check seuser error=%d\n",
276 /* Intentional fall thru */
280 /* Cleanup account files for dead processes */
281 status = ia_close_session(iah, 0, pid,
282 exitcode, entry_id, &out);
289 status = ia_open_session(iah, session_type,
290 entry_type, entry_id, &out);
295 if (status != IA_SUCCESS) {
296 Debug("solaris_accounting: PAM session error=%d\n", status);
306 /****************************************************************************
309 * Set Users login credentials: uid, gid, and group lists
310 ****************************************************************************/
313 solaris_setcred(char* prog_name, char* user, uid_t uid, gid_t gid)
315 int cred_type, status;
316 struct ia_status out;
317 void *iah; /* PAM authentication handle */
318 char* user_str = user ? user : "NULL";
320 Debug("solaris_setcred: prog_name=%s\n", prog_name);
321 Debug("solaris_setcred: user=%s\n", user_str);
322 Debug("solaris_setcred: uid=%d\n", uid);
323 Debug("solaris_setcred: gid=%d\n", gid);
326 /* Open PAM connection */
328 status = ia_start(prog_name, user, NULL, NULL, &ia_conv, &iah);
330 Debug("solaris_setcred: PAM start error=%d\n", status);
334 /* Set users credentials */
336 ia_set_item(iah, IA_AUTHTOK, saved_user_passwd);
338 cred_type = SC_INITGPS|SC_SETRID;
339 status = ia_setcred(iah, cred_type, uid, gid, 0, NULL, &out);
342 Debug("solaris_setcred: user=%s, err=%d)\n", user, status);
347 Debug("solaris_setcred: return status =%d\n", status);
354 /***************************************************************************
357 * A utility function. Takes short device name like "console" and returns
358 * a long device name like "/dev/console"
359 ***************************************************************************/
362 create_devname(char* short_devname)
366 if (short_devname == NULL)
369 long_devname = (char *) malloc (strlen(short_devname) + 5);
371 if (long_devname == NULL)
374 strcpy(long_devname,"/dev/");
375 strcat(long_devname, short_devname);
377 return(long_devname);
381 /****************************************************************************
384 * change owner/group/permissions of devices list in /etc/logindevperm.
386 * This code is directly from Sun Solaris 5.3 login. Eventually this will
387 * be a fuction in a Solaris library. Till then, this is duplicate copy.
388 ***************************************************************************/
390 #define LOGINDEVPERM "/etc/logindevperm"
391 #define DIRWILD "/*" /* directory wildcard */
392 #define DIRWLDLEN 2 /* strlen(DIRWILD) */
396 logindevperm(char *ttyn, uid_t uid, gid_t gid)
398 char *field_delims = " \t\n";
399 char *permfile = LOGINDEVPERM;
400 char line[MAXPATHLEN];
411 if ((fp = fopen(permfile, "r")) == NULL)
415 while (fgets(line, MAXPATHLEN, fp) != NULL) {
418 if ((ptr = strchr(line, '#')) != NULL)
419 *ptr = '\0'; /* handle comments */
421 if ((console = strtok(line, field_delims)) == NULL)
422 continue; /* ignore blank lines */
424 if (strcmp(console, ttyn) != 0)
425 continue; /* not our tty, skip */
427 mode_str = strtok((char *)NULL, field_delims);
428 if (mode_str == NULL) {
429 (void) fprintf(stderr,
430 "%s: line %d, invalid entry -- %s\n", permfile,
435 /* convert string to octal value */
436 mode = strtol(mode_str, &ptr, 8);
437 if (mode < 0 || mode > 0777 || *ptr != '\0') {
438 (void) fprintf(stderr,
439 "%s: line %d, invalid mode -- %s\n", permfile,
444 dev_list = strtok((char *)NULL, field_delims);
445 if (dev_list == NULL) {
446 (void) fprintf(stderr,
447 "%s: line %d, %s -- empty device list\n",
448 permfile, lineno, console);
452 device = strtok(dev_list, ":");
453 while (device != NULL) {
455 ptr = &device[l - DIRWLDLEN];
456 if ((l > DIRWLDLEN) && (strcmp(ptr, DIRWILD) == 0)) {
457 *ptr = '\0'; /* chop off wildcard */
458 dir_dev_acc(device, uid, gid, mode, permfile);
461 * change the owner/group/permission;
462 * nonexistent devices are ignored
464 if (chown(device, uid, gid) == -1) {
465 if (errno != ENOENT) {
466 (void) fprintf(stderr, "%s: ",
471 if ((chmod(device, mode) == -1) &&
473 (void) fprintf(stderr, "%s: ",
479 device = strtok((char *)NULL, ":");
485 /***************************************************************************
488 * Apply owner/group/perms to all files (except "." and "..") in a directory.
490 * This code is directly Sun Solaris 5.3 login. Eventually this will
491 * be internal to a Solaris library. Till then, this is a duplicate copy.
492 ***************************************************************************/
495 dir_dev_acc(char *dir, uid_t uid, gid_t gid, mode_t mode, char *permfile)
498 struct dirent *direntp;
499 char *name, path[MAXPATHLEN];
505 while ((direntp = readdir(dirp)) != NULL) {
506 name = direntp->d_name;
507 if ((strcmp(name, ".") == 0) || (strcmp(name, "..") == 0))
510 (void) sprintf(path, "%s/%s", dir, name);
511 if (chown(path, uid, gid) == -1) {
512 if (errno != ENOENT) {
513 (void) fprintf(stderr, "%s: ", permfile);
517 if ((chmod(path, mode) == -1) && (errno != ENOENT)) {
518 (void) fprintf(stderr, "%s: ", permfile);
523 (void) closedir(dirp);
526 /*****************************************************************************
529 * Clean up access of devices in /etc/logindevperm by resetting the
530 * owner/group/permissions back to the login pseudo user.
532 *****************************************************************************/
534 extern struct passwd puser; /* pseudo_user password entry */
537 resetdevperm(char *ttyn)
539 logindevperm(ttyn, puser.pw_uid, puser.pw_gid);
543 /*****************************************************************************
546 * This is a conv (conversation) function called from the PAM
547 * authentication scheme. It returns the user's password when requested by
548 * internal PAM authentication modules and also logs any internal PAM error
550 *****************************************************************************/
553 login_conv(int conv_id, int num_msg, struct ia_message **msg,
554 struct ia_response **response, void *appdata_ptr)
556 struct ia_message *m;
557 struct ia_response *r;
565 return (IA_CONV_FAILURE);
567 *response = (struct ia_response*)
568 calloc(num_msg, sizeof (struct ia_response));
569 if (*response == NULL)
570 return (IA_CONV_FAILURE);
572 (void) memset(*response, 0, sizeof (struct ia_response));
579 switch (m->msg_style) {
581 case IA_PROMPT_ECHO_OFF:
582 if (saved_user_passwd != NULL) {
583 r->resp = (char *) malloc(strlen(saved_user_passwd)+1);
584 if (r->resp == NULL) {
585 free_resp(num_msg, *response);
587 return (IA_CONV_FAILURE);
589 (void) strcpy(r->resp, saved_user_passwd);
590 r->resp_len = strlen(r->resp);
599 if (m->msg != NULL) {
600 Debug ("login_conv: %s\n", m->msg);
607 Debug ("login_conv: Unexpected case %d\n",
616 /*****************************************************************************
619 * End of conversation function. Called from PAM.
620 * Currently a noop for dtlogin.
621 *****************************************************************************/
629 /****************************************************************************
630 * Following Solaris utmp management code was taken from portions of the
631 * private libauth open and close session code. There are no equivalent calls
632 * in libpam that replaces it, so the code is copied to here. Eventually this
633 * code may become functions in either PAM or other appropriate libraries.
634 ***************************************************************************/
636 #define INIT_PROC_PID 1
637 #define PAMTXD "SUNW_OST_SYSOSPAM"
638 #define SCPYN(a, b) (void) strncpy(a, b, sizeof (a))
640 /* utility function to do UTMP/WTMP management */
642 solaris_setutmp_mgmt(
643 char *user, /* user */
644 char *ttyn, /* ttyn */
645 char *rhost, /* remote hostname */
646 int flags, /* Flags - see below */
647 int type, /* type of utmp/wtmp entry */
648 char id[] /* 4 byte id field for utmp */
652 * solaris_reset_utmp_mgmt
653 * a utility function which terminates UTMP/WTMP mgmt
656 solaris_reset_utmp_mgmt(
657 char **user, /* user */
658 char **ttyn, /* tty name */
659 char **rhost, /* remote host */
660 int flags, /* flags - see below */
661 int status, /* logout process status */
662 char id[] /* logout ut_id (/etc/inittab id) */
665 /* flags for the flags field */
666 #define __NOOP 8 /* No utmp action desired */
668 /* __setproc_cred is a utility function to set process credentials */
671 char *user, /* user */
672 int flags, /* flags - see below */
673 uid_t uid, /* User ID to set for this process */
674 gid_t gid, /* Group ID */
675 int ngroups, /* Number of groups */
676 gid_t *grouplist /* Group list */
679 /* flags indicates specific set credential actions */
681 #define __INITGROUPS 0x00000001 /* Request to initgroups() */
682 #define __SETGROUPS 0x00000002 /* Request to setgroups() */
683 #define __SETEGID 0x00000004 /* Set effective gid only */
684 #define __SETGID 0x00000008 /* Set real gid */
685 #define __SETEUID 0x00000010 /* Set effective uid only */
686 #define __SETUID 0x00000020 /* Set real uid */
687 #define __SETEID (__SETEGID|__SETEUID) /* Set effective ids only */
688 #define __SETRID (__SETGID|__SETUID) /* Set real ids */
691 * solaris_setutmp_mgmt - A utility function used to do UTMP/WTMP management.
693 * "user" is the current username.
694 * "ttyn" is the tty name.
695 * "rhost" is the remote hostname.
696 * The following flags may be set in the "flags" field:
698 * SOLARIS_UPDATE_ENTRY No new entry will be created if utmp
699 * entry not found - return __NOENTRY
700 * SOLARIS_NOLOG Generate a wtmp/wtmpx entry only
701 * SOLARIS_LOGIN Caller is a login application - update
702 * utmp entry accordingly
704 * "type" is used to indicate the type of utmp/wtmp entry written
705 * (see also utmp.h and utmpx.h)
706 * "id is an optional application-defined 4 byte array that represents
707 * the /sbin/inittab id (created by the process that puts an entry in
710 * Upon successful completion, SOLARIS_SUCCESS is returned.
711 * Error values may include:
713 * SOLARIS_NOENTRY An entry for the specified process was not found
714 * SOLARIS_ENTRYFAIL Could not make/remove entry for specified process
718 solaris_setutmp_mgmt(
727 struct utmpx *u = (struct utmpx *)0;
730 int err = PAM_SUCCESS;
732 Debug("Enter: solaris_setutmp_mgmt()\n");
734 (void) memset((void *)&utmp, 0, sizeof (utmp));
736 (void) time(&utmp.ut_tv.tv_sec);
737 utmp.ut_pid = getpid();
738 if (rhost != NULL && rhost[0] != '\0') {
739 (void) strcpy(utmp.ut_host, rhost);
740 tmplen = strlen(rhost) + 1;
741 if (tmplen < sizeof (utmp.ut_host))
742 utmp.ut_syslen = tmplen;
744 utmp.ut_syslen = sizeof (utmp.ut_host);
746 (void) memset(utmp.ut_host, 0, sizeof (utmp.ut_host));
750 (void) strcpy(utmp.ut_user, user);
752 * Copy in the name of the tty minus the "/dev/" if a /dev/ is
756 if (!(flags&SOLARIS_LOGIN))
757 SCPYN(utmp.ut_line, ttyn);
764 (void) memcpy(utmp.ut_id, id, sizeof (utmp.ut_id));
766 if ((flags & SOLARIS_NOLOG) == SOLARIS_NOLOG) {
767 updwtmpx(WTMPX_FILE, &utmp);
770 * Go through each entry one by one, looking only at INIT,
771 * LOGIN or USER Processes. Use the entry found if flags == 0
772 * and the line name matches, or if the process ID matches if
773 * the UPDATE_ENTRY flag is set. The UPDATE_ENTRY flag is
774 * mainly for login which normally only wants to update an
775 * entry if the pid fields matches.
778 if (flags & SOLARIS_LOGIN) {
779 while ((u = getutxent()) != NULL) {
780 if ((u->ut_type == INIT_PROCESS ||
781 u->ut_type == LOGIN_PROCESS ||
782 u->ut_type == USER_PROCESS) &&
783 ((flags == SOLARIS_LOGIN && ttyn != NULL &&
784 strncmp(u->ut_line, ttyntail,
785 sizeof (u->ut_line)) == 0) ||
786 u->ut_pid == utmp.ut_pid)) {
789 (ttyn + sizeof ("/dev/") - 1));
791 (void) memcpy(utmp.ut_id, u->ut_id,
792 sizeof (utmp.ut_id));
794 (void) pututxline(&utmp);
798 endutxent(); /* Close utmp file */
801 if (u == (struct utmpx *)NULL) {
802 /* audit_login_main11(); */
803 if (flags & SOLARIS_UPDATE_ENTRY)
804 err = SOLARIS_NOENTRY;
806 (void) makeutx(&utmp);
809 updwtmpx(WTMPX_FILE, &utmp);
815 * solaris_reset_utmp_mgmt
816 * A utility function used to terminate UTMP/WTMP mgmt.
818 * "user" is the current username.
819 * "ttyn" is the tty name.
820 * "rhost" is the remote hostname.
821 * The following flags may be set in the "flags" field:
823 * SOLARIS_NOLOG Write a wtmp/wtmpx entry only
824 * SOLARIS_NOOP Ignore utmp/wtmp processing
826 * "status" is the logout process status.
827 * "id is an optional application-defined 4 byte array that represents
828 * the /sbin/inittab id (created by the process that puts an entry in
831 * Upon successful completion, PAM_SUCCESS is returned.
832 * Error values may include:
834 * SOLARIS_NOENTRY An entry for the specified process was not found
835 * SOLARIS_ENTRYFAIL Could not make/remove entry for specified process
839 solaris_reset_utmp_mgmt(
852 Debug("Enter: solaris_reset_utmp_mgmt()\n");
855 pid = (int) getpid();
857 if ((flags & SOLARIS_NOLOG) == SOLARIS_NOLOG) {
858 /* only write to wtmp files */
859 /* clear utmpx entry */
860 (void) memset((char *)&ut, 0, sizeof (ut));
863 (void) memcpy(ut.ut_id, id, sizeof (ut.ut_id));
865 if (*ttyn != NULL && **ttyn != '\0') {
866 if (strstr(*ttyn, "/dev/") != NULL)
867 (void) strncpy(ut.ut_line, (*ttyn+sizeof ("/dev/")-1),
868 sizeof (ut.ut_line));
870 (void) strncpy(ut.ut_line, *ttyn,
871 sizeof (ut.ut_line));
874 ut.ut_type = DEAD_PROCESS;
875 ut.ut_exit.e_termination = 0;
876 ut.ut_exit.e_exit = 0;
878 (void) gettimeofday(&ut.ut_tv, NULL);
879 updwtmpx(WTMPX_FILE, &ut);
881 return (PAM_SUCCESS);
884 while (up = getutxent()) {
885 if (up->ut_pid == pid) {
886 if (up->ut_type == DEAD_PROCESS) {
888 * Cleaned up elsewhere.
893 if ((*user = (char *) strdup (up->ut_user))
895 (*ttyn = (char *) strdup (up->ut_line))
897 (*rhost = (char *) strdup (up->ut_host))
899 return (PAM_BUF_ERR);
901 up->ut_type = DEAD_PROCESS;
902 up->ut_exit.e_termination = status & 0xff;
903 up->ut_exit.e_exit = (status >> 8) & 0xff;
905 (void) memcpy(up->ut_id, id,
907 (void) time(&up->ut_tv.tv_sec);
910 * For init (Process ID 1) we don't write to
911 * init's pipe, since we are init.
913 if (getpid() == INIT_PROC_PID) {
914 (void) pututxline(up);
916 * Now attempt to add to the end of the
917 * wtmp and wtmpx files. Do not create
918 * if they don't already exist.
920 updwtmpx("wtmpx", up);
922 if (modutx(up) == NULL) {
923 Debug("modutx failed\n");
925 * Since modutx failed we'll
926 * write out the new entry
929 (void) pututxline(up);
930 updwtmpx("wtmpx", up);
936 return (PAM_SUCCESS);
941 return (SOLARIS_NOENTRY);