VirtualBox

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

Last change on this file since 35993 was 35993, checked in by vboxsync, 14 years ago

typo

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 10.7 KB
Line 
1/** @file
2 *
3 * VirtualBox External Authentication Library:
4 * Linux PAM Authentication.
5 */
6
7/*
8 * Copyright (C) 2006-2011 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# else
66# define PAM_LIB_NAME "libpam.so.0"
67# endif
68#endif /* VBOX_AUTH_USE_PAM_DLLOAD */
69
70
71#include <stdio.h>
72#include <stdlib.h>
73#include <stdarg.h>
74#include <string.h>
75#ifndef RT_OS_FREEBSD
76# include <malloc.h>
77#endif
78
79#include <security/pam_appl.h>
80
81#include <VBox/VBoxAuth.h>
82
83#ifdef VBOX_AUTH_USE_PAM_DLLOAD
84#include <dlfcn.h>
85
86static int (*fn_pam_start)(const char *service_name,
87 const char *user,
88 const struct pam_conv *pam_conversation,
89 pam_handle_t **pamh);
90static int (*fn_pam_authenticate)(pam_handle_t *pamh, int flags);
91static int (*fn_pam_acct_mgmt)(pam_handle_t *pamh, int flags);
92static int (*fn_pam_end)(pam_handle_t *pamh, int pam_status);
93static const char * (*fn_pam_strerror)(pam_handle_t *pamh, int errnum);
94#else
95#define fn_pam_start pam_start
96#define fn_pam_authenticate pam_authenticate
97#define fn_pam_acct_mgmt pam_acct_mgmt
98#define fn_pam_end pam_end
99#define fn_pam_strerror pam_strerror
100#endif /* VBOX_AUTH_USE_PAM_DLLOAD */
101
102static void debug_printf(const char *fmt, ...)
103{
104#if defined(VBOX_AUTH_DEBUG_FILENAME_ENV) || defined(VBOX_AUTH_DEBUG_FILENAME_ENV_OLD)
105 va_list va;
106
107 char buffer[1024];
108
109 const char *filename = NULL;
110
111 va_start(va, fmt);
112
113#if defined(VBOX_AUTH_DEBUG_FILENAME_ENV)
114 filename = getenv (VBOX_AUTH_DEBUG_FILENAME_ENV);
115#endif /* VBOX_AUTH_DEBUG_FILENAME_ENV */
116
117#if defined(VBOX_AUTH_DEBUG_FILENAME_ENV_OLD)
118 if (filename == NULL)
119 {
120 filename = getenv (VBOX_AUTH_DEBUG_FILENAME_ENV_OLD);
121 }
122#endif /* VBOX_AUTH_DEBUG_FILENAME_ENV_OLD */
123
124 if (filename)
125 {
126 FILE *f;
127
128 vsnprintf (buffer, sizeof (buffer), fmt, va);
129
130 f = fopen (filename, "ab");
131 if (f != NULL)
132 {
133 fprintf (f, "%s", buffer);
134 fclose (f);
135 }
136 }
137
138 va_end (va);
139#endif /* VBOX_AUTH_DEBUG_FILENAME_ENV || VBOX_AUTH_DEBUG_FILENAME_ENV_OLD */
140}
141
142#ifdef VBOX_AUTH_USE_PAM_DLLOAD
143
144static void *gpvLibPam = NULL;
145
146typedef struct _SymMap
147{
148 void **ppfn;
149 const char *pszName;
150} SymMap;
151
152static SymMap symmap[] =
153{
154 { (void **)&fn_pam_start, "pam_start" },
155 { (void **)&fn_pam_authenticate, "pam_authenticate" },
156 { (void **)&fn_pam_acct_mgmt, "pam_acct_mgmt" },
157 { (void **)&fn_pam_end, "pam_end" },
158 { (void **)&fn_pam_strerror, "pam_strerror" },
159 { NULL, NULL }
160};
161
162static int auth_pam_init(void)
163{
164 SymMap *iter;
165
166 gpvLibPam = dlopen(PAM_LIB_NAME, RTLD_LAZY | RTLD_GLOBAL);
167
168 if (!gpvLibPam)
169 {
170 debug_printf("auth_pam_init: dlopen %s failed\n", PAM_LIB_NAME);
171 return PAM_SYSTEM_ERR;
172 }
173
174 iter = &symmap[0];
175
176 while (iter->pszName != NULL)
177 {
178 void *pv = dlsym (gpvLibPam, iter->pszName);
179
180 if (pv == NULL)
181 {
182 debug_printf("auth_pam_init: dlsym %s failed\n", iter->pszName);
183
184 dlclose(gpvLibPam);
185 gpvLibPam = NULL;
186
187 return PAM_SYSTEM_ERR;
188 }
189
190 *iter->ppfn = pv;
191
192 iter++;
193 }
194
195 return PAM_SUCCESS;
196}
197
198static void auth_pam_close(void)
199{
200 if (gpvLibPam)
201 {
202 dlclose(gpvLibPam);
203 gpvLibPam = NULL;
204 }
205
206 return;
207}
208#else
209static int auth_pam_init(void)
210{
211 return PAM_SUCCESS;
212}
213
214static void auth_pam_close(void)
215{
216 return;
217}
218#endif /* VBOX_AUTH_USE_PAM_DLLOAD */
219
220static const char *auth_get_pam_service (void)
221{
222 const char *service = getenv (VBOX_AUTH_PAM_SERVICE_NAME_ENV);
223
224 if (service == NULL)
225 {
226 service = getenv (VBOX_AUTH_PAM_SERVICE_NAME_ENV_OLD);
227
228 if (service == NULL)
229 {
230 service = VBOX_AUTH_PAM_DEFAULT_SERVICE_NAME;
231 }
232 }
233
234 debug_printf ("Using PAM service: %s\n", service);
235
236 return service;
237}
238
239typedef struct _PamContext
240{
241 char *szUser;
242 char *szPassword;
243} PamContext;
244
245static int conv (int num_msg, const struct pam_message **msg,
246 struct pam_response **resp, void *appdata_ptr)
247{
248 int i;
249 struct pam_response *r;
250
251 PamContext *ctx = (PamContext *)appdata_ptr;
252
253 if (ctx == NULL)
254 {
255 debug_printf("conv: ctx is NULL\n");
256 return PAM_CONV_ERR;
257 }
258
259 debug_printf("conv: num %d u[%s] p[%d]\n", num_msg, ctx->szUser, ctx->szPassword? strlen (ctx->szPassword): 0);
260
261 r = (struct pam_response *) calloc (num_msg, sizeof (struct pam_response));
262
263 if (r == NULL)
264 {
265 return PAM_CONV_ERR;
266 }
267
268 for (i = 0; i < num_msg; i++)
269 {
270 r[i].resp_retcode = 0;
271
272 if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF)
273 {
274 r[i].resp = strdup (ctx->szPassword);
275 debug_printf("conv: %d returning password [%d]\n", i, r[i].resp? strlen (r[i].resp): 0);
276 }
277 else if (msg[i]->msg_style == PAM_PROMPT_ECHO_ON)
278 {
279 r[i].resp = strdup (ctx->szUser);
280 debug_printf("conv: %d returning name [%s]\n", i, r[i].resp);
281 }
282 else
283 {
284 debug_printf("conv: %d style %d: [%s]\n", i, msg[i]->msg_style, msg[i]->msg? msg[i]->msg: "(null)");
285 r[i].resp = NULL;
286 }
287 }
288
289 *resp = r;
290 return PAM_SUCCESS;
291}
292
293/* The entry point must be visible. */
294#if defined(_MSC_VER) || defined(__OS2__)
295# define DECLEXPORT(type) __declspec(dllexport) type
296#else
297# ifdef VBOX_HAVE_VISIBILITY_HIDDEN
298# define DECLEXPORT(type) __attribute__((visibility("default"))) type
299# else
300# define DECLEXPORT(type) type
301# endif
302#endif
303
304/* prototype to prevent gcc warning */
305DECLEXPORT(AuthResult) AUTHCALL AuthEntry(const char *szCaller,
306 PAUTHUUID pUuid,
307 AuthGuestJudgement guestJudgement,
308 const char *szUser,
309 const char *szPassword,
310 const char *szDomain,
311 int fLogon,
312 unsigned clientId);
313DECLEXPORT(AuthResult) AUTHCALL AuthEntry(const char *szCaller,
314 PAUTHUUID pUuid,
315 AuthGuestJudgement guestJudgement,
316 const char *szUser,
317 const char *szPassword,
318 const char *szDomain,
319 int fLogon,
320 unsigned clientId)
321{
322 AuthResult result = AuthResultAccessDenied;
323
324 int rc;
325
326 PamContext ctx;
327 struct pam_conv pam_conversation;
328
329 pam_handle_t *pam_handle = NULL;
330
331 /* Only process logon requests. */
332 if (!fLogon)
333 return result; /* Return value is ignored by the caller. */
334
335 debug_printf("u[%s], d[%s], p[%d]\n", szUser, szDomain, szPassword? strlen (szPassword): 0);
336
337 ctx.szUser = (char *)szUser;
338 ctx.szPassword = (char *)szPassword;
339
340 pam_conversation.conv = conv;
341 pam_conversation.appdata_ptr = &ctx;
342
343 rc = auth_pam_init ();
344
345 if (rc == PAM_SUCCESS)
346 {
347 debug_printf("init ok\n");
348
349 rc = fn_pam_start(auth_get_pam_service (), szUser, &pam_conversation, &pam_handle);
350
351 if (rc == PAM_SUCCESS)
352 {
353 debug_printf("start ok\n");
354
355 rc = fn_pam_authenticate(pam_handle, 0);
356
357 if (rc == PAM_SUCCESS)
358 {
359 debug_printf("auth ok\n");
360
361 rc = fn_pam_acct_mgmt(pam_handle, 0);
362 if (rc == PAM_AUTHINFO_UNAVAIL
363 &&
364 getenv("VBOX_PAM_ALLOW_INACTIVE") != NULL)
365 {
366 debug_printf("PAM_AUTHINFO_UNAVAIL\n");
367 rc = PAM_SUCCESS;
368 }
369
370 if (rc == PAM_SUCCESS)
371 {
372 debug_printf("access granted\n");
373
374 result = AuthResultAccessGranted;
375 }
376 else
377 {
378 debug_printf("pam_acct_mgmt failed %d. %s\n", rc, fn_pam_strerror (pam_handle, rc));
379 }
380 }
381 else
382 {
383 debug_printf("pam_authenticate failed %d. %s\n", rc, fn_pam_strerror (pam_handle, rc));
384 }
385
386 fn_pam_end(pam_handle, rc);
387 }
388 else
389 {
390 debug_printf("pam_start failed %d\n", rc);
391 }
392
393 auth_pam_close ();
394
395 debug_printf("auth_pam_close completed\n");
396 }
397 else
398 {
399 debug_printf("auth_pam_init failed %d\n", rc);
400 }
401
402 return result;
403}
404
405/* Verify the function prototype. */
406static 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