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