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