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-2026 OpenVPN Inc <sales@openvpn.net>
9 * Copyright (C) 2016-2026 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 */
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
187#if defined(__GNUC__) || defined(__clang__)
188#pragma GCC diagnostic push
189#pragma GCC diagnostic ignored "-Wsign-compare"
190#endif
191
192static ssize_t
193send_string(int fd, const char *string)
194{
195 const size_t len = strlen(string) + 1;
196 const ssize_t size = write(fd, string, len);
197 if (size == len)
198 {
199 return size;
200 }
201 else
202 {
203 return -1;
204 }
205}
206
207#if defined(__GNUC__) || defined(__clang__)
208#pragma GCC diagnostic pop
209#endif
210
211#ifdef DO_DAEMONIZE
212
213/*
214 * Daemonize if "daemon" env var is true.
215 * Preserve stderr across daemonization if
216 * "daemon_log_redirect" env var is true.
217 */
218static void
219daemonize(const char *envp[])
220{
221 const char *daemon_string = get_env("daemon", envp);
222 if (daemon_string && daemon_string[0] == '1')
223 {
224 const char *log_redirect = get_env("daemon_log_redirect", envp);
225 int fd = -1;
226 if (log_redirect && log_redirect[0] == '1')
227 {
228 fd = dup(2);
229 }
230#if defined(__APPLE__) && defined(__clang__)
231#pragma clang diagnostic push
232#pragma clang diagnostic ignored "-Wdeprecated-declarations"
233#endif
234 if (daemon(0, 0) < 0)
235 {
236 plugin_log(PLOG_ERR | PLOG_ERRNO, MODULE, "daemonization failed");
237 }
238#if defined(__APPLE__) && defined(__clang__)
239#pragma clang diagnostic pop
240#endif
241 else if (fd >= 3)
242 {
243 dup2(fd, 2);
244 close(fd);
245 }
246 }
247}
248
249#endif /* ifdef DO_DAEMONIZE */
250
251/*
252 * Close most of parent's fds.
253 * Keep stdin/stdout/stderr, plus one
254 * other fd which is presumed to be
255 * our pipe back to parent.
256 * Admittedly, a bit of a kludge,
257 * but posix doesn't give us a kind
258 * of FD_CLOEXEC which will stop
259 * fds from crossing a fork().
260 */
261static void
263{
264 int i;
265 closelog();
266 for (i = 3; i <= 100; ++i)
267 {
268 if (i != keep)
269 {
270 close(i);
271 }
272 }
273}
274
275/*
276 * Usually we ignore signals, because our parent will
277 * deal with them.
278 */
279static void
290
291/*
292 * Return 1 if query matches match.
293 */
294static int
295name_value_match(const char *query, const char *match)
296{
297 while (!isalnum(*query))
298 {
299 if (*query == '\0')
300 {
301 return 0;
302 }
303 ++query;
304 }
305 return strncasecmp(match, query, strlen(match)) == 0;
306}
307
308/*
309 * Split and decode up->password in the form SCRV1:base64_pass:base64_response
310 * into pass and response and save in up->password and up->response.
311 * If the password is not in the expected format, input is not changed.
312 */
313static void
315{
316 const int skip = strlen("SCRV1:");
317 if (strncmp(up->password, "SCRV1:", skip) != 0)
318 {
319 return;
320 }
321
322 char *tmp = strdup(up->password);
323 if (!tmp)
324 {
325 plugin_log(PLOG_ERR, MODULE, "out of memory parsing static challenge password");
326 goto out;
327 }
328
329 char *pass = tmp + skip;
330 char *resp = strchr(pass, ':');
331 if (!resp) /* string not in SCRV1:xx:yy format */
332 {
333 goto out;
334 }
335 *resp++ = '\0';
336
337 int n = plugin_base64_decode(pass, up->password, sizeof(up->password) - 1);
338 if (n >= 0)
339 {
340 up->password[n] = '\0';
341 n = plugin_base64_decode(resp, up->response, sizeof(up->response) - 1);
342 if (n >= 0)
343 {
344 up->response[n] = '\0';
345 if (DEBUG(up->verb))
346 {
347 plugin_log(PLOG_NOTE, MODULE, "BACKGROUND: parsed static challenge password");
348 }
349 goto out;
350 }
351 }
352
353 /* decode error: reinstate original value of up->password and return */
354 plugin_secure_memzero(up->password, sizeof(up->password));
355 plugin_secure_memzero(up->response, sizeof(up->response));
356 strcpy(up->password, tmp); /* tmp is guaranteed to fit in up->password */
357
358 plugin_log(PLOG_ERR, MODULE, "base64 decode error while parsing static challenge password");
359
360out:
361 if (tmp)
362 {
364 free(tmp);
365 }
366}
367
371{
372 pid_t pid;
373 int fd[2];
374
377
378 const int base_parms = 2;
379
380 const char **argv = args->argv;
381 const char **envp = args->envp;
382
383 /* Check API compatibility -- struct version 5 or higher needed */
384 if (v3structver < 5)
385 {
387 "AUTH-PAM: This plugin is incompatible with the running version of OpenVPN\n");
389 }
390
391 /*
392 * Allocate our context
393 */
394 context = (struct auth_pam_context *)calloc(1, sizeof(struct auth_pam_context));
395 if (!context)
396 {
397 goto error;
398 }
399 context->foreground_fd = -1;
400
401 /*
402 * Intercept the --auth-user-pass-verify callback.
403 */
405
406 /* Save global pointers to functions exported from openvpn */
410
411 /*
412 * Make sure we have two string arguments: the first is the .so name,
413 * the second is the PAM service type.
414 */
415 if (string_array_len(argv) < base_parms)
416 {
417 plugin_log(PLOG_ERR, MODULE, "need PAM service parameter");
418 goto error;
419 }
420
421 /*
422 * See if we have optional name/value pairs to match against
423 * PAM module queried fields in the conversation function.
424 */
426 if (string_array_len(argv) > base_parms)
427 {
428 const int nv_len = string_array_len(argv) - base_parms;
429 int i;
430
431 if ((nv_len & 1) == 1 || (nv_len / 2) > N_NAME_VALUE)
432 {
433 plugin_log(PLOG_ERR, MODULE, "bad name/value list length");
434 goto error;
435 }
436
437 name_value_list.len = nv_len / 2;
438 for (i = 0; i < name_value_list.len; ++i)
439 {
440 const int base = base_parms + i * 2;
441 name_value_list.data[i].name = argv[base];
442 name_value_list.data[i].value = argv[base + 1];
443 }
444 }
445
446 /*
447 * Get verbosity level from environment
448 */
449 {
450 const char *verb_string = get_env("verb", envp);
451 if (verb_string)
452 {
453 context->verb = atoi(verb_string);
454 }
455 }
456
457 /*
458 * Make a socket for foreground and background processes
459 * to communicate.
460 */
461 if (socketpair(PF_UNIX, SOCK_DGRAM, 0, fd) == -1)
462 {
463 plugin_log(PLOG_ERR | PLOG_ERRNO, MODULE, "socketpair call failed");
464 goto error;
465 }
466
467 /*
468 * Fork off the privileged process. It will remain privileged
469 * even after the foreground process drops its privileges.
470 */
471 pid = fork();
472
473 if (pid)
474 {
475 int status;
476
477 /*
478 * Foreground Process
479 */
480
481 context->background_pid = pid;
482
483 /* close our copy of child's socket */
484 close(fd[1]);
485
486 /* don't let future subprocesses inherit child socket */
487 if (fcntl(fd[0], F_SETFD, FD_CLOEXEC) < 0)
488 {
490 "Set FD_CLOEXEC flag on socket file descriptor failed");
491 }
492
493 /* wait for background child process to initialize */
494 status = recv_control(fd[0]);
496 {
497 context->foreground_fd = fd[0];
499 plugin_log(PLOG_NOTE, MODULE, "initialization succeeded (fg)");
501 }
502 }
503 else
504 {
505 /*
506 * Background Process
507 */
508
509 /* close all parent fds except our socket back to parent */
510 close_fds_except(fd[1]);
511
512 /* Ignore most signals (the parent will receive them) */
513 set_signals();
514
515#ifdef DO_DAEMONIZE
516 /* Daemonize if --daemon option is set. */
517 daemonize(envp);
518#endif
519
520 /* execute the event loop */
521 pam_server(fd[1], argv[1], context->verb, &name_value_list);
522
523 close(fd[1]);
524
525 exit(0);
526 return 0; /* NOTREACHED */
527 }
528
529error:
530 free(context);
532}
533
535openvpn_plugin_func_v1(openvpn_plugin_handle_t handle, const int type, const char *argv[],
536 const char *envp[])
537{
538 struct auth_pam_context *context = (struct auth_pam_context *)handle;
539
540 if (type == OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY && context->foreground_fd >= 0)
541 {
542 /* get username/password from envp string array */
543 const char *username = get_env("username", envp);
544 const char *password = get_env("password", envp);
545 const char *common_name = get_env("common_name", envp) ? get_env("common_name", envp) : "";
546 const char *remote = get_env("untrusted_ip6", envp);
547
548 if (remote == NULL)
549 {
550 remote = get_env("untrusted_ip", envp);
551 }
552
553 if (remote == NULL)
554 {
555 remote = "";
556 }
557
558 /* should we do deferred auth?
559 * yes, if there is "auth_control_file" and "deferred_auth_pam" env
560 */
561 const char *auth_control_file = get_env("auth_control_file", envp);
562 const char *deferred_auth_pam = get_env("deferred_auth_pam", envp);
563 if (auth_control_file != NULL && deferred_auth_pam != NULL)
564 {
565 if (DEBUG(context->verb))
566 {
567 plugin_log(PLOG_NOTE, MODULE, "do deferred auth '%s'", auth_control_file);
568 }
569 }
570 else
571 {
572 auth_control_file = "";
573 }
574
575 if (username && strlen(username) > 0 && password)
576 {
577 if (send_control(context->foreground_fd, COMMAND_VERIFY) == -1
578 || send_string(context->foreground_fd, username) == -1
579 || send_string(context->foreground_fd, password) == -1
580 || send_string(context->foreground_fd, common_name) == -1
581 || send_string(context->foreground_fd, auth_control_file) == -1
582 || send_string(context->foreground_fd, remote) == -1)
583 {
585 "Error sending auth info to background process");
586 }
587 else
588 {
589 const int status = recv_control(context->foreground_fd);
591 {
593 }
594 if (status == RESPONSE_DEFER)
595 {
596 if (DEBUG(context->verb))
597 {
598 plugin_log(PLOG_NOTE, MODULE, "deferred authentication");
599 }
601 }
602 if (status == -1)
603 {
605 "Error receiving auth confirmation from background process");
606 }
607 }
608 }
609 }
611}
612
615{
616 struct auth_pam_context *context = (struct auth_pam_context *)handle;
617
618 if (DEBUG(context->verb))
619 {
620 plugin_log(PLOG_NOTE, MODULE, "close");
621 }
622
623 if (context->foreground_fd >= 0)
624 {
625 /* tell background process to exit */
626 if (send_control(context->foreground_fd, COMMAND_EXIT) == -1)
627 {
628 plugin_log(PLOG_ERR | PLOG_ERRNO, MODULE, "Error signaling background process to exit");
629 }
630
631 /* wait for background process to exit */
632 if (context->background_pid > 0)
633 {
634 waitpid(context->background_pid, NULL, 0);
635 }
636
637 close(context->foreground_fd);
638 context->foreground_fd = -1;
639 }
640
641 free(context);
642}
643
646{
647 struct auth_pam_context *context = (struct auth_pam_context *)handle;
648
649 /* tell background process to exit */
650 if (context && context->foreground_fd >= 0)
651 {
652 send_control(context->foreground_fd, COMMAND_EXIT);
653 close(context->foreground_fd);
654 context->foreground_fd = -1;
655 }
656}
657
658/*
659 * PAM conversation function
660 */
661static int
662my_conv(int num_msg, const struct pam_message **msg_array, struct pam_response **response_array,
663 void *appdata_ptr)
664{
665 const struct user_pass *up = (const struct user_pass *)appdata_ptr;
666 struct pam_response *aresp;
667 int ret = PAM_SUCCESS;
668
669 *response_array = NULL;
670
671 if (num_msg <= 0 || num_msg > PAM_MAX_NUM_MSG)
672 {
673 return (PAM_CONV_ERR);
674 }
675 if ((aresp = calloc((size_t)num_msg, sizeof *aresp)) == NULL)
676 {
677 return (PAM_BUF_ERR);
678 }
679
680 /* loop through each PAM-module query */
681 for (int i = 0; i < num_msg; ++i)
682 {
683 const struct pam_message *msg = msg_array[i];
684 aresp[i].resp_retcode = 0;
685 aresp[i].resp = NULL;
686
687 if (DEBUG(up->verb))
688 {
689 plugin_log(PLOG_NOTE, MODULE, "BACKGROUND: my_conv[%d] query='%s' style=%d", i,
690 msg->msg ? msg->msg : "NULL", msg->msg_style);
691 }
692
693 if (up->name_value_list && up->name_value_list->len > 0)
694 {
695 /* use name/value list match method */
696 const struct name_value_list *list = up->name_value_list;
697
698 /* loop through name/value pairs */
699 int j; /* checked after loop */
700 for (j = 0; j < list->len; ++j)
701 {
702 const char *match_name = list->data[j].name;
703 const char *match_value = list->data[j].value;
704
706 {
707 /* found name/value match */
708 aresp[i].resp = NULL;
709
710 if (DEBUG(up->verb))
711 {
714 "BACKGROUND: name match found, query/match-string ['%s', '%s'] = '%s'",
715 msg->msg, match_name, match_value);
716 }
717
718 if (strstr(match_value, "USERNAME"))
719 {
720 aresp[i].resp = searchandreplace(match_value, "USERNAME", up->username);
721 }
722 else if (strstr(match_value, "PASSWORD"))
723 {
724 aresp[i].resp = searchandreplace(match_value, "PASSWORD", up->password);
725 }
726 else if (strstr(match_value, "COMMONNAME"))
727 {
728 aresp[i].resp =
729 searchandreplace(match_value, "COMMONNAME", up->common_name);
730 }
731 else if (strstr(match_value, "OTP"))
732 {
733 aresp[i].resp = searchandreplace(match_value, "OTP", up->response);
734 }
735 else
736 {
737 aresp[i].resp = strdup(match_value);
738 }
739
740 if (aresp[i].resp == NULL)
741 {
742 ret = PAM_CONV_ERR;
743 }
744 break;
745 }
746 }
747
748 if (j == list->len)
749 {
750 ret = PAM_CONV_ERR;
751 }
752 }
753 else
754 {
755 /* use PAM_PROMPT_ECHO_x hints */
756 switch (msg->msg_style)
757 {
759 aresp[i].resp = strdup(up->password);
760 if (aresp[i].resp == NULL)
761 {
762 ret = PAM_CONV_ERR;
763 }
764 break;
765
767 aresp[i].resp = strdup(up->username);
768 if (aresp[i].resp == NULL)
769 {
770 ret = PAM_CONV_ERR;
771 }
772 break;
773
774 case PAM_ERROR_MSG:
775 case PAM_TEXT_INFO:
776 break;
777
778 default:
779 ret = PAM_CONV_ERR;
780 break;
781 }
782 }
783 }
784
785 if (ret == PAM_SUCCESS)
786 {
788 }
789 else
790 {
791 free(aresp);
792 }
793
794 return ret;
795}
796
797/*
798 * Return 1 if authenticated and 0 if failed.
799 * Called once for every username/password
800 * to be authenticated.
801 */
802static int
803pam_auth(const char *service, const struct user_pass *up)
804{
805 struct pam_conv conv;
806 pam_handle_t *pamh = NULL;
807 int status = PAM_SUCCESS;
808 int ret = 0;
809 const int name_value_list_provided = (up->name_value_list && up->name_value_list->len > 0);
810
811 /* Initialize PAM */
812 conv.conv = my_conv;
813 conv.appdata_ptr = (void *)up;
814 status = pam_start(service, name_value_list_provided ? NULL : up->username, &conv, &pamh);
815 if (status == PAM_SUCCESS)
816 {
817 /* Set PAM_RHOST environment variable */
818 if (*(up->remote))
819 {
820 status = pam_set_item(pamh, PAM_RHOST, up->remote);
821 }
822 /* Call PAM to verify username/password */
823 if (status == PAM_SUCCESS)
824 {
825 status = pam_authenticate(pamh, 0);
826 }
827 if (status == PAM_SUCCESS)
828 {
829 status = pam_acct_mgmt(pamh, 0);
830 }
831 if (status == PAM_SUCCESS)
832 {
833 ret = 1;
834 }
835
836 /* Output error message if failed */
837 if (!ret)
838 {
839 plugin_log(PLOG_ERR, MODULE, "BACKGROUND: user '%s' failed to authenticate: %s",
840 up->username, pam_strerror(pamh, status));
841 }
842
843 /* Close PAM */
844 pam_end(pamh, status);
845 }
846
847 return ret;
848}
849
850/*
851 * deferred auth handler
852 * - fork() (twice, to avoid the need for async wait / SIGCHLD handling)
853 * - query PAM stack via pam_auth()
854 * - send response back to OpenVPN via "ac_file_name"
855 *
856 * parent process returns "0" for "fork() and wait() succeeded",
857 * "-1" for "something went wrong, abort program"
858 */
859
860static void
861do_deferred_pam_auth(int fd, const char *ac_file_name, const char *service,
862 const struct user_pass *up)
863{
864 if (send_control(fd, RESPONSE_DEFER) == -1)
865 {
866 plugin_log(PLOG_ERR | PLOG_ERRNO, MODULE, "BACKGROUND: write error on response socket [4]");
867 return;
868 }
869
870 /* double forking so we do not need to wait() for async auth kids */
871 pid_t p1 = fork();
872
873 if (p1 < 0)
874 {
875 plugin_log(PLOG_ERR | PLOG_ERRNO, MODULE, "BACKGROUND: fork(1) failed");
876 return;
877 }
878 if (p1 != 0) /* parent */
879 {
880 waitpid(p1, NULL, 0);
881 return; /* parent's job succeeded */
882 }
883
884 /* child */
885 close(fd); /* socketpair no longer needed */
886
887 pid_t p2 = fork();
888 if (p2 < 0)
889 {
890 plugin_log(PLOG_ERR | PLOG_ERRNO, MODULE, "BACKGROUND: fork(2) failed");
891 exit(1);
892 }
893
894 if (p2 != 0) /* new parent: exit right away */
895 {
896 exit(0);
897 }
898
899 /* grandchild */
900 plugin_log(PLOG_NOTE, MODULE, "BACKGROUND: deferred auth for '%s', pid=%d", up->username,
901 (int)getpid());
902
903 /* the rest is very simple: do PAM, write status byte to file, done */
904 int ac_fd = open(ac_file_name, O_WRONLY);
905 if (ac_fd < 0)
906 {
907 plugin_log(PLOG_ERR | PLOG_ERRNO, MODULE, "cannot open '%s' for writing", ac_file_name);
908 exit(1);
909 }
910 int pam_success = pam_auth(service, up);
911
912 if (write(ac_fd, pam_success ? "1" : "0", 1) != 1)
913 {
914 plugin_log(PLOG_ERR | PLOG_ERRNO, MODULE, "cannot write to '%s'", ac_file_name);
915 }
916 close(ac_fd);
917 plugin_log(PLOG_NOTE, MODULE, "BACKGROUND: %s: deferred auth: PAM %s", up->username,
918 pam_success ? "succeeded" : "rejected");
919 exit(0);
920}
921
922/*
923 * Background process -- runs with privilege.
924 */
925static void
926pam_server(int fd, const char *service, int verb, const struct name_value_list *name_value_list)
927{
928 struct user_pass up;
929 char ac_file_name[PATH_MAX];
930 int command;
931#ifdef USE_PAM_DLOPEN
932 static const char pam_so[] = "libpam.so";
933#endif
934
935 /*
936 * Do initialization
937 */
938 if (DEBUG(verb))
939 {
940 plugin_log(PLOG_NOTE, MODULE, "BACKGROUND: INIT service='%s'", service);
941 }
942
943#ifdef USE_PAM_DLOPEN
944 /*
945 * Load PAM shared object
946 */
947 if (!dlopen_pam(pam_so))
948 {
949 plugin_log(PLOG_ERR, MODULE, "BACKGROUND: could not load PAM lib %s: %s", pam_so,
950 dlerror());
952 goto done;
953 }
954#endif
955
956 /*
957 * Tell foreground that we initialized successfully
958 */
960 {
961 plugin_log(PLOG_ERR | PLOG_ERRNO, MODULE, "BACKGROUND: write error on response socket [1]");
962 goto done;
963 }
964
965 plugin_log(PLOG_NOTE, MODULE, "BACKGROUND: initialization succeeded");
966
967 /*
968 * Event loop
969 */
970 while (1)
971 {
972 memset(&up, 0, sizeof(up));
973 up.verb = verb;
975
976 /* get a command from foreground process */
977 command = recv_control(fd);
978
979 if (DEBUG(verb))
980 {
981 plugin_log(PLOG_NOTE, MODULE, "BACKGROUND: received command code: %d", command);
982 }
983
984 switch (command)
985 {
986 case COMMAND_VERIFY:
987 if (recv_string(fd, up.username, sizeof(up.username)) == -1
988 || recv_string(fd, up.password, sizeof(up.password)) == -1
989 || recv_string(fd, up.common_name, sizeof(up.common_name)) == -1
990 || recv_string(fd, ac_file_name, sizeof(ac_file_name)) == -1
991 || recv_string(fd, up.remote, sizeof(up.remote)) == -1)
992 {
994 "BACKGROUND: read error on command channel: code=%d, exiting",
995 command);
996 goto done;
997 }
998
999 if (DEBUG(verb))
1000 {
1001#if 0
1002 plugin_log(PLOG_NOTE, MODULE, "BACKGROUND: USER/PASS: %s/%s",
1003 up.username, up.password);
1004#else
1005 plugin_log(PLOG_NOTE, MODULE, "BACKGROUND: USER: %s", up.username);
1006 plugin_log(PLOG_NOTE, MODULE, "BACKGROUND: REMOTE: %s", up.remote);
1007#endif
1008 }
1009
1010 /* If password is of the form SCRV1:base64:base64 split it up */
1012
1013 /* client wants deferred auth
1014 */
1015 if (strlen(ac_file_name) > 0)
1016 {
1017 do_deferred_pam_auth(fd, ac_file_name, service, &up);
1018 break;
1019 }
1020
1021
1022 /* non-deferred auth: wait for pam result and send
1023 * result back via control socketpair
1024 */
1025 if (pam_auth(service, &up)) /* Succeeded */
1026 {
1028 {
1030 "BACKGROUND: write error on response socket [2]");
1031 goto done;
1032 }
1033 }
1034 else /* Failed */
1035 {
1036 if (send_control(fd, RESPONSE_VERIFY_FAILED) == -1)
1037 {
1039 "BACKGROUND: write error on response socket [3]");
1040 goto done;
1041 }
1042 }
1043 plugin_secure_memzero(up.password, sizeof(up.password));
1044 break;
1045
1046 case COMMAND_EXIT:
1047 goto done;
1048
1049 case -1:
1051 "BACKGROUND: read error on command channel");
1052 goto done;
1053
1054 default:
1055 plugin_log(PLOG_ERR, MODULE, "BACKGROUND: unknown command code: code=%d, exiting",
1056 command);
1057 goto done;
1058 }
1059 plugin_secure_memzero(up.response, sizeof(up.response));
1060 }
1061done:
1062 plugin_secure_memzero(up.password, sizeof(up.password));
1063 plugin_secure_memzero(up.response, sizeof(up.response));
1064#ifdef USE_PAM_DLOPEN
1065 dlclose_pam();
1066#endif
1067 if (DEBUG(verb))
1068 {
1069 plugin_log(PLOG_NOTE, MODULE, "BACKGROUND: EXIT");
1070 }
1071
1072 return;
1073}
OPENVPN_EXPORT int openvpn_plugin_func_v1(openvpn_plugin_handle_t handle, const int type, const char *argv[], const char *envp[])
This function is called by OpenVPN each time the OpenVPN reaches a point where plug-in calls should h...
Definition auth-pam.c:535
static void set_signals(void)
Definition auth-pam.c:280
#define RESPONSE_DEFER
Definition auth-pam.c:65
OPENVPN_EXPORT void openvpn_plugin_close_v1(openvpn_plugin_handle_t handle)
This cleans up the last part of the plug-in, allows it to shut down cleanly and release the plug-in g...
Definition auth-pam.c:614
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:803
#define N_NAME_VALUE
Definition auth-pam.c:100
static int name_value_match(const char *query, const char *match)
Definition auth-pam.c:295
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:861
#define RESPONSE_INIT_FAILED
Definition auth-pam.c:62
static ssize_t send_string(int fd, const char *string)
Definition auth-pam.c:193
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:926
#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:645
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:262
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)
This function is called when OpenVPN loads the plug-in.
Definition auth-pam.c:369
#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:662
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:314
int string_array_len(const char **array)
Definition buffer.c:707
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
@ PLOG_NOTE
@ PLOG_ERRNO
@ PLOG_ERR
#define OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY
void * openvpn_plugin_handle_t
#define OPENVPN_PLUGIN_MASK(x)
void(* plugin_log_t)(openvpn_plugin_log_flags_t flags, const char *plugin_name, const char *format,...) _ovpn_chk_fmt(3
#define OPENVPN_PLUGIN_FUNC_DEFERRED
#define OPENVPN_PLUGIN_FUNC_SUCCESS
void(*) typedef void(*) typedef void(* plugin_secure_memzero_t)(void *data, size_t len)
Export of secure_memzero() to be used inside plug-ins.
#define OPENVPN_EXPORT
#define OPENVPN_PLUGIN_FUNC_ERROR
int(* plugin_base64_decode_t)(const char *str, void *data, int size)
Export of openvpn_base64_decode() to be used inside plug-ins.
#define msg(flags,...)
Definition error.h:152
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:471
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
Arguments used to transport variables to the plug-in.
struct openvpn_plugin_callbacks * callbacks
Arguments used to transport variables from the plug-in back to the OpenVPN process.
openvpn_plugin_handle_t handle
plugin_base64_decode_t plugin_base64_decode
plugin_secure_memzero_t plugin_secure_memzero
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
#define SIGHUP
Definition syshead.h:55
#define SIGINT
Definition syshead.h:56
#define SIGTERM
Definition syshead.h:59
#define SIGUSR1
Definition syshead.h:57
#define SIGUSR2
Definition syshead.h:58
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