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