diff options
author | Alberto Gonzalez Iniesta <agi@inittab.org> | 2016-12-27 18:25:47 +0100 |
---|---|---|
committer | Alberto Gonzalez Iniesta <agi@inittab.org> | 2016-12-27 18:25:47 +0100 |
commit | 79f3537f69e125f19f59c36aa090120a63186a54 (patch) | |
tree | 2089a3b7dac990841dbc2e4d9b2f535b82dbb0af /src/plugins/auth-pam | |
parent | f2137fedb30cb87448eb03b2f288920df6187571 (diff) | |
parent | 3a2bbdb05ca6a6996e424c9fb225cb0d53804125 (diff) |
Merge tag 'upstream/2.4.0'
Upstream version 2.4.0
Diffstat (limited to 'src/plugins/auth-pam')
-rw-r--r-- | src/plugins/auth-pam/Makefile.in | 6 | ||||
-rw-r--r-- | src/plugins/auth-pam/auth-pam.c | 1028 | ||||
-rw-r--r-- | src/plugins/auth-pam/pamdl.c | 113 | ||||
-rw-r--r-- | src/plugins/auth-pam/pamdl.h | 6 | ||||
-rw-r--r-- | src/plugins/auth-pam/utils.c | 102 | ||||
-rw-r--r-- | src/plugins/auth-pam/utils.h | 6 |
6 files changed, 680 insertions, 581 deletions
diff --git a/src/plugins/auth-pam/Makefile.in b/src/plugins/auth-pam/Makefile.in index 3a3c656..90d5058 100644 --- a/src/plugins/auth-pam/Makefile.in +++ b/src/plugins/auth-pam/Makefile.in @@ -1,4 +1,4 @@ -# Makefile.in generated by automake 1.14.1 from Makefile.am. +# Makefile.in generated by automake 1.13.4 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2013 Free Software Foundation, Inc. @@ -494,14 +494,14 @@ distclean-compile: @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c `$(CYGPATH_W) '$<'` .c.lo: @am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< diff --git a/src/plugins/auth-pam/auth-pam.c b/src/plugins/auth-pam/auth-pam.c index 5ad3ec8..d3e2c89 100644 --- a/src/plugins/auth-pam/auth-pam.c +++ b/src/plugins/auth-pam/auth-pam.c @@ -5,7 +5,7 @@ * packet encryption, packet authentication, and * packet compression. * - * Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net> + * Copyright (C) 2002-2017 OpenVPN Technologies, Inc. <sales@openvpn.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 @@ -68,14 +68,14 @@ */ struct auth_pam_context { - /* Foreground's socket to background process */ - int foreground_fd; + /* Foreground's socket to background process */ + int foreground_fd; - /* Process ID of background process */ - pid_t background_pid; + /* Process ID of background process */ + pid_t background_pid; - /* Verbosity level of OpenVPN */ - int verb; + /* Verbosity level of OpenVPN */ + int verb; }; /* @@ -90,13 +90,13 @@ struct auth_pam_context #define N_NAME_VALUE 16 struct name_value { - const char *name; - const char *value; + const char *name; + const char *value; }; struct name_value_list { - int len; - struct name_value data[N_NAME_VALUE]; + int len; + struct name_value data[N_NAME_VALUE]; }; /* @@ -104,17 +104,17 @@ struct name_value_list { * to the PAM conversation function. */ struct user_pass { - int verb; + int verb; - char username[128]; - char password[128]; - char common_name[128]; + char username[128]; + char password[128]; + char common_name[128]; - const struct name_value_list *name_value_list; + const struct name_value_list *name_value_list; }; /* Background process function */ -static void pam_server (int fd, const char *service, int verb, const struct name_value_list *name_value_list); +static void pam_server(int fd, const char *service, int verb, const struct name_value_list *name_value_list); /* @@ -122,54 +122,66 @@ static void pam_server (int fd, const char *service, int verb, const struct name */ static int -recv_control (int fd) +recv_control(int fd) { - unsigned char c; - const ssize_t size = read (fd, &c, sizeof (c)); - if (size == sizeof (c)) - return c; - else + unsigned char c; + const ssize_t size = read(fd, &c, sizeof(c)); + if (size == sizeof(c)) { - /*fprintf (stderr, "AUTH-PAM: DEBUG recv_control.read=%d\n", (int)size);*/ - return -1; + return c; + } + else + { + /*fprintf (stderr, "AUTH-PAM: DEBUG recv_control.read=%d\n", (int)size);*/ + return -1; } } static int -send_control (int fd, int code) +send_control(int fd, int code) { - unsigned char c = (unsigned char) code; - const ssize_t size = write (fd, &c, sizeof (c)); - if (size == sizeof (c)) - return (int) size; - else - return -1; + unsigned char c = (unsigned char) code; + const ssize_t size = write(fd, &c, sizeof(c)); + if (size == sizeof(c)) + { + return (int) size; + } + else + { + return -1; + } } static int -recv_string (int fd, char *buffer, int len) +recv_string(int fd, char *buffer, int len) { - if (len > 0) + if (len > 0) { - ssize_t size; - memset (buffer, 0, len); - size = read (fd, buffer, len); - buffer[len-1] = 0; - if (size >= 1) - return (int)size; + ssize_t size; + memset(buffer, 0, len); + size = read(fd, buffer, len); + buffer[len-1] = 0; + if (size >= 1) + { + return (int)size; + } } - return -1; + return -1; } static int -send_string (int fd, const char *string) +send_string(int fd, const char *string) { - const int len = strlen (string) + 1; - const ssize_t size = write (fd, string, len); - if (size == len) - return (int) size; - else - return -1; + const int len = strlen(string) + 1; + const ssize_t size = write(fd, string, len); + if (size == len) + { + return (int) size; + } + else + { + return -1; + } } #ifdef DO_DAEMONIZE @@ -180,28 +192,30 @@ send_string (int fd, const char *string) * "daemon_log_redirect" env var is true. */ static void -daemonize (const char *envp[]) +daemonize(const char *envp[]) { - const char *daemon_string = get_env ("daemon", envp); - if (daemon_string && daemon_string[0] == '1') - { - const char *log_redirect = get_env ("daemon_log_redirect", envp); - int fd = -1; - if (log_redirect && log_redirect[0] == '1') - fd = dup (2); - if (daemon (0, 0) < 0) - { - fprintf (stderr, "AUTH-PAM: daemonization failed\n"); - } - else if (fd >= 3) - { - dup2 (fd, 2); - close (fd); - } + const char *daemon_string = get_env("daemon", envp); + if (daemon_string && daemon_string[0] == '1') + { + const char *log_redirect = get_env("daemon_log_redirect", envp); + int fd = -1; + if (log_redirect && log_redirect[0] == '1') + { + fd = dup(2); + } + if (daemon(0, 0) < 0) + { + fprintf(stderr, "AUTH-PAM: daemonization failed\n"); + } + else if (fd >= 3) + { + dup2(fd, 2); + close(fd); + } } } -#endif +#endif /* ifdef DO_DAEMONIZE */ /* * Close most of parent's fds. @@ -214,14 +228,16 @@ daemonize (const char *envp[]) * fds from crossing a fork(). */ static void -close_fds_except (int keep) +close_fds_except(int keep) { - int i; - closelog (); - for (i = 3; i <= 100; ++i) + int i; + closelog(); + for (i = 3; i <= 100; ++i) { - if (i != keep) - close (i); + if (i != keep) + { + close(i); + } } } @@ -230,243 +246,263 @@ close_fds_except (int keep) * deal with them. */ static void -set_signals (void) +set_signals(void) { - signal (SIGTERM, SIG_DFL); + signal(SIGTERM, SIG_DFL); - signal (SIGINT, SIG_IGN); - signal (SIGHUP, SIG_IGN); - signal (SIGUSR1, SIG_IGN); - signal (SIGUSR2, SIG_IGN); - signal (SIGPIPE, SIG_IGN); + signal(SIGINT, SIG_IGN); + signal(SIGHUP, SIG_IGN); + signal(SIGUSR1, SIG_IGN); + signal(SIGUSR2, SIG_IGN); + signal(SIGPIPE, SIG_IGN); } /* * Return 1 if query matches match. */ static int -name_value_match (const char *query, const char *match) +name_value_match(const char *query, const char *match) { - while (!isalnum (*query)) + while (!isalnum(*query)) { - if (*query == '\0') - return 0; - ++query; + if (*query == '\0') + { + return 0; + } + ++query; } - return strncasecmp (match, query, strlen (match)) == 0; + return strncasecmp(match, query, strlen(match)) == 0; } OPENVPN_EXPORT openvpn_plugin_handle_t -openvpn_plugin_open_v1 (unsigned int *type_mask, const char *argv[], const char *envp[]) +openvpn_plugin_open_v1(unsigned int *type_mask, const char *argv[], const char *envp[]) { - pid_t pid; - int fd[2]; - - struct auth_pam_context *context; - struct name_value_list name_value_list; - - const int base_parms = 2; - - /* - * Allocate our context - */ - context = (struct auth_pam_context *) calloc (1, sizeof (struct auth_pam_context)); - if (!context) - goto error; - context->foreground_fd = -1; - - /* - * Intercept the --auth-user-pass-verify callback. - */ - *type_mask = OPENVPN_PLUGIN_MASK (OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY); + pid_t pid; + int fd[2]; + + struct auth_pam_context *context; + struct name_value_list name_value_list; - /* - * Make sure we have two string arguments: the first is the .so name, - * the second is the PAM service type. - */ - if (string_array_len (argv) < base_parms) + const int base_parms = 2; + + /* + * Allocate our context + */ + context = (struct auth_pam_context *) calloc(1, sizeof(struct auth_pam_context)); + if (!context) { - fprintf (stderr, "AUTH-PAM: need PAM service parameter\n"); - goto error; + goto error; } - - /* - * See if we have optional name/value pairs to match against - * PAM module queried fields in the conversation function. - */ - name_value_list.len = 0; - if (string_array_len (argv) > base_parms) + context->foreground_fd = -1; + + /* + * Intercept the --auth-user-pass-verify callback. + */ + *type_mask = OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY); + + /* + * Make sure we have two string arguments: the first is the .so name, + * the second is the PAM service type. + */ + if (string_array_len(argv) < base_parms) { - const int nv_len = string_array_len (argv) - base_parms; - int i; - - if ((nv_len & 1) == 1 || (nv_len / 2) > N_NAME_VALUE) - { - fprintf (stderr, "AUTH-PAM: bad name/value list length\n"); - goto error; - } - - name_value_list.len = nv_len / 2; - for (i = 0; i < name_value_list.len; ++i) - { - const int base = base_parms + i * 2; - name_value_list.data[i].name = argv[base]; - name_value_list.data[i].value = argv[base+1]; - } + fprintf(stderr, "AUTH-PAM: need PAM service parameter\n"); + goto error; } - /* - * Get verbosity level from environment - */ - { - const char *verb_string = get_env ("verb", envp); - if (verb_string) - context->verb = atoi (verb_string); - } - - /* - * Make a socket for foreground and background processes - * to communicate. - */ - if (socketpair (PF_UNIX, SOCK_DGRAM, 0, fd) == -1) + /* + * See if we have optional name/value pairs to match against + * PAM module queried fields in the conversation function. + */ + name_value_list.len = 0; + if (string_array_len(argv) > base_parms) { - fprintf (stderr, "AUTH-PAM: socketpair call failed\n"); - goto error; + const int nv_len = string_array_len(argv) - base_parms; + int i; + + if ((nv_len & 1) == 1 || (nv_len / 2) > N_NAME_VALUE) + { + fprintf(stderr, "AUTH-PAM: bad name/value list length\n"); + goto error; + } + + name_value_list.len = nv_len / 2; + for (i = 0; i < name_value_list.len; ++i) + { + const int base = base_parms + i * 2; + name_value_list.data[i].name = argv[base]; + name_value_list.data[i].value = argv[base+1]; + } } - /* - * Fork off the privileged process. It will remain privileged - * even after the foreground process drops its privileges. - */ - pid = fork (); - - if (pid) + /* + * Get verbosity level from environment + */ { - int status; - - /* - * Foreground Process - */ - - context->background_pid = pid; + const char *verb_string = get_env("verb", envp); + if (verb_string) + { + context->verb = atoi(verb_string); + } + } - /* close our copy of child's socket */ - close (fd[1]); + /* + * Make a socket for foreground and background processes + * to communicate. + */ + if (socketpair(PF_UNIX, SOCK_DGRAM, 0, fd) == -1) + { + fprintf(stderr, "AUTH-PAM: socketpair call failed\n"); + goto error; + } - /* don't let future subprocesses inherit child socket */ - if (fcntl (fd[0], F_SETFD, FD_CLOEXEC) < 0) - fprintf (stderr, "AUTH-PAM: Set FD_CLOEXEC flag on socket file descriptor failed\n"); + /* + * Fork off the privileged process. It will remain privileged + * even after the foreground process drops its privileges. + */ + pid = fork(); - /* wait for background child process to initialize */ - status = recv_control (fd[0]); - if (status == RESPONSE_INIT_SUCCEEDED) - { - context->foreground_fd = fd[0]; - return (openvpn_plugin_handle_t) context; - } + if (pid) + { + int status; + + /* + * Foreground Process + */ + + context->background_pid = pid; + + /* close our copy of child's socket */ + close(fd[1]); + + /* don't let future subprocesses inherit child socket */ + if (fcntl(fd[0], F_SETFD, FD_CLOEXEC) < 0) + { + fprintf(stderr, "AUTH-PAM: Set FD_CLOEXEC flag on socket file descriptor failed\n"); + } + + /* wait for background child process to initialize */ + status = recv_control(fd[0]); + if (status == RESPONSE_INIT_SUCCEEDED) + { + context->foreground_fd = fd[0]; + return (openvpn_plugin_handle_t) context; + } } - else + else { - /* - * Background Process - */ + /* + * Background Process + */ - /* close all parent fds except our socket back to parent */ - close_fds_except (fd[1]); + /* close all parent fds except our socket back to parent */ + close_fds_except(fd[1]); - /* Ignore most signals (the parent will receive them) */ - set_signals (); + /* Ignore most signals (the parent will receive them) */ + set_signals(); #ifdef DO_DAEMONIZE - /* Daemonize if --daemon option is set. */ - daemonize (envp); + /* Daemonize if --daemon option is set. */ + daemonize(envp); #endif - /* execute the event loop */ - pam_server (fd[1], argv[1], context->verb, &name_value_list); + /* execute the event loop */ + pam_server(fd[1], argv[1], context->verb, &name_value_list); - close (fd[1]); + close(fd[1]); - exit (0); - return 0; /* NOTREACHED */ + exit(0); + return 0; /* NOTREACHED */ } - error: - if (context) - free (context); - return NULL; +error: + if (context) + { + free(context); + } + return NULL; } OPENVPN_EXPORT int -openvpn_plugin_func_v1 (openvpn_plugin_handle_t handle, const int type, const char *argv[], const char *envp[]) +openvpn_plugin_func_v1(openvpn_plugin_handle_t handle, const int type, const char *argv[], const char *envp[]) { - struct auth_pam_context *context = (struct auth_pam_context *) handle; - - if (type == OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY && context->foreground_fd >= 0) - { - /* get username/password from envp string array */ - const char *username = get_env ("username", envp); - const char *password = get_env ("password", envp); - const char *common_name = get_env ("common_name", envp) ? get_env ("common_name", envp) : ""; - - if (username && strlen (username) > 0 && password) - { - if (send_control (context->foreground_fd, COMMAND_VERIFY) == -1 - || send_string (context->foreground_fd, username) == -1 - || send_string (context->foreground_fd, password) == -1 - || send_string (context->foreground_fd, common_name) == -1) - { - fprintf (stderr, "AUTH-PAM: Error sending auth info to background process\n"); - } - else - { - const int status = recv_control (context->foreground_fd); - if (status == RESPONSE_VERIFY_SUCCEEDED) - return OPENVPN_PLUGIN_FUNC_SUCCESS; - if (status == -1) - fprintf (stderr, "AUTH-PAM: Error receiving auth confirmation from background process\n"); - } - } - } - return OPENVPN_PLUGIN_FUNC_ERROR; + struct auth_pam_context *context = (struct auth_pam_context *) handle; + + if (type == OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY && context->foreground_fd >= 0) + { + /* get username/password from envp string array */ + const char *username = get_env("username", envp); + const char *password = get_env("password", envp); + const char *common_name = get_env("common_name", envp) ? get_env("common_name", envp) : ""; + + if (username && strlen(username) > 0 && password) + { + if (send_control(context->foreground_fd, COMMAND_VERIFY) == -1 + || send_string(context->foreground_fd, username) == -1 + || send_string(context->foreground_fd, password) == -1 + || send_string(context->foreground_fd, common_name) == -1) + { + fprintf(stderr, "AUTH-PAM: Error sending auth info to background process\n"); + } + else + { + const int status = recv_control(context->foreground_fd); + if (status == RESPONSE_VERIFY_SUCCEEDED) + { + return OPENVPN_PLUGIN_FUNC_SUCCESS; + } + if (status == -1) + { + fprintf(stderr, "AUTH-PAM: Error receiving auth confirmation from background process\n"); + } + } + } + } + return OPENVPN_PLUGIN_FUNC_ERROR; } OPENVPN_EXPORT void -openvpn_plugin_close_v1 (openvpn_plugin_handle_t handle) +openvpn_plugin_close_v1(openvpn_plugin_handle_t handle) { - struct auth_pam_context *context = (struct auth_pam_context *) handle; - - if (DEBUG (context->verb)) - fprintf (stderr, "AUTH-PAM: close\n"); + struct auth_pam_context *context = (struct auth_pam_context *) handle; - if (context->foreground_fd >= 0) + if (DEBUG(context->verb)) { - /* tell background process to exit */ - if (send_control (context->foreground_fd, COMMAND_EXIT) == -1) - fprintf (stderr, "AUTH-PAM: Error signaling background process to exit\n"); - - /* wait for background process to exit */ - if (context->background_pid > 0) - waitpid (context->background_pid, NULL, 0); + fprintf(stderr, "AUTH-PAM: close\n"); + } - close (context->foreground_fd); - context->foreground_fd = -1; + if (context->foreground_fd >= 0) + { + /* tell background process to exit */ + if (send_control(context->foreground_fd, COMMAND_EXIT) == -1) + { + fprintf(stderr, "AUTH-PAM: Error signaling background process to exit\n"); + } + + /* wait for background process to exit */ + if (context->background_pid > 0) + { + waitpid(context->background_pid, NULL, 0); + } + + close(context->foreground_fd); + context->foreground_fd = -1; } - free (context); + free(context); } OPENVPN_EXPORT void -openvpn_plugin_abort_v1 (openvpn_plugin_handle_t handle) +openvpn_plugin_abort_v1(openvpn_plugin_handle_t handle) { - struct auth_pam_context *context = (struct auth_pam_context *) handle; + struct auth_pam_context *context = (struct auth_pam_context *) handle; - /* tell background process to exit */ - if (context && context->foreground_fd >= 0) + /* tell background process to exit */ + if (context && context->foreground_fd >= 0) { - send_control (context->foreground_fd, COMMAND_EXIT); - close (context->foreground_fd); - context->foreground_fd = -1; + send_control(context->foreground_fd, COMMAND_EXIT); + close(context->foreground_fd); + context->foreground_fd = -1; } } @@ -474,111 +510,137 @@ openvpn_plugin_abort_v1 (openvpn_plugin_handle_t handle) * PAM conversation function */ static int -my_conv (int n, const struct pam_message **msg_array, - struct pam_response **response_array, void *appdata_ptr) +my_conv(int n, const struct pam_message **msg_array, + struct pam_response **response_array, void *appdata_ptr) { - const struct user_pass *up = ( const struct user_pass *) appdata_ptr; - struct pam_response *aresp; - int i; - int ret = PAM_SUCCESS; - - *response_array = NULL; - - if (n <= 0 || n > PAM_MAX_NUM_MSG) - return (PAM_CONV_ERR); - if ((aresp = calloc (n, sizeof *aresp)) == NULL) - return (PAM_BUF_ERR); - - /* loop through each PAM-module query */ - for (i = 0; i < n; ++i) - { - const struct pam_message *msg = msg_array[i]; - aresp[i].resp_retcode = 0; - aresp[i].resp = NULL; - - if (DEBUG (up->verb)) - { - fprintf (stderr, "AUTH-PAM: BACKGROUND: my_conv[%d] query='%s' style=%d\n", - i, - msg->msg ? msg->msg : "NULL", - msg->msg_style); - } - - if (up->name_value_list && up->name_value_list->len > 0) - { - /* use name/value list match method */ - const struct name_value_list *list = up->name_value_list; - int j; - - /* loop through name/value pairs */ - for (j = 0; j < list->len; ++j) - { - const char *match_name = list->data[j].name; - const char *match_value = list->data[j].value; - - if (name_value_match (msg->msg, match_name)) - { - /* found name/value match */ - aresp[i].resp = NULL; - - if (DEBUG (up->verb)) - fprintf (stderr, "AUTH-PAM: BACKGROUND: name match found, query/match-string ['%s', '%s'] = '%s'\n", - msg->msg, - match_name, - match_value); - - if (strstr(match_value, "USERNAME")) - aresp[i].resp = searchandreplace(match_value, "USERNAME", up->username); - else if (strstr(match_value, "PASSWORD")) - aresp[i].resp = searchandreplace(match_value, "PASSWORD", up->password); - else if (strstr(match_value, "COMMONNAME")) - aresp[i].resp = searchandreplace(match_value, "COMMONNAME", up->common_name); - else - aresp[i].resp = strdup (match_value); - - if (aresp[i].resp == NULL) - ret = PAM_CONV_ERR; - break; - } - } - - if (j == list->len) - ret = PAM_CONV_ERR; - } - else - { - /* use PAM_PROMPT_ECHO_x hints */ - switch (msg->msg_style) - { - case PAM_PROMPT_ECHO_OFF: - aresp[i].resp = strdup (up->password); - if (aresp[i].resp == NULL) - ret = PAM_CONV_ERR; - break; - - case PAM_PROMPT_ECHO_ON: - aresp[i].resp = strdup (up->username); - if (aresp[i].resp == NULL) - ret = PAM_CONV_ERR; - break; - - case PAM_ERROR_MSG: - case PAM_TEXT_INFO: - break; - - default: - ret = PAM_CONV_ERR; - break; - } - } - } - - if (ret == PAM_SUCCESS) - *response_array = aresp; - else - free(aresp); - - return ret; + const struct user_pass *up = ( const struct user_pass *) appdata_ptr; + struct pam_response *aresp; + int i; + int ret = PAM_SUCCESS; + + *response_array = NULL; + + if (n <= 0 || n > PAM_MAX_NUM_MSG) + { + return (PAM_CONV_ERR); + } + if ((aresp = calloc(n, sizeof *aresp)) == NULL) + { + return (PAM_BUF_ERR); + } + + /* loop through each PAM-module query */ + for (i = 0; i < n; ++i) + { + const struct pam_message *msg = msg_array[i]; + aresp[i].resp_retcode = 0; + aresp[i].resp = NULL; + + if (DEBUG(up->verb)) + { + fprintf(stderr, "AUTH-PAM: BACKGROUND: my_conv[%d] query='%s' style=%d\n", + i, + msg->msg ? msg->msg : "NULL", + msg->msg_style); + } + + if (up->name_value_list && up->name_value_list->len > 0) + { + /* use name/value list match method */ + const struct name_value_list *list = up->name_value_list; + int j; + + /* loop through name/value pairs */ + for (j = 0; j < list->len; ++j) + { + const char *match_name = list->data[j].name; + const char *match_value = list->data[j].value; + + if (name_value_match(msg->msg, match_name)) + { + /* found name/value match */ + aresp[i].resp = NULL; + + if (DEBUG(up->verb)) + { + fprintf(stderr, "AUTH-PAM: BACKGROUND: name match found, query/match-string ['%s', '%s'] = '%s'\n", + msg->msg, + match_name, + match_value); + } + + if (strstr(match_value, "USERNAME")) + { + aresp[i].resp = searchandreplace(match_value, "USERNAME", up->username); + } + else if (strstr(match_value, "PASSWORD")) + { + aresp[i].resp = searchandreplace(match_value, "PASSWORD", up->password); + } + else if (strstr(match_value, "COMMONNAME")) + { + aresp[i].resp = searchandreplace(match_value, "COMMONNAME", up->common_name); + } + else + { + aresp[i].resp = strdup(match_value); + } + + if (aresp[i].resp == NULL) + { + ret = PAM_CONV_ERR; + } + break; + } + } + + if (j == list->len) + { + ret = PAM_CONV_ERR; + } + } + else + { + /* use PAM_PROMPT_ECHO_x hints */ + switch (msg->msg_style) + { + case PAM_PROMPT_ECHO_OFF: + aresp[i].resp = strdup(up->password); + if (aresp[i].resp == NULL) + { + ret = PAM_CONV_ERR; + } + break; + + case PAM_PROMPT_ECHO_ON: + aresp[i].resp = strdup(up->username); + if (aresp[i].resp == NULL) + { + ret = PAM_CONV_ERR; + } + break; + + case PAM_ERROR_MSG: + case PAM_TEXT_INFO: + break; + + default: + ret = PAM_CONV_ERR; + break; + } + } + } + + if (ret == PAM_SUCCESS) + { + *response_array = aresp; + } + else + { + free(aresp); + } + + return ret; } /* @@ -587,156 +649,166 @@ my_conv (int n, const struct pam_message **msg_array, * to be authenticated. */ static int -pam_auth (const char *service, const struct user_pass *up) +pam_auth(const char *service, const struct user_pass *up) { - struct pam_conv conv; - pam_handle_t *pamh = NULL; - int status = PAM_SUCCESS; - int ret = 0; - const int name_value_list_provided = (up->name_value_list && up->name_value_list->len > 0); - - /* Initialize PAM */ - conv.conv = my_conv; - conv.appdata_ptr = (void *)up; - status = pam_start (service, name_value_list_provided ? NULL : up->username, &conv, &pamh); - if (status == PAM_SUCCESS) - { - /* Call PAM to verify username/password */ - status = pam_authenticate(pamh, 0); - if (status == PAM_SUCCESS) - status = pam_acct_mgmt (pamh, 0); - if (status == PAM_SUCCESS) - ret = 1; - - /* Output error message if failed */ - if (!ret) - { - fprintf (stderr, "AUTH-PAM: BACKGROUND: user '%s' failed to authenticate: %s\n", - up->username, - pam_strerror (pamh, status)); - } - - /* Close PAM */ - pam_end (pamh, status); - } - - return ret; + struct pam_conv conv; + pam_handle_t *pamh = NULL; + int status = PAM_SUCCESS; + int ret = 0; + const int name_value_list_provided = (up->name_value_list && up->name_value_list->len > 0); + + /* Initialize PAM */ + conv.conv = my_conv; + conv.appdata_ptr = (void *)up; + status = pam_start(service, name_value_list_provided ? NULL : up->username, &conv, &pamh); + if (status == PAM_SUCCESS) + { + /* Call PAM to verify username/password */ + status = pam_authenticate(pamh, 0); + if (status == PAM_SUCCESS) + { + status = pam_acct_mgmt(pamh, 0); + } + if (status == PAM_SUCCESS) + { + ret = 1; + } + + /* Output error message if failed */ + if (!ret) + { + fprintf(stderr, "AUTH-PAM: BACKGROUND: user '%s' failed to authenticate: %s\n", + up->username, + pam_strerror(pamh, status)); + } + + /* Close PAM */ + pam_end(pamh, status); + } + + return ret; } /* * Background process -- runs with privilege. */ static void -pam_server (int fd, const char *service, int verb, const struct name_value_list *name_value_list) +pam_server(int fd, const char *service, int verb, const struct name_value_list *name_value_list) { - struct user_pass up; - int command; + struct user_pass up; + int command; #ifdef USE_PAM_DLOPEN - static const char pam_so[] = "libpam.so"; + static const char pam_so[] = "libpam.so"; #endif - /* - * Do initialization - */ - if (DEBUG (verb)) - fprintf (stderr, "AUTH-PAM: BACKGROUND: INIT service='%s'\n", service); + /* + * Do initialization + */ + if (DEBUG(verb)) + { + fprintf(stderr, "AUTH-PAM: BACKGROUND: INIT service='%s'\n", service); + } #ifdef USE_PAM_DLOPEN - /* - * Load PAM shared object - */ - if (!dlopen_pam (pam_so)) + /* + * Load PAM shared object + */ + if (!dlopen_pam(pam_so)) { - fprintf (stderr, "AUTH-PAM: BACKGROUND: could not load PAM lib %s: %s\n", pam_so, dlerror()); - send_control (fd, RESPONSE_INIT_FAILED); - goto done; + fprintf(stderr, "AUTH-PAM: BACKGROUND: could not load PAM lib %s: %s\n", pam_so, dlerror()); + send_control(fd, RESPONSE_INIT_FAILED); + goto done; } #endif - /* - * Tell foreground that we initialized successfully - */ - if (send_control (fd, RESPONSE_INIT_SUCCEEDED) == -1) + /* + * Tell foreground that we initialized successfully + */ + if (send_control(fd, RESPONSE_INIT_SUCCEEDED) == -1) { - fprintf (stderr, "AUTH-PAM: BACKGROUND: write error on response socket [1]\n"); - goto done; + fprintf(stderr, "AUTH-PAM: BACKGROUND: write error on response socket [1]\n"); + goto done; } - /* - * Event loop - */ - while (1) + /* + * Event loop + */ + while (1) { - memset (&up, 0, sizeof (up)); - up.verb = verb; - up.name_value_list = name_value_list; - - /* get a command from foreground process */ - command = recv_control (fd); - - if (DEBUG (verb)) - fprintf (stderr, "AUTH-PAM: BACKGROUND: received command code: %d\n", command); - - switch (command) - { - case COMMAND_VERIFY: - if (recv_string (fd, up.username, sizeof (up.username)) == -1 - || recv_string (fd, up.password, sizeof (up.password)) == -1 - || recv_string (fd, up.common_name, sizeof (up.common_name)) == -1) - { - fprintf (stderr, "AUTH-PAM: BACKGROUND: read error on command channel: code=%d, exiting\n", - command); - goto done; - } - - if (DEBUG (verb)) - { + memset(&up, 0, sizeof(up)); + up.verb = verb; + up.name_value_list = name_value_list; + + /* get a command from foreground process */ + command = recv_control(fd); + + if (DEBUG(verb)) + { + fprintf(stderr, "AUTH-PAM: BACKGROUND: received command code: %d\n", command); + } + + switch (command) + { + case COMMAND_VERIFY: + if (recv_string(fd, up.username, sizeof(up.username)) == -1 + || recv_string(fd, up.password, sizeof(up.password)) == -1 + || recv_string(fd, up.common_name, sizeof(up.common_name)) == -1) + { + fprintf(stderr, "AUTH-PAM: BACKGROUND: read error on command channel: code=%d, exiting\n", + command); + goto done; + } + + if (DEBUG(verb)) + { #if 0 - fprintf (stderr, "AUTH-PAM: BACKGROUND: USER/PASS: %s/%s\n", - up.username, up.password); + fprintf(stderr, "AUTH-PAM: BACKGROUND: USER/PASS: %s/%s\n", + up.username, up.password); #else - fprintf (stderr, "AUTH-PAM: BACKGROUND: USER: %s\n", up.username); + fprintf(stderr, "AUTH-PAM: BACKGROUND: USER: %s\n", up.username); #endif - } - - if (pam_auth (service, &up)) /* Succeeded */ - { - if (send_control (fd, RESPONSE_VERIFY_SUCCEEDED) == -1) - { - fprintf (stderr, "AUTH-PAM: BACKGROUND: write error on response socket [2]\n"); - goto done; - } - } - else /* Failed */ - { - if (send_control (fd, RESPONSE_VERIFY_FAILED) == -1) - { - fprintf (stderr, "AUTH-PAM: BACKGROUND: write error on response socket [3]\n"); - goto done; - } - } - break; - - case COMMAND_EXIT: - goto done; - - case -1: - fprintf (stderr, "AUTH-PAM: BACKGROUND: read error on command channel\n"); - goto done; - - default: - fprintf (stderr, "AUTH-PAM: BACKGROUND: unknown command code: code=%d, exiting\n", - command); - goto done; - } - } - done: + } + + if (pam_auth(service, &up)) /* Succeeded */ + { + if (send_control(fd, RESPONSE_VERIFY_SUCCEEDED) == -1) + { + fprintf(stderr, "AUTH-PAM: BACKGROUND: write error on response socket [2]\n"); + goto done; + } + } + else /* Failed */ + { + if (send_control(fd, RESPONSE_VERIFY_FAILED) == -1) + { + fprintf(stderr, "AUTH-PAM: BACKGROUND: write error on response socket [3]\n"); + goto done; + } + } + break; + + case COMMAND_EXIT: + goto done; + + case -1: + fprintf(stderr, "AUTH-PAM: BACKGROUND: read error on command channel\n"); + goto done; + + default: + fprintf(stderr, "AUTH-PAM: BACKGROUND: unknown command code: code=%d, exiting\n", + command); + goto done; + } + } +done: #ifdef USE_PAM_DLOPEN - dlclose_pam (); + dlclose_pam(); #endif - if (DEBUG (verb)) - fprintf (stderr, "AUTH-PAM: BACKGROUND: EXIT\n"); + if (DEBUG(verb)) + { + fprintf(stderr, "AUTH-PAM: BACKGROUND: EXIT\n"); + } - return; + return; } diff --git a/src/plugins/auth-pam/pamdl.c b/src/plugins/auth-pam/pamdl.c index 26e9821..02ea71a 100644 --- a/src/plugins/auth-pam/pamdl.c +++ b/src/plugins/auth-pam/pamdl.c @@ -21,125 +21,136 @@ static void *libpam_h = NULL; #define RESOLVE_PAM_FUNCTION(x, y, z, err) \ { \ - union { const void *tpointer; y (*fn) z ; } fptr; \ - fptr.tpointer = dlsym(libpam_h, #x); real_##x = fptr.fn; \ - if (real_##x == NULL) { \ - fprintf (stderr, "PAMDL: unable to resolve '%s': %s\n", #x, dlerror()); \ - return err; \ - } \ + union { const void *tpointer; y(*fn) z; } fptr; \ + fptr.tpointer = dlsym(libpam_h, #x); real_ ## x = fptr.fn; \ + if (real_ ## x == NULL) { \ + fprintf(stderr, "PAMDL: unable to resolve '%s': %s\n", #x, dlerror()); \ + return err; \ + } \ } int -dlopen_pam (const char *so) +dlopen_pam(const char *so) { - if (libpam_h == NULL) + if (libpam_h == NULL) { - libpam_h = dlopen(so, RTLD_GLOBAL|RTLD_NOW); + libpam_h = dlopen(so, RTLD_GLOBAL|RTLD_NOW); } - return libpam_h != NULL; + return libpam_h != NULL; } void -dlclose_pam (void) +dlclose_pam(void) { - if (libpam_h != NULL) + if (libpam_h != NULL) { - dlclose(libpam_h); - libpam_h = NULL; + dlclose(libpam_h); + libpam_h = NULL; } } -int pam_start(const char *service_name, const char *user, - const struct pam_conv *pam_conversation, - pam_handle_t **pamh) +int +pam_start(const char *service_name, const char *user, + const struct pam_conv *pam_conversation, + pam_handle_t **pamh) { int (*real_pam_start)(const char *, const char *, - const struct pam_conv *, - pam_handle_t **); + const struct pam_conv *, + pam_handle_t **); RESOLVE_PAM_FUNCTION(pam_start, int, (const char *, const char *, - const struct pam_conv *, - pam_handle_t **), PAM_ABORT); + const struct pam_conv *, + pam_handle_t **), PAM_ABORT); return real_pam_start(service_name, user, pam_conversation, pamh); } -int pam_end(pam_handle_t *pamh, int pam_status) +int +pam_end(pam_handle_t *pamh, int pam_status) { int (*real_pam_end)(pam_handle_t *, int); RESOLVE_PAM_FUNCTION(pam_end, int, (pam_handle_t *, int), PAM_ABORT); return real_pam_end(pamh, pam_status); } -int pam_set_item(pam_handle_t *pamh, int item_type, const void *item) +int +pam_set_item(pam_handle_t *pamh, int item_type, const void *item) { int (*real_pam_set_item)(pam_handle_t *, int, const void *); RESOLVE_PAM_FUNCTION(pam_set_item, int, - (pam_handle_t *, int, const void *), PAM_ABORT); + (pam_handle_t *, int, const void *), PAM_ABORT); return real_pam_set_item(pamh, item_type, item); } -int pam_get_item(const pam_handle_t *pamh, int item_type, const void **item) +int +pam_get_item(const pam_handle_t *pamh, int item_type, const void **item) { int (*real_pam_get_item)(const pam_handle_t *, int, const void **); RESOLVE_PAM_FUNCTION(pam_get_item, int, - (const pam_handle_t *, int, const void **), - PAM_ABORT); + (const pam_handle_t *, int, const void **), + PAM_ABORT); return real_pam_get_item(pamh, item_type, item); } -int pam_fail_delay(pam_handle_t *pamh, unsigned int musec_delay) +int +pam_fail_delay(pam_handle_t *pamh, unsigned int musec_delay) { int (*real_pam_fail_delay)(pam_handle_t *, unsigned int); RESOLVE_PAM_FUNCTION(pam_fail_delay, int, (pam_handle_t *, unsigned int), - PAM_ABORT); + PAM_ABORT); return real_pam_fail_delay(pamh, musec_delay); } -typedef const char * const_char_pointer; +typedef const char *const_char_pointer; -const_char_pointer pam_strerror(pam_handle_t *pamh, int errnum) +const_char_pointer +pam_strerror(pam_handle_t *pamh, int errnum) { const_char_pointer (*real_pam_strerror)(pam_handle_t *, int); RESOLVE_PAM_FUNCTION(pam_strerror, const_char_pointer, - (pam_handle_t *, int), NULL); + (pam_handle_t *, int), NULL); return real_pam_strerror(pamh, errnum); } -int pam_putenv(pam_handle_t *pamh, const char *name_value) +int +pam_putenv(pam_handle_t *pamh, const char *name_value) { int (*real_pam_putenv)(pam_handle_t *, const char *); RESOLVE_PAM_FUNCTION(pam_putenv, int, (pam_handle_t *, const char *), - PAM_ABORT); + PAM_ABORT); return real_pam_putenv(pamh, name_value); } -const_char_pointer pam_getenv(pam_handle_t *pamh, const char *name) +const_char_pointer +pam_getenv(pam_handle_t *pamh, const char *name) { const_char_pointer (*real_pam_getenv)(pam_handle_t *, const char *); RESOLVE_PAM_FUNCTION(pam_getenv, const_char_pointer, - (pam_handle_t *, const char *), NULL); + (pam_handle_t *, const char *), NULL); return real_pam_getenv(pamh, name); } -typedef char ** char_ppointer; -char_ppointer pam_getenvlist(pam_handle_t *pamh) +typedef char **char_ppointer; +char_ppointer +pam_getenvlist(pam_handle_t *pamh) { char_ppointer (*real_pam_getenvlist)(pam_handle_t *); RESOLVE_PAM_FUNCTION(pam_getenvlist, char_ppointer, (pam_handle_t *), - NULL); + NULL); return real_pam_getenvlist(pamh); } /* Authentication management */ -int pam_authenticate(pam_handle_t *pamh, int flags) +int +pam_authenticate(pam_handle_t *pamh, int flags) { int (*real_pam_authenticate)(pam_handle_t *, int); RESOLVE_PAM_FUNCTION(pam_authenticate, int, (pam_handle_t *, int), - PAM_ABORT); + PAM_ABORT); return real_pam_authenticate(pamh, flags); } -int pam_setcred(pam_handle_t *pamh, int flags) +int +pam_setcred(pam_handle_t *pamh, int flags) { int (*real_pam_setcred)(pam_handle_t *, int); RESOLVE_PAM_FUNCTION(pam_setcred, int, (pam_handle_t *, int), PAM_ABORT); @@ -148,7 +159,8 @@ int pam_setcred(pam_handle_t *pamh, int flags) /* Account Management API's */ -int pam_acct_mgmt(pam_handle_t *pamh, int flags) +int +pam_acct_mgmt(pam_handle_t *pamh, int flags) { int (*real_pam_acct_mgmt)(pam_handle_t *, int); RESOLVE_PAM_FUNCTION(pam_acct_mgmt, int, (pam_handle_t *, int), PAM_ABORT); @@ -157,28 +169,31 @@ int pam_acct_mgmt(pam_handle_t *pamh, int flags) /* Session Management API's */ -int pam_open_session(pam_handle_t *pamh, int flags) +int +pam_open_session(pam_handle_t *pamh, int flags) { int (*real_pam_open_session)(pam_handle_t *, int); RESOLVE_PAM_FUNCTION(pam_open_session, int, (pam_handle_t *, int), - PAM_ABORT); + PAM_ABORT); return real_pam_open_session(pamh, flags); } -int pam_close_session(pam_handle_t *pamh, int flags) +int +pam_close_session(pam_handle_t *pamh, int flags) { int (*real_pam_close_session)(pam_handle_t *, int); RESOLVE_PAM_FUNCTION(pam_close_session, int, (pam_handle_t *, int), - PAM_ABORT); + PAM_ABORT); return real_pam_close_session(pamh, flags); } /* Password Management API's */ -int pam_chauthtok(pam_handle_t *pamh, int flags) +int +pam_chauthtok(pam_handle_t *pamh, int flags) { int (*real_pam_chauthtok)(pam_handle_t *, int); RESOLVE_PAM_FUNCTION(pam_chauthtok, int, (pam_handle_t *, int), PAM_ABORT); return real_pam_chauthtok(pamh, flags); } -#endif +#endif /* ifdef USE_PAM_DLOPEN */ diff --git a/src/plugins/auth-pam/pamdl.h b/src/plugins/auth-pam/pamdl.h index 12ba068..15ca85e 100644 --- a/src/plugins/auth-pam/pamdl.h +++ b/src/plugins/auth-pam/pamdl.h @@ -1,5 +1,7 @@ #ifdef USE_PAM_DLOPEN /* Dynamically load and unload the PAM library */ -int dlopen_pam (const char *so); -void dlclose_pam (void); +int dlopen_pam(const char *so); + +void dlclose_pam(void); + #endif diff --git a/src/plugins/auth-pam/utils.c b/src/plugins/auth-pam/utils.c index 4f2bec1..4f8fb0a 100644 --- a/src/plugins/auth-pam/utils.c +++ b/src/plugins/auth-pam/utils.c @@ -5,7 +5,7 @@ * packet encryption, packet authentication, and * packet compression. * - * Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net> + * Copyright (C) 2002-2017 OpenVPN Technologies, Inc. <sales@openvpn.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 @@ -43,71 +43,81 @@ char * searchandreplace(const char *tosearch, const char *searchfor, const char *replacewith) { - if (!tosearch || !searchfor || !replacewith) return NULL; + if (!tosearch || !searchfor || !replacewith) + { + return NULL; + } - size_t tosearchlen = strlen(tosearch); - size_t replacewithlen = strlen(replacewith); - size_t templen = tosearchlen * replacewithlen; + size_t tosearchlen = strlen(tosearch); + size_t replacewithlen = strlen(replacewith); + size_t templen = tosearchlen * replacewithlen; - if (tosearchlen == 0 || strlen(searchfor) == 0 || replacewithlen == 0) { - return NULL; - } + if (tosearchlen == 0 || strlen(searchfor) == 0 || replacewithlen == 0) + { + return NULL; + } - bool is_potential_integer_overflow = (templen == SIZE_MAX) || (templen / tosearchlen != replacewithlen); + bool is_potential_integer_overflow = (templen == SIZE_MAX) || (templen / tosearchlen != replacewithlen); - if (is_potential_integer_overflow) { - return NULL; - } + if (is_potential_integer_overflow) + { + return NULL; + } - // state: all parameters are valid + /* state: all parameters are valid */ - const char *searching=tosearch; - char *scratch; + const char *searching = tosearch; + char *scratch; - char temp[templen+1]; - temp[0]=0; + char temp[templen+1]; + temp[0] = 0; - scratch = strstr(searching,searchfor); - if (!scratch) return strdup(tosearch); + scratch = strstr(searching,searchfor); + if (!scratch) + { + return strdup(tosearch); + } - while (scratch) { - strncat(temp,searching,scratch-searching); - strcat(temp,replacewith); + while (scratch) { + strncat(temp,searching,scratch-searching); + strcat(temp,replacewith); - searching=scratch+strlen(searchfor); - scratch = strstr(searching,searchfor); - } - return strdup(temp); + searching = scratch+strlen(searchfor); + scratch = strstr(searching,searchfor); + } + return strdup(temp); } const char * -get_env (const char *name, const char *envp[]) +get_env(const char *name, const char *envp[]) { - if (envp) + if (envp) { - int i; - const int namelen = strlen (name); - for (i = 0; envp[i]; ++i) - { - if (!strncmp (envp[i], name, namelen)) - { - const char *cp = envp[i] + namelen; - if (*cp == '=') - return cp + 1; - } - } + int i; + const int namelen = strlen(name); + for (i = 0; envp[i]; ++i) + { + if (!strncmp(envp[i], name, namelen)) + { + const char *cp = envp[i] + namelen; + if (*cp == '=') + { + return cp + 1; + } + } + } } - return NULL; + return NULL; } int -string_array_len (const char *array[]) +string_array_len(const char *array[]) { - int i = 0; - if (array) + int i = 0; + if (array) { - while (array[i]) - ++i; + while (array[i]) + ++i; } - return i; + return i; } diff --git a/src/plugins/auth-pam/utils.h b/src/plugins/auth-pam/utils.h index 2f72040..fbc9705 100644 --- a/src/plugins/auth-pam/utils.h +++ b/src/plugins/auth-pam/utils.h @@ -5,7 +5,7 @@ * packet encryption, packet authentication, and * packet compression. * - * Copyright (C) 2002-2010 OpenVPN Technologies, Inc. <sales@openvpn.net> + * Copyright (C) 2002-2017 OpenVPN Technologies, Inc. <sales@openvpn.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 @@ -52,7 +52,7 @@ searchandreplace(const char *tosearch, const char *searchfor, const char *replac * @return Returns a pointer to the value of the enviroment variable if found, otherwise NULL is returned. */ const char * -get_env (const char *name, const char *envp[]); +get_env(const char *name, const char *envp[]); /** * Return the length of a string array @@ -61,6 +61,6 @@ get_env (const char *name, const char *envp[]); * */ int -string_array_len (const char *array[]); +string_array_len(const char *array[]); #endif |