VirtualBox

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

Last change on this file since 94069 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

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