VirtualBox

source: vbox/trunk/src/VBox/HostServices/auth/pam/VBoxAuthPAM.c@ 102654

Last change on this file since 102654 was 98103, checked in by vboxsync, 23 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.8 KB
Line 
1/** @file
2 *
3 * VirtualBox External Authentication Library:
4 * Linux PAM Authentication.
5 */
6
7/*
8 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
9 *
10 * This file is part of VirtualBox base platform packages, as
11 * available from https://www.virtualbox.org.
12 *
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation, in version 3 of the
16 * License.
17 *
18 * This program is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, see <https://www.gnu.org/licenses>.
25 *
26 * SPDX-License-Identifier: GPL-3.0-only
27 */
28
29
30/* The PAM service name.
31 *
32 * The service name is the name of a file in the /etc/pam.d which contains
33 * authentication rules. It is possible to use an existing service
34 * name, like "login" for example. But if different set of rules
35 * is required, one can create a new file /etc/pam.d/vrdpauth
36 * specially for VRDP authentication. Note that the name of the
37 * service must be lowercase. See PAM documentation for details.
38 *
39 * The Auth module takes the PAM service name from the
40 * environment variable VBOX_AUTH_PAM_SERVICE. If the variable
41 * is not specified, then the 'login' PAM service is used.
42 */
43#define VBOX_AUTH_PAM_SERVICE_NAME_ENV_OLD "VRDP_AUTH_PAM_SERVICE"
44#define VBOX_AUTH_PAM_SERVICE_NAME_ENV "VBOX_AUTH_PAM_SERVICE"
45#define VBOX_AUTH_PAM_DEFAULT_SERVICE_NAME "login"
46
47
48/* The debug log file name.
49 *
50 * If defined, debug messages will be written to the file specified in the
51 * VBOX_AUTH_DEBUG_FILENAME (or deprecated VRDP_AUTH_DEBUG_FILENAME) environment
52 * variable:
53 *
54 * export VBOX_AUTH_DEBUG_FILENAME=pam.log
55 *
56 * The above will cause writing to the pam.log.
57 */
58#define VBOX_AUTH_DEBUG_FILENAME_ENV_OLD "VRDP_AUTH_DEBUG_FILENAME"
59#define VBOX_AUTH_DEBUG_FILENAME_ENV "VBOX_AUTH_DEBUG_FILENAME"
60
61
62/* Dynamic loading of the PAM library.
63 *
64 * If defined, the libpam.so is loaded dynamically.
65 * Enabled by default since it is often required,
66 * and does not harm.
67 */
68#define VBOX_AUTH_USE_PAM_DLLOAD
69
70
71#ifdef VBOX_AUTH_USE_PAM_DLLOAD
72/* The name of the PAM library */
73# ifdef RT_OS_SOLARIS
74# define PAM_LIB_NAME "libpam.so.1"
75# elif defined(RT_OS_FREEBSD)
76# define PAM_LIB_NAME "libpam.so"
77# else
78# define PAM_LIB_NAME "libpam.so.0"
79# endif
80#endif /* VBOX_AUTH_USE_PAM_DLLOAD */
81
82
83#include <stdio.h>
84#include <stdlib.h>
85#include <stdarg.h>
86#include <string.h>
87#ifndef RT_OS_FREEBSD
88# include <malloc.h>
89#endif
90
91#include <security/pam_appl.h>
92
93#include <VBox/VBoxAuth.h>
94
95#ifdef VBOX_AUTH_USE_PAM_DLLOAD
96#include <dlfcn.h>
97
98static int (*fn_pam_start)(const char *service_name,
99 const char *user,
100 const struct pam_conv *pam_conversation,
101 pam_handle_t **pamh);
102static int (*fn_pam_authenticate)(pam_handle_t *pamh, int flags);
103static int (*fn_pam_acct_mgmt)(pam_handle_t *pamh, int flags);
104static int (*fn_pam_end)(pam_handle_t *pamh, int pam_status);
105static const char * (*fn_pam_strerror)(pam_handle_t *pamh, int errnum);
106#else
107#define fn_pam_start pam_start
108#define fn_pam_authenticate pam_authenticate
109#define fn_pam_acct_mgmt pam_acct_mgmt
110#define fn_pam_end pam_end
111#define fn_pam_strerror pam_strerror
112#endif /* VBOX_AUTH_USE_PAM_DLLOAD */
113
114static void debug_printf(const char *fmt, ...)
115{
116#if defined(VBOX_AUTH_DEBUG_FILENAME_ENV) || defined(VBOX_AUTH_DEBUG_FILENAME_ENV_OLD)
117 va_list va;
118
119 char buffer[1024];
120
121 const char *filename = NULL;
122
123 va_start(va, fmt);
124
125#if defined(VBOX_AUTH_DEBUG_FILENAME_ENV)
126 filename = getenv (VBOX_AUTH_DEBUG_FILENAME_ENV);
127#endif /* VBOX_AUTH_DEBUG_FILENAME_ENV */
128
129#if defined(VBOX_AUTH_DEBUG_FILENAME_ENV_OLD)
130 if (filename == NULL)
131 {
132 filename = getenv (VBOX_AUTH_DEBUG_FILENAME_ENV_OLD);
133 }
134#endif /* VBOX_AUTH_DEBUG_FILENAME_ENV_OLD */
135
136 if (filename)
137 {
138 FILE *f;
139
140 vsnprintf (buffer, sizeof (buffer), fmt, va);
141
142 f = fopen (filename, "ab");
143 if (f != NULL)
144 {
145 fprintf (f, "%s", buffer);
146 fclose (f);
147 }
148 }
149
150 va_end (va);
151#endif /* VBOX_AUTH_DEBUG_FILENAME_ENV || VBOX_AUTH_DEBUG_FILENAME_ENV_OLD */
152}
153
154#ifdef VBOX_AUTH_USE_PAM_DLLOAD
155
156static void *gpvLibPam = NULL;
157
158typedef struct _SymMap
159{
160 void **ppfn;
161 const char *pszName;
162} SymMap;
163
164static SymMap symmap[] =
165{
166 { (void **)&fn_pam_start, "pam_start" },
167 { (void **)&fn_pam_authenticate, "pam_authenticate" },
168 { (void **)&fn_pam_acct_mgmt, "pam_acct_mgmt" },
169 { (void **)&fn_pam_end, "pam_end" },
170 { (void **)&fn_pam_strerror, "pam_strerror" },
171 { NULL, NULL }
172};
173
174static int auth_pam_init(void)
175{
176 SymMap *iter;
177
178 gpvLibPam = dlopen(PAM_LIB_NAME, RTLD_LAZY | RTLD_GLOBAL);
179
180 if (!gpvLibPam)
181 {
182 debug_printf("auth_pam_init: dlopen %s failed\n", PAM_LIB_NAME);
183 return PAM_SYSTEM_ERR;
184 }
185
186 iter = &symmap[0];
187
188 while (iter->pszName != NULL)
189 {
190 void *pv = dlsym (gpvLibPam, iter->pszName);
191
192 if (pv == NULL)
193 {
194 debug_printf("auth_pam_init: dlsym %s failed\n", iter->pszName);
195
196 dlclose(gpvLibPam);
197 gpvLibPam = NULL;
198
199 return PAM_SYSTEM_ERR;
200 }
201
202 *iter->ppfn = pv;
203
204 iter++;
205 }
206
207 return PAM_SUCCESS;
208}
209
210static void auth_pam_close(void)
211{
212 if (gpvLibPam)
213 {
214 dlclose(gpvLibPam);
215 gpvLibPam = NULL;
216 }
217
218 return;
219}
220#else
221static int auth_pam_init(void)
222{
223 return PAM_SUCCESS;
224}
225
226static void auth_pam_close(void)
227{
228 return;
229}
230#endif /* VBOX_AUTH_USE_PAM_DLLOAD */
231
232static const char *auth_get_pam_service (void)
233{
234 const char *service = getenv (VBOX_AUTH_PAM_SERVICE_NAME_ENV);
235
236 if (service == NULL)
237 {
238 service = getenv (VBOX_AUTH_PAM_SERVICE_NAME_ENV_OLD);
239
240 if (service == NULL)
241 {
242 service = VBOX_AUTH_PAM_DEFAULT_SERVICE_NAME;
243 }
244 }
245
246 debug_printf ("Using PAM service: %s\n", service);
247
248 return service;
249}
250
251typedef struct _PamContext
252{
253 char *pszUser;
254 char *pszPassword;
255} PamContext;
256
257#if defined(RT_OS_SOLARIS)
258static int conv (int num_msg, struct pam_message **msg,
259 struct pam_response **resp, void *appdata_ptr)
260#else
261static int conv (int num_msg, const struct pam_message **msg,
262 struct pam_response **resp, void *appdata_ptr)
263#endif
264{
265 int i;
266 struct pam_response *r;
267
268 PamContext *ctx = (PamContext *)appdata_ptr;
269
270 if (ctx == NULL)
271 {
272 debug_printf("conv: ctx is NULL\n");
273 return PAM_CONV_ERR;
274 }
275
276 debug_printf("conv: num %d u[%s] p[%d]\n", num_msg, ctx->pszUser, ctx->pszPassword? strlen (ctx->pszPassword): 0);
277
278 r = (struct pam_response *) calloc (num_msg, sizeof (struct pam_response));
279
280 if (r == NULL)
281 {
282 return PAM_CONV_ERR;
283 }
284
285 for (i = 0; i < num_msg; i++)
286 {
287 r[i].resp_retcode = 0;
288
289 if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF)
290 {
291 r[i].resp = strdup (ctx->pszPassword);
292 debug_printf("conv: %d returning password [%d]\n", i, r[i].resp? strlen (r[i].resp): 0);
293 }
294 else if (msg[i]->msg_style == PAM_PROMPT_ECHO_ON)
295 {
296 r[i].resp = strdup (ctx->pszUser);
297 debug_printf("conv: %d returning name [%s]\n", i, r[i].resp);
298 }
299 else
300 {
301 debug_printf("conv: %d style %d: [%s]\n", i, msg[i]->msg_style, msg[i]->msg? msg[i]->msg: "(null)");
302 r[i].resp = NULL;
303 }
304 }
305
306 *resp = r;
307 return PAM_SUCCESS;
308}
309
310/* The entry point must be visible. */
311#if defined(_MSC_VER) || defined(__OS2__)
312# define DECLEXPORT(type) __declspec(dllexport) type
313#else
314# ifdef VBOX_HAVE_VISIBILITY_HIDDEN
315# define DECLEXPORT(type) __attribute__((visibility("default"))) type
316# else
317# define DECLEXPORT(type) type
318# endif
319#endif
320
321/* prototype to prevent gcc warning */
322DECLEXPORT(AUTHENTRY3) AuthEntry;
323
324DECLEXPORT(AuthResult) AUTHCALL AuthEntry(const char *pszCaller,
325 PAUTHUUID pUuid,
326 AuthGuestJudgement guestJudgement,
327 const char *pszUser,
328 const char *pszPassword,
329 const char *pszDomain,
330 int fLogon,
331 unsigned clientId)
332{
333 AuthResult result = AuthResultAccessDenied;
334 int rc;
335 PamContext ctx;
336 struct pam_conv pam_conversation;
337 pam_handle_t *pam_handle = NULL;
338
339 (void)pszCaller;
340 (void)pUuid;
341 (void)guestJudgement;
342 (void)clientId;
343
344 /* Only process logon requests. */
345 if (!fLogon)
346 return result; /* Return value is ignored by the caller. */
347
348 debug_printf("u[%s], d[%s], p[%d]\n", pszUser, pszDomain, pszPassword ? strlen(pszPassword) : 0);
349
350 ctx.pszUser = (char *)pszUser;
351 ctx.pszPassword = (char *)pszPassword;
352
353 pam_conversation.conv = conv;
354 pam_conversation.appdata_ptr = &ctx;
355
356 rc = auth_pam_init ();
357
358 if (rc == PAM_SUCCESS)
359 {
360 debug_printf("init ok\n");
361
362 rc = fn_pam_start(auth_get_pam_service (), pszUser, &pam_conversation, &pam_handle);
363
364 if (rc == PAM_SUCCESS)
365 {
366 debug_printf("start ok\n");
367
368 rc = fn_pam_authenticate(pam_handle, 0);
369
370 if (rc == PAM_SUCCESS)
371 {
372 debug_printf("auth ok\n");
373
374 rc = fn_pam_acct_mgmt(pam_handle, 0);
375 if (rc == PAM_AUTHINFO_UNAVAIL
376 &&
377 getenv("VBOX_PAM_ALLOW_INACTIVE") != NULL)
378 {
379 debug_printf("PAM_AUTHINFO_UNAVAIL\n");
380 rc = PAM_SUCCESS;
381 }
382
383 if (rc == PAM_SUCCESS)
384 {
385 debug_printf("access granted\n");
386
387 result = AuthResultAccessGranted;
388 }
389 else
390 {
391 debug_printf("pam_acct_mgmt failed %d. %s\n", rc, fn_pam_strerror (pam_handle, rc));
392 }
393 }
394 else
395 {
396 debug_printf("pam_authenticate failed %d. %s\n", rc, fn_pam_strerror (pam_handle, rc));
397 }
398
399 fn_pam_end(pam_handle, rc);
400 }
401 else
402 {
403 debug_printf("pam_start failed %d\n", rc);
404 }
405
406 auth_pam_close ();
407
408 debug_printf("auth_pam_close completed\n");
409 }
410 else
411 {
412 debug_printf("auth_pam_init failed %d\n", rc);
413 }
414
415 return result;
416}
417
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette