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