14847Smj162486 /*
24847Smj162486 * CDDL HEADER START
34847Smj162486 *
44847Smj162486 * The contents of this file are subject to the terms of the
54847Smj162486 * Common Development and Distribution License (the "License").
64847Smj162486 * You may not use this file except in compliance with the License.
74847Smj162486 *
84847Smj162486 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
94847Smj162486 * or http://www.opensolaris.org/os/licensing.
104847Smj162486 * See the License for the specific language governing permissions
114847Smj162486 * and limitations under the License.
124847Smj162486 *
134847Smj162486 * When distributing Covered Code, include this CDDL HEADER in each
144847Smj162486 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
154847Smj162486 * If applicable, add the following below this CDDL HEADER, with the
164847Smj162486 * fields enclosed by brackets "[]" replaced with your own identifying
174847Smj162486 * information: Portions Copyright [yyyy] [name of copyright owner]
184847Smj162486 *
194847Smj162486 * CDDL HEADER END
204847Smj162486 */
214847Smj162486
224847Smj162486 /*
23*8553SMilan.Jurik@Sun.COM * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
244847Smj162486 * Use is subject to license terms.
254847Smj162486 */
264847Smj162486
274847Smj162486 #include <stdio.h>
284847Smj162486 #include <string.h>
294847Smj162486 #include <sys/types.h>
304847Smj162486 #include <sys/stat.h>
314847Smj162486 #include <syslog.h>
324847Smj162486 #include <netdb.h>
334847Smj162486 #include <malloc.h>
344847Smj162486 #include <unistd.h>
354847Smj162486 #include <errno.h>
364847Smj162486 #include <security/pam_appl.h>
374847Smj162486 #include <security/pam_modules.h>
384847Smj162486 #include <security/pam_impl.h>
394847Smj162486
40*8553SMilan.Jurik@Sun.COM #define ILLEGAL_COMBINATION "pam_list: illegal combination of options"
41*8553SMilan.Jurik@Sun.COM
42*8553SMilan.Jurik@Sun.COM typedef enum {
43*8553SMilan.Jurik@Sun.COM LIST_EXTERNAL_FILE,
44*8553SMilan.Jurik@Sun.COM LIST_PLUS_CHECK,
45*8553SMilan.Jurik@Sun.COM LIST_COMPAT_MODE
46*8553SMilan.Jurik@Sun.COM } pam_list_mode_t;
47*8553SMilan.Jurik@Sun.COM
48*8553SMilan.Jurik@Sun.COM static const char *
string_mode_type(pam_list_mode_t op_mode,boolean_t allow)49*8553SMilan.Jurik@Sun.COM string_mode_type(pam_list_mode_t op_mode, boolean_t allow)
50*8553SMilan.Jurik@Sun.COM {
51*8553SMilan.Jurik@Sun.COM return ((op_mode == LIST_COMPAT_MODE) ? "compat" :
52*8553SMilan.Jurik@Sun.COM (allow ? "allow" : "deny"));
53*8553SMilan.Jurik@Sun.COM }
54*8553SMilan.Jurik@Sun.COM
55*8553SMilan.Jurik@Sun.COM static void
log_illegal_combination(const char * s1,const char * s2)56*8553SMilan.Jurik@Sun.COM log_illegal_combination(const char *s1, const char *s2)
57*8553SMilan.Jurik@Sun.COM {
58*8553SMilan.Jurik@Sun.COM __pam_log(LOG_AUTH | LOG_ERR, ILLEGAL_COMBINATION
59*8553SMilan.Jurik@Sun.COM " %s and %s", s1, s2);
60*8553SMilan.Jurik@Sun.COM }
61*8553SMilan.Jurik@Sun.COM
624847Smj162486 /*ARGSUSED*/
634847Smj162486 int
pam_sm_acct_mgmt(pam_handle_t * pamh,int flags,int argc,const char ** argv)644847Smj162486 pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv)
654847Smj162486 {
664847Smj162486 FILE *fd;
674847Smj162486 const char *allowdeny_filename = PF_PATH;
684847Smj162486 char buf[BUFSIZ];
694847Smj162486 char hostname[MAXHOSTNAMELEN];
704847Smj162486 char *username = NULL;
714847Smj162486 char *bufp;
724847Smj162486 char *rhost;
734847Smj162486 char *limit;
744847Smj162486 int userok = 0;
754847Smj162486 int hostok = 0;
764847Smj162486 int i;
774847Smj162486 int allow_deny_test = 0;
784847Smj162486 boolean_t debug = B_FALSE;
794847Smj162486 boolean_t allow = B_FALSE;
804847Smj162486 boolean_t matched = B_FALSE;
814847Smj162486 boolean_t check_user = B_TRUE;
824847Smj162486 boolean_t check_host = B_FALSE;
834847Smj162486 boolean_t check_exact = B_FALSE;
84*8553SMilan.Jurik@Sun.COM pam_list_mode_t op_mode = LIST_PLUS_CHECK;
854847Smj162486
864847Smj162486 for (i = 0; i < argc; ++i) {
874847Smj162486 if (strncasecmp(argv[i], "debug", sizeof ("debug")) == 0) {
884847Smj162486 debug = B_TRUE;
894847Smj162486 } else if (strncasecmp(argv[i], "user", sizeof ("user")) == 0) {
904847Smj162486 check_user = B_TRUE;
914847Smj162486 } else if (strncasecmp(argv[i], "nouser",
924847Smj162486 sizeof ("nouser")) == 0) {
934847Smj162486 check_user = B_FALSE;
944847Smj162486 } else if (strncasecmp(argv[i], "host", sizeof ("host")) == 0) {
954847Smj162486 check_host = B_TRUE;
964847Smj162486 } else if (strncasecmp(argv[i], "nohost",
974847Smj162486 sizeof ("nohost")) == 0) {
984847Smj162486 check_host = B_FALSE;
994847Smj162486 } else if (strncasecmp(argv[i], "user_host_exact",
1004847Smj162486 sizeof ("user_host_exact")) == 0) {
1014847Smj162486 check_exact = B_TRUE;
102*8553SMilan.Jurik@Sun.COM } else if (strcasecmp(argv[i], "compat") == 0) {
103*8553SMilan.Jurik@Sun.COM if (op_mode == LIST_PLUS_CHECK) {
104*8553SMilan.Jurik@Sun.COM op_mode = LIST_COMPAT_MODE;
105*8553SMilan.Jurik@Sun.COM } else {
106*8553SMilan.Jurik@Sun.COM log_illegal_combination("compat",
107*8553SMilan.Jurik@Sun.COM string_mode_type(op_mode, allow));
108*8553SMilan.Jurik@Sun.COM return (PAM_SERVICE_ERR);
109*8553SMilan.Jurik@Sun.COM }
1104847Smj162486 } else if (strncasecmp(argv[i], "allow=",
1114847Smj162486 sizeof ("allow=") - 1) == 0) {
112*8553SMilan.Jurik@Sun.COM if (op_mode == LIST_PLUS_CHECK) {
113*8553SMilan.Jurik@Sun.COM allowdeny_filename = argv[i] +
114*8553SMilan.Jurik@Sun.COM sizeof ("allow=") - 1;
115*8553SMilan.Jurik@Sun.COM allow = B_TRUE;
116*8553SMilan.Jurik@Sun.COM op_mode = LIST_EXTERNAL_FILE;
117*8553SMilan.Jurik@Sun.COM allow_deny_test++;
118*8553SMilan.Jurik@Sun.COM } else {
119*8553SMilan.Jurik@Sun.COM log_illegal_combination("allow",
120*8553SMilan.Jurik@Sun.COM string_mode_type(op_mode, allow));
121*8553SMilan.Jurik@Sun.COM return (PAM_SERVICE_ERR);
122*8553SMilan.Jurik@Sun.COM }
1234847Smj162486 } else if (strncasecmp(argv[i], "deny=",
1244847Smj162486 sizeof ("deny=") - 1) == 0) {
125*8553SMilan.Jurik@Sun.COM if (op_mode == LIST_PLUS_CHECK) {
126*8553SMilan.Jurik@Sun.COM allowdeny_filename = argv[i] +
127*8553SMilan.Jurik@Sun.COM sizeof ("deny=") - 1;
128*8553SMilan.Jurik@Sun.COM allow = B_FALSE;
129*8553SMilan.Jurik@Sun.COM op_mode = LIST_EXTERNAL_FILE;
130*8553SMilan.Jurik@Sun.COM allow_deny_test++;
131*8553SMilan.Jurik@Sun.COM } else {
132*8553SMilan.Jurik@Sun.COM log_illegal_combination("deny",
133*8553SMilan.Jurik@Sun.COM string_mode_type(op_mode, allow));
134*8553SMilan.Jurik@Sun.COM return (PAM_SERVICE_ERR);
135*8553SMilan.Jurik@Sun.COM }
1364847Smj162486 } else {
1374847Smj162486 __pam_log(LOG_AUTH | LOG_ERR,
1384847Smj162486 "pam_list: illegal option %s", argv[i]);
1394847Smj162486 return (PAM_SERVICE_ERR);
1404847Smj162486 }
1414847Smj162486 }
1424847Smj162486
1434847Smj162486 if (((check_user || check_host || check_exact) == B_FALSE) ||
1444847Smj162486 (allow_deny_test > 1)) {
145*8553SMilan.Jurik@Sun.COM __pam_log(LOG_AUTH | LOG_ERR, ILLEGAL_COMBINATION);
146*8553SMilan.Jurik@Sun.COM return (PAM_SERVICE_ERR);
147*8553SMilan.Jurik@Sun.COM }
148*8553SMilan.Jurik@Sun.COM
149*8553SMilan.Jurik@Sun.COM if ((op_mode == LIST_COMPAT_MODE) && (check_user == B_FALSE)) {
150*8553SMilan.Jurik@Sun.COM log_illegal_combination("compat", "nouser");
1514847Smj162486 return (PAM_SERVICE_ERR);
1524847Smj162486 }
1534847Smj162486
1544847Smj162486 if (debug) {
1554847Smj162486 __pam_log(LOG_AUTH | LOG_DEBUG,
1564847Smj162486 "pam_list: check_user = %d, check_host = %d,"
1574847Smj162486 "check_exact = %d\n",
1584847Smj162486 check_user, check_host, check_exact);
1594847Smj162486
1604847Smj162486 __pam_log(LOG_AUTH | LOG_DEBUG,
1614847Smj162486 "pam_list: auth_file: %s, %s\n", allowdeny_filename,
162*8553SMilan.Jurik@Sun.COM (op_mode == LIST_COMPAT_MODE) ? "compat mode" :
163*8553SMilan.Jurik@Sun.COM (allow ? "allow file" : "deny file"));
1644847Smj162486 }
1654847Smj162486
1664847Smj162486 (void) pam_get_item(pamh, PAM_USER, (void**)&username);
1674847Smj162486
1684847Smj162486 if ((check_user || check_exact) && ((username == NULL) ||
1694847Smj162486 (*username == '\0'))) {
1704847Smj162486 __pam_log(LOG_AUTH | LOG_ERR,
1714847Smj162486 "pam_list: username not supplied, critical error");
1724847Smj162486 return (PAM_USER_UNKNOWN);
1734847Smj162486 }
1744847Smj162486
1754847Smj162486 (void) pam_get_item(pamh, PAM_RHOST, (void**)&rhost);
1764847Smj162486
1774847Smj162486 if ((check_host || check_exact) && ((rhost == NULL) ||
1784847Smj162486 (*rhost == '\0'))) {
1794847Smj162486 if (gethostname(hostname, MAXHOSTNAMELEN) == 0) {
1804847Smj162486 rhost = hostname;
1814847Smj162486 } else {
1824847Smj162486 __pam_log(LOG_AUTH | LOG_ERR,
1834847Smj162486 "pam_list: error by gethostname - %m");
1844847Smj162486 return (PAM_SERVICE_ERR);
1854847Smj162486 }
1864847Smj162486 }
1874847Smj162486
1884847Smj162486 if (debug) {
1894847Smj162486 __pam_log(LOG_AUTH | LOG_DEBUG,
190*8553SMilan.Jurik@Sun.COM "pam_list: pam_sm_acct_mgmt for (%s,%s,)",
1914847Smj162486 (rhost != NULL) ? rhost : "", username);
1924847Smj162486 }
1934847Smj162486
194*8553SMilan.Jurik@Sun.COM if (strlen(allowdeny_filename) == 0) {
195*8553SMilan.Jurik@Sun.COM __pam_log(LOG_AUTH | LOG_ERR,
196*8553SMilan.Jurik@Sun.COM "pam_list: file name not specified");
197*8553SMilan.Jurik@Sun.COM return (PAM_SERVICE_ERR);
198*8553SMilan.Jurik@Sun.COM }
199*8553SMilan.Jurik@Sun.COM
2004847Smj162486 if ((fd = fopen(allowdeny_filename, "rF")) == NULL) {
201*8553SMilan.Jurik@Sun.COM __pam_log(LOG_AUTH | LOG_ERR, "pam_list: fopen of %s: %s",
202*8553SMilan.Jurik@Sun.COM allowdeny_filename, strerror(errno));
2034847Smj162486 return (PAM_SERVICE_ERR);
2044847Smj162486 }
2054847Smj162486
2064847Smj162486 while (fgets(buf, BUFSIZ, fd) != NULL) {
2074847Smj162486 /* lines longer than BUFSIZ-1 */
2084847Smj162486 if ((strlen(buf) == (BUFSIZ - 1)) &&
2094847Smj162486 (buf[BUFSIZ - 2] != '\n')) {
2104847Smj162486 while ((fgetc(fd) != '\n') && (!feof(fd))) {
2114847Smj162486 continue;
2124847Smj162486 }
2134847Smj162486 __pam_log(LOG_AUTH | LOG_DEBUG,
2144847Smj162486 "pam_list: long line in file,"
2154847Smj162486 "more than %d chars, the rest ignored", BUFSIZ - 1);
2164847Smj162486 }
2174847Smj162486
2184847Smj162486 /* remove unneeded colons if necessary */
2194847Smj162486 if ((limit = strpbrk(buf, ":\n")) != NULL) {
2204847Smj162486 *limit = '\0';
2214847Smj162486 }
2224847Smj162486
2234847Smj162486 /* ignore free values */
2244847Smj162486 if (buf[0] == '\0') {
2254847Smj162486 continue;
2264847Smj162486 }
2274847Smj162486
228*8553SMilan.Jurik@Sun.COM bufp = buf;
2294847Smj162486
2304847Smj162486 /* test for interesting lines = +/- in /etc/passwd */
231*8553SMilan.Jurik@Sun.COM if (op_mode == LIST_COMPAT_MODE) {
232*8553SMilan.Jurik@Sun.COM /* simple + matches all */
233*8553SMilan.Jurik@Sun.COM if ((buf[0] == '+') && (buf[1] == '\0')) {
234*8553SMilan.Jurik@Sun.COM matched = B_TRUE;
235*8553SMilan.Jurik@Sun.COM allow = B_TRUE;
236*8553SMilan.Jurik@Sun.COM break;
237*8553SMilan.Jurik@Sun.COM }
238*8553SMilan.Jurik@Sun.COM
239*8553SMilan.Jurik@Sun.COM /* simple - is not defined */
240*8553SMilan.Jurik@Sun.COM if ((buf[0] == '-') && (buf[1] == '\0')) {
241*8553SMilan.Jurik@Sun.COM __pam_log(LOG_AUTH | LOG_ERR,
242*8553SMilan.Jurik@Sun.COM "pam_list: simple minus unknown, "
243*8553SMilan.Jurik@Sun.COM "illegal line in " PF_PATH);
244*8553SMilan.Jurik@Sun.COM (void) fclose(fd);
245*8553SMilan.Jurik@Sun.COM return (PAM_SERVICE_ERR);
246*8553SMilan.Jurik@Sun.COM }
247*8553SMilan.Jurik@Sun.COM
248*8553SMilan.Jurik@Sun.COM /* @ is not allowed on the first position */
249*8553SMilan.Jurik@Sun.COM if (buf[0] == '@') {
250*8553SMilan.Jurik@Sun.COM __pam_log(LOG_AUTH | LOG_ERR,
251*8553SMilan.Jurik@Sun.COM "pam_list: @ is not allowed on the first "
252*8553SMilan.Jurik@Sun.COM "position in " PF_PATH);
253*8553SMilan.Jurik@Sun.COM (void) fclose(fd);
254*8553SMilan.Jurik@Sun.COM return (PAM_SERVICE_ERR);
255*8553SMilan.Jurik@Sun.COM }
256*8553SMilan.Jurik@Sun.COM
257*8553SMilan.Jurik@Sun.COM /* -user or -@netgroup */
258*8553SMilan.Jurik@Sun.COM if (buf[0] == '-') {
259*8553SMilan.Jurik@Sun.COM allow = B_FALSE;
260*8553SMilan.Jurik@Sun.COM bufp++;
261*8553SMilan.Jurik@Sun.COM /* +user or +@netgroup */
262*8553SMilan.Jurik@Sun.COM } else if (buf[0] == '+') {
263*8553SMilan.Jurik@Sun.COM allow = B_TRUE;
264*8553SMilan.Jurik@Sun.COM bufp++;
265*8553SMilan.Jurik@Sun.COM /* user */
266*8553SMilan.Jurik@Sun.COM } else {
267*8553SMilan.Jurik@Sun.COM allow = B_TRUE;
268*8553SMilan.Jurik@Sun.COM }
269*8553SMilan.Jurik@Sun.COM } else if (op_mode == LIST_PLUS_CHECK) {
2704847Smj162486 if (((buf[0] != '+') && (buf[0] != '-')) ||
2714847Smj162486 (buf[1] == '\0')) {
2724847Smj162486 continue;
2734847Smj162486 }
2744847Smj162486
2754847Smj162486 if (buf[0] == '+') {
2764847Smj162486 allow = B_TRUE;
2774847Smj162486 } else {
2784847Smj162486 allow = B_FALSE;
2794847Smj162486 }
2804847Smj162486 bufp++;
2814847Smj162486 }
2824847Smj162486
2834847Smj162486 /*
2844847Smj162486 * if -> netgroup line
2854847Smj162486 * else -> user line
2864847Smj162486 */
2874847Smj162486 if ((bufp[0] == '@') && (bufp[1] != '\0')) {
2884847Smj162486 bufp++;
2894847Smj162486
2904847Smj162486 if (check_exact) {
2914847Smj162486 if (innetgr(bufp, rhost, username,
2924847Smj162486 NULL) == 1) {
2934847Smj162486 matched = B_TRUE;
2944847Smj162486 break;
2954847Smj162486 }
2964847Smj162486 } else {
2974847Smj162486 if (check_user) {
2984847Smj162486 userok = innetgr(bufp, NULL, username,
2994847Smj162486 NULL);
3004847Smj162486 } else {
3014847Smj162486 userok = 1;
3024847Smj162486 }
3034847Smj162486 if (check_host) {
3044847Smj162486 hostok = innetgr(bufp, rhost, NULL,
3054847Smj162486 NULL);
3064847Smj162486 } else {
3074847Smj162486 hostok = 1;
3084847Smj162486 }
3094847Smj162486 if (userok && hostok) {
3104847Smj162486 matched = B_TRUE;
3114847Smj162486 break;
3124847Smj162486 }
3134847Smj162486 }
3144847Smj162486 } else {
3154847Smj162486 if (check_user) {
3164847Smj162486 if (strcmp(bufp, username) == 0) {
3174847Smj162486 matched = B_TRUE;
3184847Smj162486 break;
3194847Smj162486 }
3204847Smj162486 }
3214847Smj162486 }
322*8553SMilan.Jurik@Sun.COM
323*8553SMilan.Jurik@Sun.COM /*
324*8553SMilan.Jurik@Sun.COM * No match found in /etc/passwd yet. For compat mode
325*8553SMilan.Jurik@Sun.COM * a failure to match should result in a return of
326*8553SMilan.Jurik@Sun.COM * PAM_PERM_DENIED which is achieved below if 'matched'
327*8553SMilan.Jurik@Sun.COM * is false and 'allow' is true.
328*8553SMilan.Jurik@Sun.COM */
329*8553SMilan.Jurik@Sun.COM if (op_mode == LIST_COMPAT_MODE) {
330*8553SMilan.Jurik@Sun.COM allow = B_TRUE;
331*8553SMilan.Jurik@Sun.COM }
3324847Smj162486 }
3334847Smj162486 (void) fclose(fd);
334*8553SMilan.Jurik@Sun.COM
335*8553SMilan.Jurik@Sun.COM if (debug) {
336*8553SMilan.Jurik@Sun.COM __pam_log(LOG_AUTH | LOG_DEBUG,
337*8553SMilan.Jurik@Sun.COM "pam_list: %s for %s", matched ? "matched" : "no match",
338*8553SMilan.Jurik@Sun.COM allow ? "allow" : "deny");
339*8553SMilan.Jurik@Sun.COM }
340*8553SMilan.Jurik@Sun.COM
3414847Smj162486 if (matched) {
3424847Smj162486 return (allow ? PAM_SUCCESS : PAM_PERM_DENIED);
3434847Smj162486 }
344*8553SMilan.Jurik@Sun.COM /*
345*8553SMilan.Jurik@Sun.COM * For compatibility with passwd_compat mode to prevent root access
346*8553SMilan.Jurik@Sun.COM * denied.
347*8553SMilan.Jurik@Sun.COM */
348*8553SMilan.Jurik@Sun.COM if (op_mode == LIST_PLUS_CHECK) {
3494847Smj162486 return (PAM_IGNORE);
3504847Smj162486 }
3514847Smj162486 return (allow ? PAM_PERM_DENIED : PAM_SUCCESS);
3524847Smj162486 }
353