]> git.ipfire.org Git - thirdparty/squid.git/blob - helpers/basic_auth/PAM/basic_pam_auth.cc
Merged from parent (large-rock r12530 including trunk r12732; v3.3.3+).
[thirdparty/squid.git] / helpers / basic_auth / PAM / basic_pam_auth.cc
1 /*
2 * PAM authenticator module for Squid.
3 * Copyright (C) 1999,2002,2003 Henrik Nordstrom <hno@squid-cache.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
18 *
19 * Install instructions:
20 *
21 * This program authenticates users against a PAM configured authentication
22 * service "squid". This allows you to authenticate Squid users to any
23 * authentication source for which you have a PAM module. Commonly available
24 * PAM modules includes "UNIX", RADIUS, Kerberos and SMB, but a lot of other
25 * PAM modules are available from various sources.
26 *
27 * Example PAM configuration for standard UNIX passwd authentication:
28 * /etc/pam.conf:
29 * squid auth required /lib/security/pam_unix.so.1
30 * squid account required /lib/security/pam_unix.so.1
31 *
32 * Note that some PAM modules (for example shadow password authentication)
33 * requires the program to be installed suid root to gain access to the
34 * user password database
35 *
36 * Change Log:
37 *
38 * Version 2.3, 2009-11-06
39 * Converted to C++. Brought into line with Squid-3 code styles.
40 *
41 * Version 2.2, 2003-11-05
42 * One shot mode is now the default mode of operation
43 * with persistent PAM connections enabled by -t option.
44 * Support for clearing the PAM_AUTHTOK attribute on
45 * persistent PAM connections.
46 *
47 * Version 2.1, 2002-08-12
48 * Squid-2.5 support (URL encoded login, password strings)
49 *
50 * Version 2.0, 2002-01-07
51 * One shot mode, command line options
52 * man page
53 *
54 * Version 1.3, 1999-12-10
55 * Bugfix release 1.3 to work around Solaris 2.6
56 * brokenness (not sending arguments to conversation
57 * functions)
58 *
59 * Version 1.2, internal release
60 *
61 * Version 1.1, 1999-05-11
62 * Initial version
63 *
64 * Compile this program with: gcc -o basic_pam_auth basic_pam_auth.cc -lpam -ldl
65 */
66 #include "squid.h"
67 #include "helpers/defines.h"
68 #include "rfc1738.h"
69 #include "util.h"
70
71 #if HAVE_STDIO_H
72 #include <stdio.h>
73 #endif
74 #if HAVE_ASSERT_H
75 #include <assert.h>
76 #endif
77 #if HAVE_STRING_H
78 #include <string.h>
79 #endif
80 #if HAVE_SIGNAL_H
81 #include <signal.h>
82 #endif
83 #if HAVE_TIME_H
84 #include <time.h>
85 #endif
86 #if HAVE_UNISTD_H
87 #include <unistd.h>
88 #endif
89 #if HAVE_SECURITY_PAM_APPL_H
90 #include <security/pam_appl.h>
91 #endif
92
93 /* The default PAM service name */
94 #if !defined(DEFAULT_SQUID_PAM_SERVICE)
95 #define DEFAULT_SQUID_PAM_SERVICE "squid"
96 #endif
97
98 /* The default TTL */
99 #if !defined(DEFAULT_SQUID_PAM_TTL)
100 #define DEFAULT_SQUID_PAM_TTL 0
101 #endif
102
103 #if _SQUID_SOLARIS_
104 static char *password = NULL; /* Workaround for Solaris 2.6 brokenness */
105 #endif
106
107 extern "C" int password_conversation(int num_msg, PAM_CONV_FUNC_CONST_PARM struct pam_message **msg,
108 struct pam_response **resp, void *appdata_ptr);
109
110 /**
111 * A simple "conversation" function returning the supplied password.
112 * Has a bit to much error control, but this is my first PAM application
113 * so I'd rather check everything than make any mistakes. The function
114 * expects a single converstation message of type PAM_PROMPT_ECHO_OFF.
115 */
116 int
117 password_conversation(int num_msg, PAM_CONV_FUNC_CONST_PARM struct pam_message **msg, struct pam_response **resp, void *appdata_ptr)
118 {
119 if (num_msg != 1 || msg[0]->msg_style != PAM_PROMPT_ECHO_OFF) {
120 debug("ERROR: Unexpected PAM converstaion '%d/%s'\n", msg[0]->msg_style, msg[0]->msg);
121 return PAM_CONV_ERR;
122 }
123 #if _SQUID_SOLARIS_
124 if (!appdata_ptr) {
125 /* Workaround for Solaris 2.6 where the PAM library is broken
126 * and does not pass appdata_ptr to the conversation routine
127 */
128 appdata_ptr = password;
129 }
130 #endif
131 if (!appdata_ptr) {
132 debug("ERROR: No password available to password_converstation!\n");
133 return PAM_CONV_ERR;
134 }
135 *resp = static_cast<struct pam_response *>(calloc(num_msg, sizeof(struct pam_response)));
136 if (!*resp) {
137 debug("ERROR: Out of memory!\n");
138 return PAM_CONV_ERR;
139 }
140 (*resp)[0].resp = xstrdup((char *) appdata_ptr);
141 (*resp)[0].resp_retcode = 0;
142
143 return ((*resp)[0].resp ? PAM_SUCCESS : PAM_CONV_ERR);
144 }
145
146 static struct pam_conv conv = {
147 &password_conversation,
148 NULL
149 };
150
151 static void usage(char *program)
152 {
153 fprintf(stderr, "Usage: %s [options..]\n", program);
154 fprintf(stderr, " -n service_name\n");
155 fprintf(stderr, " The PAM service name (default \"%s\")\n", DEFAULT_SQUID_PAM_SERVICE);
156 fprintf(stderr, " -t ttl PAM connection ttl in seconds (default %d)\n", DEFAULT_SQUID_PAM_TTL);
157 fprintf(stderr, " during this time the same connection will be reused\n");
158 fprintf(stderr, " to authenticate all users\n");
159 fprintf(stderr, " -o Do not perform account mgmt (account expiration etc)\n");
160 fprintf(stderr, " -1 Only one user authentication per PAM connection\n");
161 fprintf(stderr, " -r Detect and remove Negotiate/NTLM realm from username\n");
162 }
163
164 int
165 main(int argc, char *argv[])
166 {
167 pam_handle_t *pamh = NULL;
168 int retval = PAM_SUCCESS;
169 char *user;
170 char *password_buf;
171 char buf[HELPER_INPUT_BUFFER];
172 time_t pamh_created = 0;
173 int ttl = DEFAULT_SQUID_PAM_TTL;
174 const char *service = DEFAULT_SQUID_PAM_SERVICE;
175 int no_acct_mgmt = 0;
176 int no_realm = 0;
177
178 /* make standard output line buffered */
179 setvbuf(stdout, NULL, _IOLBF, 0);
180
181 while (1) {
182 int ch = getopt(argc, argv, "1n:t:or");
183 switch (ch) {
184 case -1:
185 goto start;
186 case 'n':
187 service = optarg;
188 break;
189 case 't':
190 ttl = atoi(optarg);
191 break;
192 case '1':
193 ttl = 0;
194 break;
195 case 'o':
196 no_acct_mgmt = 1;
197 break;
198 case 'r':
199 no_realm = 1;
200 break;
201 default:
202 fprintf(stderr, "FATAL: Unknown getopt value '%c'\n", ch);
203 usage(argv[0]);
204 exit(1);
205 }
206 }
207 start:
208 if (optind < argc) {
209 fprintf(stderr, "FATAL: Unknown option '%s'\n", argv[optind]);
210 usage(argv[0]);
211 exit(1);
212 }
213
214 while (fgets(buf, HELPER_INPUT_BUFFER, stdin)) {
215 user = buf;
216 password_buf = strchr(buf, '\n');
217 if (!password_buf) {
218 debug("ERROR: %s: Unexpected input '%s'\n", argv[0], buf);
219 goto error;
220 }
221 *password_buf = '\0';
222 password_buf = strchr(buf, ' ');
223 if (!password_buf) {
224 debug("ERROR: %s: Unexpected input '%s'\n", argv[0], buf);
225 goto error;
226 }
227 *password_buf = '\0';
228 ++password_buf;
229 rfc1738_unescape(user);
230 rfc1738_unescape(password_buf);
231 conv.appdata_ptr = (char *) password_buf; /* from buf above. not allocated */
232
233 if (no_realm) {
234 /* Remove DOMAIN\.. and ...@domain from the user name in case the user
235 * thought this was an NTLM or Negotiate authentication popup box
236 */
237 char * user_ptr = strchr(user, '@');
238 if (user_ptr) *user_ptr = 0;
239 else {
240 user_ptr = strchr(user, '\\');
241 if (user_ptr) user = user_ptr + 1;
242 }
243 }
244
245 #if _SQUID_SOLARIS_
246 /* Workaround for Solaris 2.6 where the PAM library is broken
247 * and does not pass appdata_ptr to the conversation routine
248 */
249 password = password_buf;
250 #endif
251 if (ttl == 0) {
252 /* Create PAM connection */
253 retval = pam_start(service, user, &conv, &pamh);
254 if (retval != PAM_SUCCESS) {
255 debug("ERROR: failed to create PAM authenticator\n");
256 goto error;
257 }
258 } else if (!pamh || (time(NULL) - pamh_created) >= ttl || pamh_created > time(NULL)) {
259 /* Close previous PAM connection */
260 if (pamh) {
261 retval = pam_end(pamh, retval);
262 if (retval != PAM_SUCCESS) {
263 debug("WARNING: failed to release PAM authenticator\n");
264 }
265 pamh = NULL;
266 }
267 /* Initialize persistent PAM connection */
268 retval = pam_start(service, "squid@", &conv, &pamh);
269 if (retval != PAM_SUCCESS) {
270 debug("ERROR: failed to create PAM authenticator\n");
271 goto error;
272 }
273 pamh_created = time(NULL);
274 }
275 /* Authentication */
276 retval = PAM_SUCCESS;
277 if (ttl != 0) {
278 retval = pam_set_item(pamh, PAM_USER, user);
279 if (retval == PAM_SUCCESS)
280 retval = pam_set_item(pamh, PAM_CONV, &conv);
281 }
282 if (retval == PAM_SUCCESS)
283 retval = pam_authenticate(pamh, 0);
284 if (retval == PAM_SUCCESS && !no_acct_mgmt)
285 retval = pam_acct_mgmt(pamh, 0);
286 if (retval == PAM_SUCCESS) {
287 SEND_OK("");
288 } else {
289 error:
290 SEND_ERR("");
291 }
292 /* cleanup */
293 retval = PAM_SUCCESS;
294 #if defined(PAM_AUTHTOK)
295 if (ttl != 0 && pamh) {
296 retval = pam_set_item(pamh, PAM_AUTHTOK, NULL);
297 }
298 #endif
299 if (pamh && (ttl == 0 || retval != PAM_SUCCESS)) {
300 retval = pam_end(pamh, retval);
301 if (retval != PAM_SUCCESS) {
302 debug("WARNING: failed to release PAM authenticator\n");
303 }
304 pamh = NULL;
305 }
306 }
307
308 if (pamh) {
309 retval = pam_end(pamh, retval);
310 if (retval != PAM_SUCCESS) {
311 pamh = NULL;
312 debug("ERROR: failed to release PAM authenticator\n");
313 }
314 }
315 return 0;
316 }