VirtualBox

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

Last change on this file since 48521 was 44528, checked in by vboxsync, 12 years ago

header (C) fixes

  • 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-2012 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 *szUser;
244 char *szPassword;
245} PamContext;
246
247static int conv (int num_msg, const struct pam_message **msg,
248 struct pam_response **resp, void *appdata_ptr)
249{
250 int i;
251 struct pam_response *r;
252
253 PamContext *ctx = (PamContext *)appdata_ptr;
254
255 if (ctx == NULL)
256 {
257 debug_printf("conv: ctx is NULL\n");
258 return PAM_CONV_ERR;
259 }
260
261 debug_printf("conv: num %d u[%s] p[%d]\n", num_msg, ctx->szUser, ctx->szPassword? strlen (ctx->szPassword): 0);
262
263 r = (struct pam_response *) calloc (num_msg, sizeof (struct pam_response));
264
265 if (r == NULL)
266 {
267 return PAM_CONV_ERR;
268 }
269
270 for (i = 0; i < num_msg; i++)
271 {
272 r[i].resp_retcode = 0;
273
274 if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF)
275 {
276 r[i].resp = strdup (ctx->szPassword);
277 debug_printf("conv: %d returning password [%d]\n", i, r[i].resp? strlen (r[i].resp): 0);
278 }
279 else if (msg[i]->msg_style == PAM_PROMPT_ECHO_ON)
280 {
281 r[i].resp = strdup (ctx->szUser);
282 debug_printf("conv: %d returning name [%s]\n", i, r[i].resp);
283 }
284 else
285 {
286 debug_printf("conv: %d style %d: [%s]\n", i, msg[i]->msg_style, msg[i]->msg? msg[i]->msg: "(null)");
287 r[i].resp = NULL;
288 }
289 }
290
291 *resp = r;
292 return PAM_SUCCESS;
293}
294
295/* The entry point must be visible. */
296#if defined(_MSC_VER) || defined(__OS2__)
297# define DECLEXPORT(type) __declspec(dllexport) type
298#else
299# ifdef VBOX_HAVE_VISIBILITY_HIDDEN
300# define DECLEXPORT(type) __attribute__((visibility("default"))) type
301# else
302# define DECLEXPORT(type) type
303# endif
304#endif
305
306/* prototype to prevent gcc warning */
307DECLEXPORT(AuthResult) AUTHCALL AuthEntry(const char *szCaller,
308 PAUTHUUID pUuid,
309 AuthGuestJudgement guestJudgement,
310 const char *szUser,
311 const char *szPassword,
312 const char *szDomain,
313 int fLogon,
314 unsigned clientId);
315DECLEXPORT(AuthResult) AUTHCALL AuthEntry(const char *szCaller,
316 PAUTHUUID pUuid,
317 AuthGuestJudgement guestJudgement,
318 const char *szUser,
319 const char *szPassword,
320 const char *szDomain,
321 int fLogon,
322 unsigned clientId)
323{
324 AuthResult result = AuthResultAccessDenied;
325
326 int rc;
327
328 PamContext ctx;
329 struct pam_conv pam_conversation;
330
331 pam_handle_t *pam_handle = NULL;
332
333 /* Only process logon requests. */
334 if (!fLogon)
335 return result; /* Return value is ignored by the caller. */
336
337 debug_printf("u[%s], d[%s], p[%d]\n", szUser, szDomain, szPassword? strlen (szPassword): 0);
338
339 ctx.szUser = (char *)szUser;
340 ctx.szPassword = (char *)szPassword;
341
342 pam_conversation.conv = conv;
343 pam_conversation.appdata_ptr = &ctx;
344
345 rc = auth_pam_init ();
346
347 if (rc == PAM_SUCCESS)
348 {
349 debug_printf("init ok\n");
350
351 rc = fn_pam_start(auth_get_pam_service (), szUser, &pam_conversation, &pam_handle);
352
353 if (rc == PAM_SUCCESS)
354 {
355 debug_printf("start ok\n");
356
357 rc = fn_pam_authenticate(pam_handle, 0);
358
359 if (rc == PAM_SUCCESS)
360 {
361 debug_printf("auth ok\n");
362
363 rc = fn_pam_acct_mgmt(pam_handle, 0);
364 if (rc == PAM_AUTHINFO_UNAVAIL
365 &&
366 getenv("VBOX_PAM_ALLOW_INACTIVE") != NULL)
367 {
368 debug_printf("PAM_AUTHINFO_UNAVAIL\n");
369 rc = PAM_SUCCESS;
370 }
371
372 if (rc == PAM_SUCCESS)
373 {
374 debug_printf("access granted\n");
375
376 result = AuthResultAccessGranted;
377 }
378 else
379 {
380 debug_printf("pam_acct_mgmt failed %d. %s\n", rc, fn_pam_strerror (pam_handle, rc));
381 }
382 }
383 else
384 {
385 debug_printf("pam_authenticate failed %d. %s\n", rc, fn_pam_strerror (pam_handle, rc));
386 }
387
388 fn_pam_end(pam_handle, rc);
389 }
390 else
391 {
392 debug_printf("pam_start failed %d\n", rc);
393 }
394
395 auth_pam_close ();
396
397 debug_printf("auth_pam_close completed\n");
398 }
399 else
400 {
401 debug_printf("auth_pam_init failed %d\n", rc);
402 }
403
404 return result;
405}
406
407/* Verify the function prototype. */
408static PAUTHENTRY3 gpfnAuthEntry = AuthEntry;
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