VirtualBox

source: vbox/trunk/src/VBox/Additions/common/pam/pam_vbox.cpp@ 38468

Last change on this file since 38468 was 38468, checked in by vboxsync, 13 years ago

PAM: First implementation of waiting for credentials.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.9 KB
Line 
1/* $Id: pam_vbox.cpp 38468 2011-08-16 09:45:38Z vboxsync $ */
2/** @file
3 * pam_vbox - PAM module for auto logons.
4 */
5
6/*
7 * Copyright (C) 2008-2011 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/*******************************************************************************
19* Header Files *
20*******************************************************************************/
21#define PAM_SM_AUTH
22#define PAM_SM_ACCOUNT
23#define PAM_SM_PASSWORD
24#define PAM_SM_SESSION
25
26#ifdef DEBUG
27# define PAM_DEBUG
28#endif
29
30#ifdef RT_OS_SOLARIS
31# include <security/pam_appl.h>
32#endif
33#include <security/pam_modules.h>
34#include <security/pam_appl.h>
35#ifdef RT_OS_LINUX
36# include <security/_pam_macros.h>
37#endif
38
39#include <pwd.h>
40#include <syslog.h>
41
42#include <iprt/assert.h>
43#include <iprt/env.h>
44#include <iprt/initterm.h>
45#include <iprt/mem.h>
46#include <iprt/stream.h>
47#include <iprt/string.h>
48
49#include <VBox/VBoxGuestLib.h>
50
51#include <VBox/log.h>
52#ifdef VBOX_WITH_GUEST_PROPS
53# include <VBox/HostServices/GuestPropertySvc.h>
54 using namespace guestProp;
55#endif
56
57#define VBOX_MODULE_NAME "pam_vbox"
58
59/** For debugging. */
60#ifdef DEBUG
61static pam_handle_t *g_pam_handle;
62static int g_verbosity = 99;
63#else
64static int g_verbosity = 0;
65#endif
66
67/**
68 * Write to system log.
69 *
70 * @param pszBuf Buffer to write to the log (NULL-terminated)
71 */
72static void pam_vbox_writesyslog(char *pszBuf)
73{
74#ifdef RT_OS_LINUX
75 openlog("pam_vbox", LOG_PID, LOG_AUTHPRIV);
76 syslog(LOG_ERR, "%s", pszBuf);
77 closelog();
78#elif defined(RT_OS_SOLARIS)
79 syslog(LOG_ERR, "pam_vbox: %s\n", pszBuf);
80#endif
81}
82
83
84/**
85 * Displays an error message.
86 *
87 * @param pszFormat The message text.
88 * @param ... Format arguments.
89 */
90static void pam_vbox_error(pam_handle_t *hPAM, const char *pszFormat, ...)
91{
92 va_list va;
93 char *buf;
94 va_start(va, pszFormat);
95 if (RT_SUCCESS(RTStrAPrintfV(&buf, pszFormat, va)))
96 {
97 LogRel(("%s: Error: %s", VBOX_MODULE_NAME, buf));
98 pam_vbox_writesyslog(buf);
99 RTStrFree(buf);
100 }
101 va_end(va);
102}
103
104
105/**
106 * Displays a debug message.
107 *
108 * @param pszFormat The message text.
109 * @param ... Format arguments.
110 */
111static void pam_vbox_log(pam_handle_t *hPAM, const char *pszFormat, ...)
112{
113 if (g_verbosity)
114 {
115 va_list va;
116 char *buf;
117 va_start(va, pszFormat);
118 if (RT_SUCCESS(RTStrAPrintfV(&buf, pszFormat, va)))
119 {
120 /* Only do normal logging in debug mode; could contain
121 * sensitive data! */
122 LogRel(("%s: %s", VBOX_MODULE_NAME, buf));
123 /* Log to syslog */
124 pam_vbox_writesyslog(buf);
125 RTStrFree(buf);
126 }
127 va_end(va);
128 }
129}
130
131
132static int vbox_set_msg(pam_handle_t *hPAM, int iStyle, const char *pszText)
133{
134 AssertPtrReturn(hPAM, VERR_INVALID_POINTER);
135 AssertPtrReturn(pszText, VERR_INVALID_POINTER);
136
137 if (!iStyle)
138 iStyle = PAM_TEXT_INFO;
139
140 int rc = VINF_SUCCESS;
141
142 struct pam_message msg;
143 msg.msg_style = iStyle;
144 msg.msg = pszText;
145
146 const struct pam_conv *conv;
147 int pamrc = pam_get_item(hPAM, PAM_CONV, (const void **)&conv);
148 if (pamrc == PAM_SUCCESS)
149 {
150 struct pam_response *resp;
151 const struct pam_message *msg_p = &msg;
152 pamrc = conv->conv(1 /* One message only */, &msg_p, &resp, conv->appdata_ptr);
153 if (pamrc == PAM_SUCCESS)
154 {
155 pam_vbox_log(hPAM, "Showing message (type %d): %s", msg, pszText);
156 free(resp);
157 }
158 }
159 else
160 rc = VERR_NOT_FOUND;
161 return rc;
162}
163
164
165static int pam_vbox_init(pam_handle_t *hPAM)
166{
167#ifdef DEBUG
168 g_pam_handle = hPAM; /* hack for getting assertion text */
169#endif
170
171 /* Don't make assertions panic because the PAM stack +
172 * the current logon module won't work anymore (or just restart).
173 * This could result in not able to log into the system anymore. */
174 RTAssertSetMayPanic(false);
175
176 int rc = RTR3Init();
177 if (RT_FAILURE(rc))
178 {
179 pam_vbox_error(hPAM, "pam_vbox_init: could not init runtime! rc=%Rrc. Aborting\n", rc);
180 return PAM_SUCCESS; /* Jump out as early as we can to not mess around. */
181 }
182
183 pam_vbox_log(hPAM, "pam_vbox_init: runtime initialized\n");
184 if (RT_SUCCESS(rc))
185 {
186 rc = VbglR3InitUser();
187 if (RT_FAILURE(rc))
188 {
189 switch(rc)
190 {
191 case VERR_ACCESS_DENIED:
192 pam_vbox_error(hPAM, "pam_vbox_init: access is denied to guest driver! Please make sure you run with sufficient rights. Aborting\n");
193 break;
194
195 case VERR_FILE_NOT_FOUND:
196 pam_vbox_error(hPAM, "pam_vbox_init: guest driver not found! Guest Additions installed? Aborting\n");
197 break;
198
199 default:
200 pam_vbox_error(hPAM, "pam_vbox_init: could not init VbglR3 library! rc=%Rrc. Aborting\n", rc);
201 break;
202 }
203 }
204 pam_vbox_log(hPAM, "pam_vbox_init: guest lib initialized\n");
205 }
206
207 if (RT_SUCCESS(rc))
208 {
209 char *rhost = NULL;
210 char *tty = NULL;
211 char *prompt = NULL;
212#ifdef RT_OS_SOLARIS
213 pam_get_item(hPAM, PAM_RHOST, (void**) &rhost);
214 pam_get_item(hPAM, PAM_TTY, (void**) &tty);
215 pam_get_item(hPAM, PAM_USER_PROMPT, (void**) &prompt);
216#else
217 pam_get_item(hPAM, PAM_RHOST, (const void**) &rhost);
218 pam_get_item(hPAM, PAM_TTY, (const void**) &tty);
219 pam_get_item(hPAM, PAM_USER_PROMPT, (const void**) &prompt);
220#endif
221 pam_vbox_log(hPAM, "pam_vbox_init: rhost=%s, tty=%s, prompt=%s\n",
222 rhost ? rhost : "<none>", tty ? tty : "<none>", prompt ? prompt : "<none>");
223 }
224
225 return rc;
226}
227
228
229static void pam_vbox_shutdown(pam_handle_t *hPAM)
230{
231 VbglR3Term();
232}
233
234
235static int pam_vbox_check_creds(pam_handle_t *hPAM)
236{
237 int rc = VbglR3CredentialsQueryAvailability();
238 if (RT_FAILURE(rc))
239 {
240 if (rc == VERR_NOT_FOUND)
241 pam_vbox_log(hPAM, "pam_vbox_check_creds: no credentials available\n");
242 else
243 pam_vbox_error(hPAM, "pam_vbox_check_creds: could not query for credentials! rc=%Rrc. Aborting\n", rc);
244 }
245 else
246 {
247 char *pszUsername;
248 char *pszPassword;
249 char *pszDomain;
250
251 rc = VbglR3CredentialsRetrieve(&pszUsername, &pszPassword, &pszDomain);
252 if (RT_FAILURE(rc))
253 {
254 pam_vbox_error(hPAM, "pam_vbox_check_creds: could not retrieve credentials! rc=%Rrc. Aborting\n", rc);
255 }
256 else
257 {
258#ifdef DEBUG
259 pam_vbox_log(hPAM, "pam_vbox_check_creds: credentials retrieved: user=%s, password=%s, domain=%s\n",
260 pszUsername, pszPassword, pszDomain);
261#else
262 /* Don't log passwords in release mode! */
263 pam_vbox_log(hPAM, "pam_vbox_check_creds: credentials retrieved: user=%s, password=XXX, domain=%s\n",
264 pszUsername, pszDomain);
265#endif
266 /* Fill credentials into PAM. */
267 int pamrc = pam_set_item(hPAM, PAM_USER, pszUsername);
268 if (pamrc != PAM_SUCCESS)
269 {
270 pam_vbox_error(hPAM, "pam_vbox_check_creds: could not set user name! pamrc=%d, msg=%s. Aborting\n",
271 pamrc, pam_strerror(hPAM, pamrc));
272 }
273 else
274 {
275 pamrc = pam_set_item(hPAM, PAM_AUTHTOK, pszPassword);
276 if (pamrc != PAM_SUCCESS)
277 pam_vbox_error(hPAM, "pam_vbox_check_creds: could not set password! pamrc=%d, msg=%s. Aborting\n",
278 pamrc, pam_strerror(hPAM, pamrc));
279 }
280 /** @todo Add handling domains as well. */
281
282 VbglR3CredentialsDestroy(pszUsername, pszPassword, pszDomain,
283 3 /* Three wipe passes */);
284 pam_vbox_log(hPAM, "pam_vbox_check_creds: returned with pamrc=%d, msg=%s\n",
285 pamrc, pam_strerror(hPAM, pamrc));
286 }
287 }
288
289 pam_vbox_log(hPAM, "pam_vbox_check_creds: returned with rc=%Rrc\n", rc);
290 return rc;
291}
292
293#ifdef VBOX_WITH_GUEST_PROPS
294static int pam_vbox_wait_for_creds(pam_handle_t *hPAM, uint32_t uClientID, uint32_t uTimeoutMS)
295{
296 int rc;
297
298 /* The buffer for storing the data and its initial size. We leave a bit
299 * of space here in case the maximum values are raised. */
300 void *pvBuf = NULL;
301 uint32_t cbBuf = MAX_NAME_LEN + MAX_VALUE_LEN + MAX_FLAGS_LEN + _1K;
302
303 pam_vbox_log(hPAM, "Waiting for credentials (%dms) ...\n", uTimeoutMS);
304
305 int i;
306 for (i = 0; i < 10; i++)
307 {
308 void *pvTmpBuf = RTMemRealloc(pvBuf, cbBuf);
309 if (pvTmpBuf)
310 {
311 char *pszName = NULL;
312 char *pszValue = NULL;
313 uint64_t u64TimestampOut = 0;
314 char *pszFlags = NULL;
315
316 pvBuf = pvTmpBuf;
317 rc = VbglR3GuestPropWait(uClientID, "/VirtualBox/GuestAdd/PAM/CredsChanged", pvBuf, cbBuf,
318 0 /* Last timestamp; just wait for next event */, uTimeoutMS,
319 &pszName, &pszValue, &u64TimestampOut,
320 &pszFlags, &cbBuf);
321 }
322 else
323 rc = VERR_NO_MEMORY;
324
325 switch (rc)
326 {
327 case VINF_SUCCESS:
328 pam_vbox_error(hPAM, "Got notification for supplied credentials\n");
329 break;
330
331 case VERR_BUFFER_OVERFLOW:
332 {
333 /* Buffer too small, try it with a bigger one next time. */
334 cbBuf += _1K;
335 continue; /* Try next round. */
336 }
337
338 case VERR_INTERRUPTED:
339 pam_vbox_error(hPAM, "The credentials notification request timed out or was interrupted\n");
340 break;
341
342 case VERR_TIMEOUT:
343 pam_vbox_error(hPAM, "Credentials did not arrive within time (%dms)\n", uTimeoutMS);
344 break;
345
346 case VERR_TOO_MUCH_DATA:
347 pam_vbox_error(hPAM, "Temporarily unable to get credentials notification\n");
348 break;
349
350 default:
351 pam_vbox_error(hPAM, "The credentials notification request failed with rc=%Rrc\n", rc);
352 break;
353 }
354
355 /* Everything except VERR_BUFFER_OVERLOW makes us bail out ... */
356 break;
357 }
358
359 pam_vbox_log(hPAM, "Waiting for credentials returned with rc=%Rrc\n", rc);
360 return rc;
361}
362
363static int pam_vbox_read_prop(pam_handle_t *hPAM, uint32_t clientid, const char *pszKey,
364 char *pszValue, size_t cbValue)
365{
366 AssertReturn(clientid, VERR_INVALID_PARAMETER);
367 AssertPtrReturn(pszKey, VERR_INVALID_POINTER);
368 AssertPtrReturn(pszValue, VERR_INVALID_POINTER);
369
370 int rc = VbglR3GuestPropReadValue(clientid, pszKey,
371 pszValue, cbValue, NULL /* Actual size, not required. */);
372 pam_vbox_log(hPAM, "pam_vbox_read_prop: read key \"%s\" with rc=%Rrc\n",
373 pszKey, rc);
374 if (RT_SUCCESS(rc))
375 pam_vbox_log(hPAM, "pam_vbox_read_prop: key \"%s\"=\"%s\"\n",
376 pszKey, pszValue);
377 return rc;
378}
379#endif
380
381
382/**
383 * Performs authentication within the PAM framework.
384 *
385 * @todo
386 */
387DECLEXPORT(int) pam_sm_authenticate(pam_handle_t *hPAM, int iFlags,
388 int argc, const char **argv)
389{
390 /* Parse arguments. */
391 int i;
392 for (i = 0; i < argc; i++)
393 {
394 if (!RTStrICmp(argv[i], "debug"))
395 g_verbosity = 1;
396 else
397 pam_vbox_error(hPAM, "pam_sm_authenticate: unknown command line argument \"%s\"\n", argv[i]);
398 }
399 pam_vbox_log(hPAM, "pam_vbox_authenticate called\n");
400
401 int rc = pam_vbox_init(hPAM);
402 if (RT_FAILURE(rc))
403 return PAM_SUCCESS; /* Jump out as early as we can to not mess around. */
404
405 bool fFallback = true;
406
407#ifdef VBOX_WITH_GUEST_PROPS
408 uint32_t uClientId;
409 rc = VbglR3GuestPropConnect(&uClientId);
410 if (RT_SUCCESS(rc))
411 {
412 char szVal[256];
413 rc = pam_vbox_read_prop(hPAM,uClientId, "/VirtualBox/GuestAdd/PAM/CredsWait",
414 szVal, sizeof(szVal));
415 if (RT_SUCCESS(rc))
416 {
417 /* All calls which are checked against rc2 are not critical, e.g. it does
418 * not matter if they succeed or not. */
419 uint32_t uTimeoutMS = RT_INDEFINITE_WAIT; /* Wait infinite by default. */
420 int rc2 = pam_vbox_read_prop(hPAM, uClientId, "/VirtualBox/GuestAdd/PAM/CredsWaitTimeout",
421 szVal, sizeof(szVal));
422 if (RT_SUCCESS(rc2))
423 {
424 uTimeoutMS = RTStrToUInt32(szVal);
425 if (!uTimeoutMS)
426 {
427 pam_vbox_error(hPAM, "pam_sm_authenticate: invalid waiting timeout value specified, defaulting to infinite timeout\n");
428 uTimeoutMS = RT_INDEFINITE_WAIT;
429 }
430 else
431 uTimeoutMS = uTimeoutMS * 1000; /* Make ms out of s. */
432 }
433
434 rc2 = pam_vbox_read_prop(hPAM, uClientId, "/VirtualBox/GuestAdd/PAM/CredsMsgWaiting",
435 szVal, sizeof(szVal));
436 const char *pszWaitMsg = NULL;
437 if (RT_SUCCESS(rc2))
438 pszWaitMsg = szVal;
439
440 rc2 = vbox_set_msg(hPAM, 0 /* Info message */,
441 pszWaitMsg ? pszWaitMsg : "Waiting for credentials ...");
442 if (RT_FAILURE(rc2)) /* Not critical. */
443 pam_vbox_error(hPAM, "pam_sm_authenticate: error setting waiting information message, rc=%Rrc\n", rc2);
444
445 if (RT_SUCCESS(rc))
446 {
447 /* Before we actuall wait for credentials just make sure we didn't already get credentials
448 * set so that we can skip waiting for them ... */
449 rc = pam_vbox_check_creds(hPAM);
450 if (rc == VERR_NOT_FOUND)
451 {
452 rc = pam_vbox_wait_for_creds(hPAM, uClientId, uTimeoutMS);
453 if (RT_SUCCESS(rc))
454 {
455 /* Waiting for credentials succeeded, try getting those ... */
456 rc = pam_vbox_check_creds(hPAM);
457 if (RT_FAILURE(rc))
458 pam_vbox_error(hPAM, "pam_sm_authenticate: no credentials given, even when waited for it, rc=%Rrc\n", rc);
459 }
460 else if (rc == VERR_TIMEOUT)
461 {
462 pam_vbox_log(hPAM, "pam_sm_authenticate: no credentials given within time\n", rc);
463
464 rc2 = pam_vbox_read_prop(hPAM, uClientId, "/VirtualBox/GuestAdd/PAM/CredsMsgWaitTimeout",
465 szVal, sizeof(szVal));
466 if (RT_SUCCESS(rc2))
467 rc2 = vbox_set_msg(hPAM, 0 /* Info message */, szVal);
468 }
469 }
470
471 /* If we got here we don't need the fallback, so just deactivate it. */
472 fFallback = false;
473 }
474 }
475
476 VbglR3GuestPropDisconnect(uClientId);
477 }
478#endif /* VBOX_WITH_GUEST_PROPS */
479
480 if (fFallback)
481 {
482 pam_vbox_log(hPAM, "pam_vbox_authenticate: falling back to old method\n");
483
484 /* If anything went wrong in the code above we just do a credentials
485 * check like it was before: Try retrieving the stuff and authenticating. */
486 int rc2 = pam_vbox_check_creds(hPAM);
487 if (RT_SUCCESS(rc))
488 rc = rc2;
489 }
490
491 pam_vbox_shutdown(hPAM);
492
493 pam_vbox_log(hPAM, "pam_vbox_authenticate: overall result rc=%Rrc\n", rc);
494
495 /* Never report an error here because if no credentials from the host are available or something
496 * went wrong we then let do the authentication by the next module in the stack. */
497
498 /* We report success here because this is all we can do right now -- we passed the credentials
499 * to the next PAM module in the block above which then might do a shadow (like pam_unix/pam_unix2)
500 * password verification to "really" authenticate the user. */
501 return PAM_SUCCESS;
502}
503
504
505/**
506 * Modifies / deletes user credentials
507 *
508 * @todo
509 */
510DECLEXPORT(int) pam_sm_setcred(pam_handle_t *hPAM, int iFlags, int argc, const char **argv)
511{
512 pam_vbox_log(hPAM, "pam_vbox_setcred called\n");
513 return PAM_SUCCESS;
514}
515
516
517DECLEXPORT(int) pam_sm_acct_mgmt(pam_handle_t *hPAM, int iFlags, int argc, const char **argv)
518{
519 pam_vbox_log(hPAM, "pam_vbox_acct_mgmt called\n");
520 return PAM_SUCCESS;
521}
522
523
524DECLEXPORT(int) pam_sm_open_session(pam_handle_t *hPAM, int iFlags, int argc, const char **argv)
525{
526 pam_vbox_log(hPAM, "pam_vbox_open_session called\n");
527 RTPrintf("This session was provided by VirtualBox Guest Additions. Have a lot of fun!\n");
528 return PAM_SUCCESS;
529}
530
531
532DECLEXPORT(int) pam_sm_close_session(pam_handle_t *hPAM, int iFlags, int argc, const char **argv)
533{
534 pam_vbox_log(hPAM, "pam_vbox_close_session called\n");
535 return PAM_SUCCESS;
536}
537
538DECLEXPORT(int) pam_sm_chauthtok(pam_handle_t *hPAM, int iFlags, int argc, const char **argv)
539{
540 pam_vbox_log(hPAM, "pam_vbox_sm_chauthtok called\n");
541 return PAM_SUCCESS;
542}
543
544#ifdef DEBUG
545DECLEXPORT(void) RTAssertMsg1Weak(const char *pszExpr, unsigned uLine, const char *pszFile, const char *pszFunction)
546{
547 pam_vbox_log(g_pam_handle,
548 "\n!!Assertion Failed!!\n"
549 "Expression: %s\n"
550 "Location : %s(%d) %s\n",
551 pszExpr, pszFile, uLine, pszFunction);
552 RTAssertMsg1(pszExpr, uLine, pszFile, pszFunction);
553}
554#endif
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