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.
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.
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]
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
29 #include <sys/types.h>
36 #include <security/pam_appl.h>
37 #include <security/pam_modules.h>
38 #include <security/pam_impl.h>
40 #define ILLEGAL_COMBINATION "pam_list: illegal combination of options"
49 string_mode_type(pam_list_mode_t op_mode
, boolean_t allow
)
51 return ((op_mode
== LIST_COMPAT_MODE
) ? "compat" :
52 (allow
? "allow" : "deny"));
56 log_illegal_combination(const char *s1
, const char *s2
)
58 __pam_log(LOG_AUTH
| LOG_ERR
, ILLEGAL_COMBINATION
59 " %s and %s", s1
, s2
);
64 pam_sm_acct_mgmt(pam_handle_t
*pamh
, int flags
, int argc
, const char **argv
)
67 const char *allowdeny_filename
= PF_PATH
;
69 char hostname
[MAXHOSTNAMELEN
];
70 char *username
= NULL
;
77 int allow_deny_test
= 0;
78 boolean_t debug
= B_FALSE
;
79 boolean_t allow
= B_FALSE
;
80 boolean_t matched
= B_FALSE
;
81 boolean_t check_user
= B_TRUE
;
82 boolean_t check_host
= B_FALSE
;
83 boolean_t check_exact
= B_FALSE
;
84 pam_list_mode_t op_mode
= LIST_PLUS_CHECK
;
86 for (i
= 0; i
< argc
; ++i
) {
87 if (strncasecmp(argv
[i
], "debug", sizeof ("debug")) == 0) {
89 } else if (strncasecmp(argv
[i
], "user", sizeof ("user")) == 0) {
91 } else if (strncasecmp(argv
[i
], "nouser",
92 sizeof ("nouser")) == 0) {
94 } else if (strncasecmp(argv
[i
], "host", sizeof ("host")) == 0) {
96 } else if (strncasecmp(argv
[i
], "nohost",
97 sizeof ("nohost")) == 0) {
99 } else if (strncasecmp(argv
[i
], "user_host_exact",
100 sizeof ("user_host_exact")) == 0) {
101 check_exact
= B_TRUE
;
102 } else if (strcasecmp(argv
[i
], "compat") == 0) {
103 if (op_mode
== LIST_PLUS_CHECK
) {
104 op_mode
= LIST_COMPAT_MODE
;
106 log_illegal_combination("compat",
107 string_mode_type(op_mode
, allow
));
108 return (PAM_SERVICE_ERR
);
110 } else if (strncasecmp(argv
[i
], "allow=",
111 sizeof ("allow=") - 1) == 0) {
112 if (op_mode
== LIST_PLUS_CHECK
) {
113 allowdeny_filename
= argv
[i
] +
114 sizeof ("allow=") - 1;
116 op_mode
= LIST_EXTERNAL_FILE
;
119 log_illegal_combination("allow",
120 string_mode_type(op_mode
, allow
));
121 return (PAM_SERVICE_ERR
);
123 } else if (strncasecmp(argv
[i
], "deny=",
124 sizeof ("deny=") - 1) == 0) {
125 if (op_mode
== LIST_PLUS_CHECK
) {
126 allowdeny_filename
= argv
[i
] +
127 sizeof ("deny=") - 1;
129 op_mode
= LIST_EXTERNAL_FILE
;
132 log_illegal_combination("deny",
133 string_mode_type(op_mode
, allow
));
134 return (PAM_SERVICE_ERR
);
137 __pam_log(LOG_AUTH
| LOG_ERR
,
138 "pam_list: illegal option %s", argv
[i
]);
139 return (PAM_SERVICE_ERR
);
143 if (((check_user
|| check_host
|| check_exact
) == B_FALSE
) ||
144 (allow_deny_test
> 1)) {
145 __pam_log(LOG_AUTH
| LOG_ERR
, ILLEGAL_COMBINATION
);
146 return (PAM_SERVICE_ERR
);
149 if ((op_mode
== LIST_COMPAT_MODE
) && (check_user
== B_FALSE
)) {
150 log_illegal_combination("compat", "nouser");
151 return (PAM_SERVICE_ERR
);
155 __pam_log(LOG_AUTH
| LOG_DEBUG
,
156 "pam_list: check_user = %d, check_host = %d,"
157 "check_exact = %d\n",
158 check_user
, check_host
, check_exact
);
160 __pam_log(LOG_AUTH
| LOG_DEBUG
,
161 "pam_list: auth_file: %s, %s\n", allowdeny_filename
,
162 (op_mode
== LIST_COMPAT_MODE
) ? "compat mode" :
163 (allow
? "allow file" : "deny file"));
166 (void) pam_get_item(pamh
, PAM_USER
, (void**)&username
);
168 if ((check_user
|| check_exact
) && ((username
== NULL
) ||
169 (*username
== '\0'))) {
170 __pam_log(LOG_AUTH
| LOG_ERR
,
171 "pam_list: username not supplied, critical error");
172 return (PAM_USER_UNKNOWN
);
175 (void) pam_get_item(pamh
, PAM_RHOST
, (void**)&rhost
);
177 if ((check_host
|| check_exact
) && ((rhost
== NULL
) ||
179 if (gethostname(hostname
, MAXHOSTNAMELEN
) == 0) {
182 __pam_log(LOG_AUTH
| LOG_ERR
,
183 "pam_list: error by gethostname - %m");
184 return (PAM_SERVICE_ERR
);
189 __pam_log(LOG_AUTH
| LOG_DEBUG
,
190 "pam_list: pam_sm_acct_mgmt for (%s,%s,)",
191 (rhost
!= NULL
) ? rhost
: "", username
);
194 if (strlen(allowdeny_filename
) == 0) {
195 __pam_log(LOG_AUTH
| LOG_ERR
,
196 "pam_list: file name not specified");
197 return (PAM_SERVICE_ERR
);
200 if ((fd
= fopen(allowdeny_filename
, "rF")) == NULL
) {
201 __pam_log(LOG_AUTH
| LOG_ERR
, "pam_list: fopen of %s: %s",
202 allowdeny_filename
, strerror(errno
));
203 return (PAM_SERVICE_ERR
);
206 while (fgets(buf
, BUFSIZ
, fd
) != NULL
) {
207 /* lines longer than BUFSIZ-1 */
208 if ((strlen(buf
) == (BUFSIZ
- 1)) &&
209 (buf
[BUFSIZ
- 2] != '\n')) {
210 while ((fgetc(fd
) != '\n') && (!feof(fd
))) {
213 __pam_log(LOG_AUTH
| LOG_DEBUG
,
214 "pam_list: long line in file,"
215 "more than %d chars, the rest ignored", BUFSIZ
- 1);
218 /* remove unneeded colons if necessary */
219 if ((limit
= strpbrk(buf
, ":\n")) != NULL
) {
223 /* ignore free values */
224 if (buf
[0] == '\0') {
230 /* test for interesting lines = +/- in /etc/passwd */
231 if (op_mode
== LIST_COMPAT_MODE
) {
232 /* simple + matches all */
233 if ((buf
[0] == '+') && (buf
[1] == '\0')) {
239 /* simple - is not defined */
240 if ((buf
[0] == '-') && (buf
[1] == '\0')) {
241 __pam_log(LOG_AUTH
| LOG_ERR
,
242 "pam_list: simple minus unknown, "
243 "illegal line in " PF_PATH
);
245 return (PAM_SERVICE_ERR
);
248 /* @ is not allowed on the first position */
250 __pam_log(LOG_AUTH
| LOG_ERR
,
251 "pam_list: @ is not allowed on the first "
252 "position in " PF_PATH
);
254 return (PAM_SERVICE_ERR
);
257 /* -user or -@netgroup */
261 /* +user or +@netgroup */
262 } else if (buf
[0] == '+') {
269 } else if (op_mode
== LIST_PLUS_CHECK
) {
270 if (((buf
[0] != '+') && (buf
[0] != '-')) ||
284 * if -> netgroup line
287 if ((bufp
[0] == '@') && (bufp
[1] != '\0')) {
291 if (innetgr(bufp
, rhost
, username
,
298 userok
= innetgr(bufp
, NULL
, username
,
304 hostok
= innetgr(bufp
, rhost
, NULL
,
309 if (userok
&& hostok
) {
316 if (strcmp(bufp
, username
) == 0) {
324 * No match found in /etc/passwd yet. For compat mode
325 * a failure to match should result in a return of
326 * PAM_PERM_DENIED which is achieved below if 'matched'
327 * is false and 'allow' is true.
329 if (op_mode
== LIST_COMPAT_MODE
) {
336 __pam_log(LOG_AUTH
| LOG_DEBUG
,
337 "pam_list: %s for %s", matched
? "matched" : "no match",
338 allow
? "allow" : "deny");
342 return (allow
? PAM_SUCCESS
: PAM_PERM_DENIED
);
345 * For compatibility with passwd_compat mode to prevent root access
348 if (op_mode
== LIST_PLUS_CHECK
) {
351 return (allow
? PAM_PERM_DENIED
: PAM_SUCCESS
);