OpenVPN
auth-pam.c
Go to the documentation of this file.
1/*
2 * OpenVPN -- An application to securely tunnel IP networks
3 * over a single TCP/UDP port, with support for SSL/TLS-based
4 * session authentication and key exchange,
5 * packet encryption, packet authentication, and
6 * packet compression.
7 *
8 * Copyright (C) 2002-2025 OpenVPN Inc <sales@openvpn.net>
9 * Copyright (C) 2016-2025 Selva Nair <selva.nair@gmail.com>
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2
13 * as published by the Free Software Foundation.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, see <https://www.gnu.org/licenses/>.
22 */
23
24/*
25 * OpenVPN plugin module to do PAM authentication using a split
26 * privilege model.
27 */
28#ifdef HAVE_CONFIG_H
29#include <config.h>
30#endif
31
32#include <security/pam_appl.h>
33
34#ifdef USE_PAM_DLOPEN
35#include "pamdl.h"
36#endif
37
38#include <stdio.h>
39#include <string.h>
40#include <ctype.h>
41#include <unistd.h>
42#include <stdlib.h>
43#include <sys/types.h>
44#include <sys/socket.h>
45#include <sys/wait.h>
46#include <fcntl.h>
47#include <signal.h>
48#include <syslog.h>
49#include <limits.h>
50#include "utils.h"
51#include <arpa/inet.h>
52#include <openvpn-plugin.h>
53
54#define DEBUG(verb) ((verb) >= 4)
55
56/* Command codes for foreground -> background communication */
57#define COMMAND_VERIFY 0
58#define COMMAND_EXIT 1
59
60/* Response codes for background -> foreground communication */
61#define RESPONSE_INIT_SUCCEEDED 10
62#define RESPONSE_INIT_FAILED 11
63#define RESPONSE_VERIFY_SUCCEEDED 12
64#define RESPONSE_VERIFY_FAILED 13
65#define RESPONSE_DEFER 14
66
67/* Pointers to functions exported from openvpn */
68static plugin_log_t plugin_log = NULL;
69static plugin_secure_memzero_t plugin_secure_memzero = NULL;
70static plugin_base64_decode_t plugin_base64_decode = NULL;
71
72/* module name for plugin_log() */
73static char *MODULE = "AUTH-PAM";
74
75/*
76 * Plugin state, used by foreground
77 */
79{
80 /* Foreground's socket to background process */
82
83 /* Process ID of background process */
85
86 /* Verbosity level of OpenVPN */
87 int verb;
88};
89
90/*
91 * Name/Value pairs for conversation function.
92 * Special Values:
93 *
94 * "USERNAME" -- substitute client-supplied username
95 * "PASSWORD" -- substitute client-specified password
96 * "COMMONNAME" -- substitute client certificate common name
97 * "OTP" -- substitute static challenge response if available
98 */
99
100#define N_NAME_VALUE 16
101
103{
104 const char *name;
105 const char *value;
106};
107
113
114/*
115 * Used to pass the username/password
116 * to the PAM conversation function.
117 */
118struct user_pass
119{
120 int verb;
121
122 char username[128];
123 char password[128];
124 char common_name[128];
125 char response[128];
126 char remote[INET6_ADDRSTRLEN];
127
129};
130
131/* Background process function */
132static void pam_server(int fd, const char *service, int verb,
133 const struct name_value_list *name_value_list);
134
135
136/*
137 * Socket read/write functions.
138 */
139
140static int
142{
143 unsigned char c;
144 const ssize_t size = read(fd, &c, sizeof(c));
145 if (size == sizeof(c))
146 {
147 return c;
148 }
149 else
150 {
151 /*fprintf (stderr, "AUTH-PAM: DEBUG recv_control.read=%d\n", (int)size);*/
152 return -1;
153 }
154}
155
156static int
157send_control(int fd, int code)
158{
159 unsigned char c = (unsigned char)code;
160 const ssize_t size = write(fd, &c, sizeof(c));
161 if (size == sizeof(c))
162 {
163 return (int)size;
164 }
165 else
166 {
167 return -1;
168 }
169}
170
171static ssize_t
172recv_string(int fd, char *buffer, size_t len)
173{
174 if (len > 0)
175 {
176 memset(buffer, 0, len);
177 ssize_t size = read(fd, buffer, len);
178 buffer[len - 1] = 0;
179 if (size >= 1)
180 {
181 return size;
182 }
183 }
184 return -1;
185}
186
187static ssize_t
188send_string(int fd, const char *string)
189{
190 const size_t len = strlen(string) + 1;
191 const ssize_t size = write(fd, string, len);
192 if (size == len)
193 {
194 return size;
195 }
196 else
197 {
198 return -1;
199 }
200}
201
202#ifdef DO_DAEMONIZE
203
204/*
205 * Daemonize if "daemon" env var is true.
206 * Preserve stderr across daemonization if
207 * "daemon_log_redirect" env var is true.
208 */
209static void
210daemonize(const char *envp[])
211{
212 const char *daemon_string = get_env("daemon", envp);
213 if (daemon_string && daemon_string[0] == '1')
214 {
215 const char *log_redirect = get_env("daemon_log_redirect", envp);
216 int fd = -1;
217 if (log_redirect && log_redirect[0] == '1')
218 {
219 fd = dup(2);
220 }
221#if defined(__APPLE__) && defined(__clang__)
222#pragma clang diagnostic push
223#pragma clang diagnostic ignored "-Wdeprecated-declarations"
224#endif
225 if (daemon(0, 0) < 0)
226 {
227 plugin_log(PLOG_ERR | PLOG_ERRNO, MODULE, "daemonization failed");
228 }
229#if defined(__APPLE__) && defined(__clang__)
230#pragma clang diagnostic pop
231#endif
232 else if (fd >= 3)
233 {
234 dup2(fd, 2);
235 close(fd);
236 }
237 }
238}
239
240#endif /* ifdef DO_DAEMONIZE */
241
242/*
243 * Close most of parent's fds.
244 * Keep stdin/stdout/stderr, plus one
245 * other fd which is presumed to be
246 * our pipe back to parent.
247 * Admittedly, a bit of a kludge,
248 * but posix doesn't give us a kind
249 * of FD_CLOEXEC which will stop
250 * fds from crossing a fork().
251 */
252static void
254{
255 int i;
256 closelog();
257 for (i = 3; i <= 100; ++i)
258 {
259 if (i != keep)
260 {
261 close(i);
262 }
263 }
264}
265
266/*
267 * Usually we ignore signals, because our parent will
268 * deal with them.
269 */
270static void
281
282/*
283 * Return 1 if query matches match.
284 */
285static int
286name_value_match(const char *query, const char *match)
287{
288 while (!isalnum(*query))
289 {
290 if (*query == '\0')
291 {
292 return 0;
293 }
294 ++query;
295 }
296 return strncasecmp(match, query, strlen(match)) == 0;
297}
298
299/*
300 * Split and decode up->password in the form SCRV1:base64_pass:base64_response
301 * into pass and response and save in up->password and up->response.
302 * If the password is not in the expected format, input is not changed.
303 */
304static void
306{
307 const int skip = strlen("SCRV1:");
308 if (strncmp(up->password, "SCRV1:", skip) != 0)
309 {
310 return;
311 }
312
313 char *tmp = strdup(up->password);
314 if (!tmp)
315 {
316 plugin_log(PLOG_ERR, MODULE, "out of memory parsing static challenge password");
317 goto out;
318 }
319
320 char *pass = tmp + skip;
321 char *resp = strchr(pass, ':');
322 if (!resp) /* string not in SCRV1:xx:yy format */
323 {
324 goto out;
325 }
326 *resp++ = '\0';
327
328 int n = plugin_base64_decode(pass, up->password, sizeof(up->password) - 1);
329 if (n >= 0)
330 {
331 up->password[n] = '\0';
332 n = plugin_base64_decode(resp, up->response, sizeof(up->response) - 1);
333 if (n >= 0)
334 {
335 up->response[n] = '\0';
336 if (DEBUG(up->verb))
337 {
338 plugin_log(PLOG_NOTE, MODULE, "BACKGROUND: parsed static challenge password");
339 }
340 goto out;
341 }
342 }
343
344 /* decode error: reinstate original value of up->password and return */
345 plugin_secure_memzero(up->password, sizeof(up->password));
346 plugin_secure_memzero(up->response, sizeof(up->response));
347 strcpy(up->password, tmp); /* tmp is guaranteed to fit in up->password */
348
349 plugin_log(PLOG_ERR, MODULE, "base64 decode error while parsing static challenge password");
350
351out:
352 if (tmp)
353 {
355 free(tmp);
356 }
357}
358
362{
363 pid_t pid;
364 int fd[2];
365
368
369 const int base_parms = 2;
370
371 const char **argv = args->argv;
372 const char **envp = args->envp;
373
374 /* Check API compatibility -- struct version 5 or higher needed */
375 if (v3structver < 5)
376 {
378 "AUTH-PAM: This plugin is incompatible with the running version of OpenVPN\n");
380 }
381
382 /*
383 * Allocate our context
384 */
385 context = (struct auth_pam_context *)calloc(1, sizeof(struct auth_pam_context));
386 if (!context)
387 {
388 goto error;
389 }
390 context->foreground_fd = -1;
391
392 /*
393 * Intercept the --auth-user-pass-verify callback.
394 */
395 ret->type_mask = OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY);
396
397 /* Save global pointers to functions exported from openvpn */
398 plugin_log = args->callbacks->plugin_log;
399 plugin_secure_memzero = args->callbacks->plugin_secure_memzero;
400 plugin_base64_decode = args->callbacks->plugin_base64_decode;
401
402 /*
403 * Make sure we have two string arguments: the first is the .so name,
404 * the second is the PAM service type.
405 */
406 if (string_array_len(argv) < base_parms)
407 {
408 plugin_log(PLOG_ERR, MODULE, "need PAM service parameter");
409 goto error;
410 }
411
412 /*
413 * See if we have optional name/value pairs to match against
414 * PAM module queried fields in the conversation function.
415 */
417 if (string_array_len(argv) > base_parms)
418 {
419 const int nv_len = string_array_len(argv) - base_parms;
420 int i;
421
422 if ((nv_len & 1) == 1 || (nv_len / 2) > N_NAME_VALUE)
423 {
424 plugin_log(PLOG_ERR, MODULE, "bad name/value list length");
425 goto error;
426 }
427
428 name_value_list.len = nv_len / 2;
429 for (i = 0; i < name_value_list.len; ++i)
430 {
431 const int base = base_parms + i * 2;
432 name_value_list.data[i].name = argv[base];
433 name_value_list.data[i].value = argv[base + 1];
434 }
435 }
436
437 /*
438 * Get verbosity level from environment
439 */
440 {
441 const char *verb_string = get_env("verb", envp);
442 if (verb_string)
443 {
444 context->verb = atoi(verb_string);
445 }
446 }
447
448 /*
449 * Make a socket for foreground and background processes
450 * to communicate.
451 */
452 if (socketpair(PF_UNIX, SOCK_DGRAM, 0, fd) == -1)
453 {
454 plugin_log(PLOG_ERR | PLOG_ERRNO, MODULE, "socketpair call failed");
455 goto error;
456 }
457
458 /*
459 * Fork off the privileged process. It will remain privileged
460 * even after the foreground process drops its privileges.
461 */
462 pid = fork();
463
464 if (pid)
465 {
466 int status;
467
468 /*
469 * Foreground Process
470 */
471
472 context->background_pid = pid;
473
474 /* close our copy of child's socket */
475 close(fd[1]);
476
477 /* don't let future subprocesses inherit child socket */
478 if (fcntl(fd[0], F_SETFD, FD_CLOEXEC) < 0)
479 {
480 plugin_log(PLOG_ERR | PLOG_ERRNO, MODULE,
481 "Set FD_CLOEXEC flag on socket file descriptor failed");
482 }
483
484 /* wait for background child process to initialize */
485 status = recv_control(fd[0]);
487 {
488 context->foreground_fd = fd[0];
489 ret->handle = (openvpn_plugin_handle_t *)context;
490 plugin_log(PLOG_NOTE, MODULE, "initialization succeeded (fg)");
491 return OPENVPN_PLUGIN_FUNC_SUCCESS;
492 }
493 }
494 else
495 {
496 /*
497 * Background Process
498 */
499
500 /* close all parent fds except our socket back to parent */
501 close_fds_except(fd[1]);
502
503 /* Ignore most signals (the parent will receive them) */
504 set_signals();
505
506#ifdef DO_DAEMONIZE
507 /* Daemonize if --daemon option is set. */
508 daemonize(envp);
509#endif
510
511 /* execute the event loop */
512 pam_server(fd[1], argv[1], context->verb, &name_value_list);
513
514 close(fd[1]);
515
516 exit(0);
517 return 0; /* NOTREACHED */
518 }
519
520error:
521 free(context);
522 return OPENVPN_PLUGIN_FUNC_ERROR;
523}
524
525OPENVPN_EXPORT int
526openvpn_plugin_func_v1(openvpn_plugin_handle_t handle, const int type, const char *argv[],
527 const char *envp[])
528{
529 struct auth_pam_context *context = (struct auth_pam_context *)handle;
530
531 if (type == OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY && context->foreground_fd >= 0)
532 {
533 /* get username/password from envp string array */
534 const char *username = get_env("username", envp);
535 const char *password = get_env("password", envp);
536 const char *common_name = get_env("common_name", envp) ? get_env("common_name", envp) : "";
537 const char *remote = get_env("untrusted_ip6", envp);
538
539 if (remote == NULL)
540 {
541 remote = get_env("untrusted_ip", envp);
542 }
543
544 if (remote == NULL)
545 {
546 remote = "";
547 }
548
549 /* should we do deferred auth?
550 * yes, if there is "auth_control_file" and "deferred_auth_pam" env
551 */
552 const char *auth_control_file = get_env("auth_control_file", envp);
553 const char *deferred_auth_pam = get_env("deferred_auth_pam", envp);
554 if (auth_control_file != NULL && deferred_auth_pam != NULL)
555 {
556 if (DEBUG(context->verb))
557 {
558 plugin_log(PLOG_NOTE, MODULE, "do deferred auth '%s'", auth_control_file);
559 }
560 }
561 else
562 {
563 auth_control_file = "";
564 }
565
566 if (username && strlen(username) > 0 && password)
567 {
568 if (send_control(context->foreground_fd, COMMAND_VERIFY) == -1
569 || send_string(context->foreground_fd, username) == -1
570 || send_string(context->foreground_fd, password) == -1
571 || send_string(context->foreground_fd, common_name) == -1
572 || send_string(context->foreground_fd, auth_control_file) == -1
573 || send_string(context->foreground_fd, remote) == -1)
574 {
575 plugin_log(PLOG_ERR | PLOG_ERRNO, MODULE,
576 "Error sending auth info to background process");
577 }
578 else
579 {
580 const int status = recv_control(context->foreground_fd);
582 {
583 return OPENVPN_PLUGIN_FUNC_SUCCESS;
584 }
585 if (status == RESPONSE_DEFER)
586 {
587 if (DEBUG(context->verb))
588 {
589 plugin_log(PLOG_NOTE, MODULE, "deferred authentication");
590 }
591 return OPENVPN_PLUGIN_FUNC_DEFERRED;
592 }
593 if (status == -1)
594 {
595 plugin_log(PLOG_ERR | PLOG_ERRNO, MODULE,
596 "Error receiving auth confirmation from background process");
597 }
598 }
599 }
600 }
601 return OPENVPN_PLUGIN_FUNC_ERROR;
602}
603
604OPENVPN_EXPORT void
605openvpn_plugin_close_v1(openvpn_plugin_handle_t handle)
606{
607 struct auth_pam_context *context = (struct auth_pam_context *)handle;
608
609 if (DEBUG(context->verb))
610 {
611 plugin_log(PLOG_NOTE, MODULE, "close");
612 }
613
614 if (context->foreground_fd >= 0)
615 {
616 /* tell background process to exit */
617 if (send_control(context->foreground_fd, COMMAND_EXIT) == -1)
618 {
619 plugin_log(PLOG_ERR | PLOG_ERRNO, MODULE, "Error signaling background process to exit");
620 }
621
622 /* wait for background process to exit */
623 if (context->background_pid > 0)
624 {
625 waitpid(context->background_pid, NULL, 0);
626 }
627
628 close(context->foreground_fd);
629 context->foreground_fd = -1;
630 }
631
632 free(context);
633}
634
635OPENVPN_EXPORT void
636openvpn_plugin_abort_v1(openvpn_plugin_handle_t handle)
637{
638 struct auth_pam_context *context = (struct auth_pam_context *)handle;
639
640 /* tell background process to exit */
641 if (context && context->foreground_fd >= 0)
642 {
643 send_control(context->foreground_fd, COMMAND_EXIT);
644 close(context->foreground_fd);
645 context->foreground_fd = -1;
646 }
647}
648
649/*
650 * PAM conversation function
651 */
652static int
653my_conv(int num_msg, const struct pam_message **msg_array, struct pam_response **response_array,
654 void *appdata_ptr)
655{
656 const struct user_pass *up = (const struct user_pass *)appdata_ptr;
657 struct pam_response *aresp;
658 int ret = PAM_SUCCESS;
659
660 *response_array = NULL;
661
662 if (num_msg <= 0 || num_msg > PAM_MAX_NUM_MSG)
663 {
664 return (PAM_CONV_ERR);
665 }
666 if ((aresp = calloc((size_t)num_msg, sizeof *aresp)) == NULL)
667 {
668 return (PAM_BUF_ERR);
669 }
670
671 /* loop through each PAM-module query */
672 for (int i = 0; i < num_msg; ++i)
673 {
674 const struct pam_message *msg = msg_array[i];
675 aresp[i].resp_retcode = 0;
676 aresp[i].resp = NULL;
677
678 if (DEBUG(up->verb))
679 {
680 plugin_log(PLOG_NOTE, MODULE, "BACKGROUND: my_conv[%d] query='%s' style=%d", i,
681 msg->msg ? msg->msg : "NULL", msg->msg_style);
682 }
683
684 if (up->name_value_list && up->name_value_list->len > 0)
685 {
686 /* use name/value list match method */
687 const struct name_value_list *list = up->name_value_list;
688
689 /* loop through name/value pairs */
690 int j; /* checked after loop */
691 for (j = 0; j < list->len; ++j)
692 {
693 const char *match_name = list->data[j].name;
694 const char *match_value = list->data[j].value;
695
697 {
698 /* found name/value match */
699 aresp[i].resp = NULL;
700
701 if (DEBUG(up->verb))
702 {
705 "BACKGROUND: name match found, query/match-string ['%s', '%s'] = '%s'",
706 msg->msg, match_name, match_value);
707 }
708
709 if (strstr(match_value, "USERNAME"))
710 {
711 aresp[i].resp = searchandreplace(match_value, "USERNAME", up->username);
712 }
713 else if (strstr(match_value, "PASSWORD"))
714 {
715 aresp[i].resp = searchandreplace(match_value, "PASSWORD", up->password);
716 }
717 else if (strstr(match_value, "COMMONNAME"))
718 {
719 aresp[i].resp =
720 searchandreplace(match_value, "COMMONNAME", up->common_name);
721 }
722 else if (strstr(match_value, "OTP"))
723 {
724 aresp[i].resp = searchandreplace(match_value, "OTP", up->response);
725 }
726 else
727 {
728 aresp[i].resp = strdup(match_value);
729 }
730
731 if (aresp[i].resp == NULL)
732 {
733 ret = PAM_CONV_ERR;
734 }
735 break;
736 }
737 }
738
739 if (j == list->len)
740 {
741 ret = PAM_CONV_ERR;
742 }
743 }
744 else
745 {
746 /* use PAM_PROMPT_ECHO_x hints */
747 switch (msg->msg_style)
748 {
750 aresp[i].resp = strdup(up->password);
751 if (aresp[i].resp == NULL)
752 {
753 ret = PAM_CONV_ERR;
754 }
755 break;
756
758 aresp[i].resp = strdup(up->username);
759 if (aresp[i].resp == NULL)
760 {
761 ret = PAM_CONV_ERR;
762 }
763 break;
764
765 case PAM_ERROR_MSG:
766 case PAM_TEXT_INFO:
767 break;
768
769 default:
770 ret = PAM_CONV_ERR;
771 break;
772 }
773 }
774 }
775
776 if (ret == PAM_SUCCESS)
777 {
779 }
780 else
781 {
782 free(aresp);
783 }
784
785 return ret;
786}
787
788/*
789 * Return 1 if authenticated and 0 if failed.
790 * Called once for every username/password
791 * to be authenticated.
792 */
793static int
794pam_auth(const char *service, const struct user_pass *up)
795{
796 struct pam_conv conv;
797 pam_handle_t *pamh = NULL;
798 int status = PAM_SUCCESS;
799 int ret = 0;
800 const int name_value_list_provided = (up->name_value_list && up->name_value_list->len > 0);
801
802 /* Initialize PAM */
803 conv.conv = my_conv;
804 conv.appdata_ptr = (void *)up;
805 status = pam_start(service, name_value_list_provided ? NULL : up->username, &conv, &pamh);
806 if (status == PAM_SUCCESS)
807 {
808 /* Set PAM_RHOST environment variable */
809 if (*(up->remote))
810 {
811 status = pam_set_item(pamh, PAM_RHOST, up->remote);
812 }
813 /* Call PAM to verify username/password */
814 if (status == PAM_SUCCESS)
815 {
816 status = pam_authenticate(pamh, 0);
817 }
818 if (status == PAM_SUCCESS)
819 {
820 status = pam_acct_mgmt(pamh, 0);
821 }
822 if (status == PAM_SUCCESS)
823 {
824 ret = 1;
825 }
826
827 /* Output error message if failed */
828 if (!ret)
829 {
830 plugin_log(PLOG_ERR, MODULE, "BACKGROUND: user '%s' failed to authenticate: %s",
831 up->username, pam_strerror(pamh, status));
832 }
833
834 /* Close PAM */
835 pam_end(pamh, status);
836 }
837
838 return ret;
839}
840
841/*
842 * deferred auth handler
843 * - fork() (twice, to avoid the need for async wait / SIGCHLD handling)
844 * - query PAM stack via pam_auth()
845 * - send response back to OpenVPN via "ac_file_name"
846 *
847 * parent process returns "0" for "fork() and wait() succeeded",
848 * "-1" for "something went wrong, abort program"
849 */
850
851static void
852do_deferred_pam_auth(int fd, const char *ac_file_name, const char *service,
853 const struct user_pass *up)
854{
855 if (send_control(fd, RESPONSE_DEFER) == -1)
856 {
857 plugin_log(PLOG_ERR | PLOG_ERRNO, MODULE, "BACKGROUND: write error on response socket [4]");
858 return;
859 }
860
861 /* double forking so we do not need to wait() for async auth kids */
862 pid_t p1 = fork();
863
864 if (p1 < 0)
865 {
866 plugin_log(PLOG_ERR | PLOG_ERRNO, MODULE, "BACKGROUND: fork(1) failed");
867 return;
868 }
869 if (p1 != 0) /* parent */
870 {
871 waitpid(p1, NULL, 0);
872 return; /* parent's job succeeded */
873 }
874
875 /* child */
876 close(fd); /* socketpair no longer needed */
877
878 pid_t p2 = fork();
879 if (p2 < 0)
880 {
881 plugin_log(PLOG_ERR | PLOG_ERRNO, MODULE, "BACKGROUND: fork(2) failed");
882 exit(1);
883 }
884
885 if (p2 != 0) /* new parent: exit right away */
886 {
887 exit(0);
888 }
889
890 /* grandchild */
891 plugin_log(PLOG_NOTE, MODULE, "BACKGROUND: deferred auth for '%s', pid=%d", up->username,
892 (int)getpid());
893
894 /* the rest is very simple: do PAM, write status byte to file, done */
895 int ac_fd = open(ac_file_name, O_WRONLY);
896 if (ac_fd < 0)
897 {
898 plugin_log(PLOG_ERR | PLOG_ERRNO, MODULE, "cannot open '%s' for writing", ac_file_name);
899 exit(1);
900 }
901 int pam_success = pam_auth(service, up);
902
903 if (write(ac_fd, pam_success ? "1" : "0", 1) != 1)
904 {
905 plugin_log(PLOG_ERR | PLOG_ERRNO, MODULE, "cannot write to '%s'", ac_file_name);
906 }
907 close(ac_fd);
908 plugin_log(PLOG_NOTE, MODULE, "BACKGROUND: %s: deferred auth: PAM %s", up->username,
909 pam_success ? "succeeded" : "rejected");
910 exit(0);
911}
912
913/*
914 * Background process -- runs with privilege.
915 */
916static void
917pam_server(int fd, const char *service, int verb, const struct name_value_list *name_value_list)
918{
919 struct user_pass up;
920 char ac_file_name[PATH_MAX];
921 int command;
922#ifdef USE_PAM_DLOPEN
923 static const char pam_so[] = "libpam.so";
924#endif
925
926 /*
927 * Do initialization
928 */
929 if (DEBUG(verb))
930 {
931 plugin_log(PLOG_NOTE, MODULE, "BACKGROUND: INIT service='%s'", service);
932 }
933
934#ifdef USE_PAM_DLOPEN
935 /*
936 * Load PAM shared object
937 */
938 if (!dlopen_pam(pam_so))
939 {
940 plugin_log(PLOG_ERR, MODULE, "BACKGROUND: could not load PAM lib %s: %s", pam_so,
941 dlerror());
943 goto done;
944 }
945#endif
946
947 /*
948 * Tell foreground that we initialized successfully
949 */
951 {
952 plugin_log(PLOG_ERR | PLOG_ERRNO, MODULE, "BACKGROUND: write error on response socket [1]");
953 goto done;
954 }
955
956 plugin_log(PLOG_NOTE, MODULE, "BACKGROUND: initialization succeeded");
957
958 /*
959 * Event loop
960 */
961 while (1)
962 {
963 memset(&up, 0, sizeof(up));
964 up.verb = verb;
966
967 /* get a command from foreground process */
968 command = recv_control(fd);
969
970 if (DEBUG(verb))
971 {
972 plugin_log(PLOG_NOTE, MODULE, "BACKGROUND: received command code: %d", command);
973 }
974
975 switch (command)
976 {
977 case COMMAND_VERIFY:
978 if (recv_string(fd, up.username, sizeof(up.username)) == -1
979 || recv_string(fd, up.password, sizeof(up.password)) == -1
980 || recv_string(fd, up.common_name, sizeof(up.common_name)) == -1
981 || recv_string(fd, ac_file_name, sizeof(ac_file_name)) == -1
982 || recv_string(fd, up.remote, sizeof(up.remote)) == -1)
983 {
984 plugin_log(PLOG_ERR | PLOG_ERRNO, MODULE,
985 "BACKGROUND: read error on command channel: code=%d, exiting",
986 command);
987 goto done;
988 }
989
990 if (DEBUG(verb))
991 {
992#if 0
993 plugin_log(PLOG_NOTE, MODULE, "BACKGROUND: USER/PASS: %s/%s",
994 up.username, up.password);
995#else
996 plugin_log(PLOG_NOTE, MODULE, "BACKGROUND: USER: %s", up.username);
997 plugin_log(PLOG_NOTE, MODULE, "BACKGROUND: REMOTE: %s", up.remote);
998#endif
999 }
1000
1001 /* If password is of the form SCRV1:base64:base64 split it up */
1003
1004 /* client wants deferred auth
1005 */
1006 if (strlen(ac_file_name) > 0)
1007 {
1008 do_deferred_pam_auth(fd, ac_file_name, service, &up);
1009 break;
1010 }
1011
1012
1013 /* non-deferred auth: wait for pam result and send
1014 * result back via control socketpair
1015 */
1016 if (pam_auth(service, &up)) /* Succeeded */
1017 {
1019 {
1020 plugin_log(PLOG_ERR | PLOG_ERRNO, MODULE,
1021 "BACKGROUND: write error on response socket [2]");
1022 goto done;
1023 }
1024 }
1025 else /* Failed */
1026 {
1027 if (send_control(fd, RESPONSE_VERIFY_FAILED) == -1)
1028 {
1029 plugin_log(PLOG_ERR | PLOG_ERRNO, MODULE,
1030 "BACKGROUND: write error on response socket [3]");
1031 goto done;
1032 }
1033 }
1034 plugin_secure_memzero(up.password, sizeof(up.password));
1035 break;
1036
1037 case COMMAND_EXIT:
1038 goto done;
1039
1040 case -1:
1041 plugin_log(PLOG_ERR | PLOG_ERRNO, MODULE,
1042 "BACKGROUND: read error on command channel");
1043 goto done;
1044
1045 default:
1046 plugin_log(PLOG_ERR, MODULE, "BACKGROUND: unknown command code: code=%d, exiting",
1047 command);
1048 goto done;
1049 }
1050 plugin_secure_memzero(up.response, sizeof(up.response));
1051 }
1052done:
1053 plugin_secure_memzero(up.password, sizeof(up.password));
1054 plugin_secure_memzero(up.response, sizeof(up.response));
1055#ifdef USE_PAM_DLOPEN
1056 dlclose_pam();
1057#endif
1058 if (DEBUG(verb))
1059 {
1060 plugin_log(PLOG_NOTE, MODULE, "BACKGROUND: EXIT");
1061 }
1062
1063 return;
1064}
OPENVPN_EXPORT int openvpn_plugin_func_v1(openvpn_plugin_handle_t handle, const int type, const char *argv[], const char *envp[])
Definition auth-pam.c:526
static void set_signals(void)
Definition auth-pam.c:271
#define RESPONSE_DEFER
Definition auth-pam.c:65
OPENVPN_EXPORT void openvpn_plugin_close_v1(openvpn_plugin_handle_t handle)
Definition auth-pam.c:605
static plugin_log_t plugin_log
Definition auth-pam.c:68
static int pam_auth(const char *service, const struct user_pass *up)
Definition auth-pam.c:794
#define N_NAME_VALUE
Definition auth-pam.c:100
static int name_value_match(const char *query, const char *match)
Definition auth-pam.c:286
static char * MODULE
Definition auth-pam.c:73
#define COMMAND_VERIFY
Definition auth-pam.c:57
#define RESPONSE_INIT_SUCCEEDED
Definition auth-pam.c:61
static plugin_secure_memzero_t plugin_secure_memzero
Definition auth-pam.c:69
static void do_deferred_pam_auth(int fd, const char *ac_file_name, const char *service, const struct user_pass *up)
Definition auth-pam.c:852
#define RESPONSE_INIT_FAILED
Definition auth-pam.c:62
static ssize_t send_string(int fd, const char *string)
Definition auth-pam.c:188
static plugin_base64_decode_t plugin_base64_decode
Definition auth-pam.c:70
static void pam_server(int fd, const char *service, int verb, const struct name_value_list *name_value_list)
Definition auth-pam.c:917
#define RESPONSE_VERIFY_FAILED
Definition auth-pam.c:64
OPENVPN_EXPORT void openvpn_plugin_abort_v1(openvpn_plugin_handle_t handle)
Definition auth-pam.c:636
static int recv_control(int fd)
Definition auth-pam.c:141
#define COMMAND_EXIT
Definition auth-pam.c:58
static int send_control(int fd, int code)
Definition auth-pam.c:157
static void close_fds_except(int keep)
Definition auth-pam.c:253
OPENVPN_EXPORT int openvpn_plugin_open_v3(const int v3structver, struct openvpn_plugin_args_open_in const *args, struct openvpn_plugin_args_open_return *ret)
Definition auth-pam.c:360
#define RESPONSE_VERIFY_SUCCEEDED
Definition auth-pam.c:63
#define DEBUG(verb)
Definition auth-pam.c:54
static int my_conv(int num_msg, const struct pam_message **msg_array, struct pam_response **response_array, void *appdata_ptr)
Definition auth-pam.c:653
static ssize_t recv_string(int fd, char *buffer, size_t len)
Definition auth-pam.c:172
static void split_scrv1_password(struct user_pass *up)
Definition auth-pam.c:305
int string_array_len(const char **array)
Definition buffer.c:703
int daemon(int nochdir, int noclose)
static void daemonize(const char *envp[])
Definition down-root.c:163
static SERVICE_STATUS status
Definition interactive.c:51
@ write
@ read
static SERVICE_STATUS_HANDLE service
Definition interactive.c:50
#define msg(flags,...)
Definition error.h:150
static const char * get_env(const char *name, const char *envp[])
Definition argv.h:35
char ** argv
Definition argv.h:39
pid_t background_pid
Definition auth-pam.c:84
Wrapper structure for dynamically allocated memory.
Definition buffer.h:60
Contains all state information for one tunnel.
Definition openvpn.h:474
struct name_value data[N_NAME_VALUE]
Definition auth-pam.c:111
const char * name
Definition auth-pam.c:104
const char * value
Definition auth-pam.c:105
char common_name[128]
Definition auth-pam.c:124
char response[128]
Definition auth-pam.c:125
const struct name_value_list * name_value_list
Definition auth-pam.c:128
char password[USER_PASS_LEN]
Definition misc.h:68
char remote[INET6_ADDRSTRLEN]
Definition auth-pam.c:126
char username[USER_PASS_LEN]
Definition misc.h:67
char * searchandreplace(const char *tosearch, const char *searchfor, const char *replacewith)
Read 'tosearch', replace all occurrences of 'searchfor' with 'replacewith' and return a pointer to th...
Definition utils.c:42