src/plugins/auth-pam/auth-pam.c
6fbf66fa
 /*
  *  OpenVPN -- An application to securely tunnel IP networks
  *             over a single TCP/UDP port, with support for SSL/TLS-based
  *             session authentication and key exchange,
  *             packet encryption, packet authentication, and
  *             packet compression.
  *
49979459
  *  Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
7369d01b
  *  Copyright (C) 2016-2018 Selva Nair <selva.nair@gmail.com>
6fbf66fa
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License version 2
  *  as published by the Free Software Foundation.
  *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
  *
caa54ac3
  *  You should have received a copy of the GNU General Public License along
  *  with this program; if not, write to the Free Software Foundation, Inc.,
  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
6fbf66fa
  */
 
 /*
  * OpenVPN plugin module to do PAM authentication using a split
  * privilege model.
  */
ce8271f5
 #ifdef HAVE_CONFIG_H
 #include <config.h>
 #endif
6fbf66fa
 
 #include <security/pam_appl.h>
ce8271f5
 
 #ifdef USE_PAM_DLOPEN
 #include "pamdl.h"
6fbf66fa
 #endif
 
 #include <stdio.h>
 #include <string.h>
 #include <ctype.h>
 #include <unistd.h>
 #include <stdlib.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/wait.h>
 #include <fcntl.h>
 #include <signal.h>
 #include <syslog.h>
4507bb6c
 #include "utils.h"
6fbf66fa
 
ce8271f5
 #include <openvpn-plugin.h>
6fbf66fa
 
e1791bb1
 #define DEBUG(verb) ((verb) >= 4)
6fbf66fa
 
 /* Command codes for foreground -> background communication */
 #define COMMAND_VERIFY 0
 #define COMMAND_EXIT   1
 
 /* Response codes for background -> foreground communication */
 #define RESPONSE_INIT_SUCCEEDED   10
 #define RESPONSE_INIT_FAILED      11
 #define RESPONSE_VERIFY_SUCCEEDED 12
 #define RESPONSE_VERIFY_FAILED    13
 
f403b9a2
 /* Pointers to functions exported from openvpn */
 static plugin_secure_memzero_t plugin_secure_memzero = NULL;
7369d01b
 static plugin_base64_decode_t plugin_base64_decode = NULL;
f403b9a2
 
6fbf66fa
 /*
  * Plugin state, used by foreground
  */
 struct auth_pam_context
 {
81d882d5
     /* Foreground's socket to background process */
     int foreground_fd;
6fbf66fa
 
81d882d5
     /* Process ID of background process */
     pid_t background_pid;
6fbf66fa
 
81d882d5
     /* Verbosity level of OpenVPN */
     int verb;
6fbf66fa
 };
 
 /*
  * Name/Value pairs for conversation function.
  * Special Values:
  *
  *  "USERNAME" -- substitute client-supplied username
  *  "PASSWORD" -- substitute client-specified password
6cfada26
  *  "COMMONNAME" -- substitute client certificate common name
7369d01b
  *  "OTP" -- substitute static challenge response if available
6fbf66fa
  */
 
 #define N_NAME_VALUE 16
 
 struct name_value {
81d882d5
     const char *name;
     const char *value;
6fbf66fa
 };
 
 struct name_value_list {
81d882d5
     int len;
     struct name_value data[N_NAME_VALUE];
6fbf66fa
 };
 
 /*
  * Used to pass the username/password
  * to the PAM conversation function.
  */
 struct user_pass {
81d882d5
     int verb;
6fbf66fa
 
81d882d5
     char username[128];
     char password[128];
     char common_name[128];
7369d01b
     char response[128];
6fbf66fa
 
81d882d5
     const struct name_value_list *name_value_list;
6fbf66fa
 };
 
 /* Background process function */
81d882d5
 static void pam_server(int fd, const char *service, int verb, const struct name_value_list *name_value_list);
6fbf66fa
 
 
 /*
  * Socket read/write functions.
  */
 
 static int
81d882d5
 recv_control(int fd)
6fbf66fa
 {
81d882d5
     unsigned char c;
     const ssize_t size = read(fd, &c, sizeof(c));
     if (size == sizeof(c))
6fbf66fa
     {
81d882d5
         return c;
     }
     else
     {
         /*fprintf (stderr, "AUTH-PAM: DEBUG recv_control.read=%d\n", (int)size);*/
         return -1;
6fbf66fa
     }
 }
 
 static int
81d882d5
 send_control(int fd, int code)
6fbf66fa
 {
81d882d5
     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;
     }
6fbf66fa
 }
 
 static int
81d882d5
 recv_string(int fd, char *buffer, int len)
6fbf66fa
 {
81d882d5
     if (len > 0)
6fbf66fa
     {
81d882d5
         ssize_t size;
         memset(buffer, 0, len);
         size = read(fd, buffer, len);
         buffer[len-1] = 0;
         if (size >= 1)
         {
             return (int)size;
         }
6fbf66fa
     }
81d882d5
     return -1;
6fbf66fa
 }
 
 static int
81d882d5
 send_string(int fd, const char *string)
6fbf66fa
 {
81d882d5
     const int len = strlen(string) + 1;
     const ssize_t size = write(fd, string, len);
     if (size == len)
     {
         return (int) size;
     }
     else
     {
         return -1;
     }
6fbf66fa
 }
 
e1791bb1
 #ifdef DO_DAEMONIZE
 
6fbf66fa
 /*
  * Daemonize if "daemon" env var is true.
  * Preserve stderr across daemonization if
  * "daemon_log_redirect" env var is true.
  */
 static void
81d882d5
 daemonize(const char *envp[])
6fbf66fa
 {
81d882d5
     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);
         }
6fbf66fa
     }
 }
 
81d882d5
 #endif /* ifdef DO_DAEMONIZE */
e1791bb1
 
6fbf66fa
 /*
  * Close most of parent's fds.
  * Keep stdin/stdout/stderr, plus one
  * other fd which is presumed to be
  * our pipe back to parent.
  * Admittedly, a bit of a kludge,
  * but posix doesn't give us a kind
  * of FD_CLOEXEC which will stop
  * fds from crossing a fork().
  */
 static void
81d882d5
 close_fds_except(int keep)
6fbf66fa
 {
81d882d5
     int i;
     closelog();
     for (i = 3; i <= 100; ++i)
6fbf66fa
     {
81d882d5
         if (i != keep)
         {
             close(i);
         }
6fbf66fa
     }
 }
 
 /*
  * Usually we ignore signals, because our parent will
  * deal with them.
  */
 static void
81d882d5
 set_signals(void)
6fbf66fa
 {
81d882d5
     signal(SIGTERM, SIG_DFL);
6fbf66fa
 
81d882d5
     signal(SIGINT, SIG_IGN);
     signal(SIGHUP, SIG_IGN);
     signal(SIGUSR1, SIG_IGN);
     signal(SIGUSR2, SIG_IGN);
     signal(SIGPIPE, SIG_IGN);
6fbf66fa
 }
 
 /*
  * Return 1 if query matches match.
  */
 static int
81d882d5
 name_value_match(const char *query, const char *match)
6fbf66fa
 {
81d882d5
     while (!isalnum(*query))
6fbf66fa
     {
81d882d5
         if (*query == '\0')
         {
             return 0;
         }
         ++query;
6fbf66fa
     }
81d882d5
     return strncasecmp(match, query, strlen(match)) == 0;
6fbf66fa
 }
 
7369d01b
 /*
  * Split and decode up->password in the form SCRV1:base64_pass:base64_response
  * into pass and response and save in up->password and up->response.
  * If the password is not in the expected format, input is not changed.
  */
 static void
 split_scrv1_password(struct user_pass *up)
 {
     const int skip = strlen("SCRV1:");
     if (strncmp(up->password, "SCRV1:", skip) != 0)
     {
         return;
     }
 
     char *tmp = strdup(up->password);
     if (!tmp)
     {
         fprintf(stderr, "AUTH-PAM: out of memory parsing static challenge password\n");
         goto out;
     }
 
     char *pass = tmp + skip;
     char *resp = strchr(pass, ':');
     if (!resp) /* string not in SCRV1:xx:yy format */
     {
         goto out;
     }
     *resp++ = '\0';
 
     int n = plugin_base64_decode(pass, up->password, sizeof(up->password)-1);
     if (n > 0)
     {
         up->password[n] = '\0';
         n = plugin_base64_decode(resp, up->response, sizeof(up->response)-1);
         if (n > 0)
         {
             up->response[n] = '\0';
             if (DEBUG(up->verb))
             {
                 fprintf(stderr, "AUTH-PAM: BACKGROUND: parsed static challenge password\n");
             }
             goto out;
         }
     }
 
     /* decode error: reinstate original value of up->password and return */
     plugin_secure_memzero(up->password, sizeof(up->password));
     plugin_secure_memzero(up->response, sizeof(up->response));
     strcpy(up->password, tmp); /* tmp is guaranteed to fit in up->password */
 
     fprintf(stderr, "AUTH-PAM: base64 decode error while parsing static challenge password\n");
 
 out:
     if (tmp)
     {
         plugin_secure_memzero(tmp, strlen(tmp));
         free(tmp);
     }
 }
 
f403b9a2
 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)
6fbf66fa
 {
81d882d5
     pid_t pid;
     int fd[2];
 
     struct auth_pam_context *context;
     struct name_value_list name_value_list;
6fbf66fa
 
81d882d5
     const int base_parms = 2;
 
f403b9a2
     const char **argv = args->argv;
     const char **envp = args->envp;
 
     /* Check API compatibility -- struct version 4 or higher needed */
     if (v3structver < 4)
     {
         fprintf(stderr, "AUTH-PAM: This plugin is incompatible with the running version of OpenVPN\n");
         return OPENVPN_PLUGIN_FUNC_ERROR;
     }
 
81d882d5
     /*
      * Allocate our context
      */
     context = (struct auth_pam_context *) calloc(1, sizeof(struct auth_pam_context));
     if (!context)
6fbf66fa
     {
81d882d5
         goto error;
6fbf66fa
     }
81d882d5
     context->foreground_fd = -1;
 
     /*
      * Intercept the --auth-user-pass-verify callback.
      */
f403b9a2
     ret->type_mask = OPENVPN_PLUGIN_MASK(OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY);
 
     /* Save global pointers to functions exported from openvpn */
     plugin_secure_memzero = args->callbacks->plugin_secure_memzero;
7369d01b
     plugin_base64_decode = args->callbacks->plugin_base64_decode;
81d882d5
 
     /*
      * 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)
6fbf66fa
     {
81d882d5
         fprintf(stderr, "AUTH-PAM: need PAM service parameter\n");
         goto error;
6fbf66fa
     }
 
81d882d5
     /*
      * 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)
6fbf66fa
     {
81d882d5
         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];
         }
6fbf66fa
     }
 
81d882d5
     /*
      * Get verbosity level from environment
      */
6fbf66fa
     {
81d882d5
         const char *verb_string = get_env("verb", envp);
         if (verb_string)
         {
             context->verb = atoi(verb_string);
         }
     }
6fbf66fa
 
81d882d5
     /*
      * 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;
     }
6fbf66fa
 
81d882d5
     /*
      * Fork off the privileged process.  It will remain privileged
      * even after the foreground process drops its privileges.
      */
     pid = fork();
6fbf66fa
 
81d882d5
     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];
f403b9a2
             ret->handle = (openvpn_plugin_handle_t *) context;
             return OPENVPN_PLUGIN_FUNC_SUCCESS;
81d882d5
         }
6fbf66fa
     }
81d882d5
     else
6fbf66fa
     {
81d882d5
         /*
          * Background Process
          */
6fbf66fa
 
81d882d5
         /* close all parent fds except our socket back to parent */
         close_fds_except(fd[1]);
6fbf66fa
 
81d882d5
         /* Ignore most signals (the parent will receive them) */
         set_signals();
6fbf66fa
 
e1791bb1
 #ifdef DO_DAEMONIZE
81d882d5
         /* Daemonize if --daemon option is set. */
         daemonize(envp);
e1791bb1
 #endif
6fbf66fa
 
81d882d5
         /* execute the event loop */
         pam_server(fd[1], argv[1], context->verb, &name_value_list);
6fbf66fa
 
81d882d5
         close(fd[1]);
6fbf66fa
 
81d882d5
         exit(0);
         return 0; /* NOTREACHED */
6fbf66fa
     }
 
81d882d5
 error:
     if (context)
     {
         free(context);
     }
f403b9a2
     return OPENVPN_PLUGIN_FUNC_ERROR;
6fbf66fa
 }
 
 OPENVPN_EXPORT int
81d882d5
 openvpn_plugin_func_v1(openvpn_plugin_handle_t handle, const int type, const char *argv[], const char *envp[])
6fbf66fa
 {
81d882d5
     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;
6fbf66fa
 }
 
 OPENVPN_EXPORT void
81d882d5
 openvpn_plugin_close_v1(openvpn_plugin_handle_t handle)
6fbf66fa
 {
81d882d5
     struct auth_pam_context *context = (struct auth_pam_context *) handle;
6fbf66fa
 
81d882d5
     if (DEBUG(context->verb))
6fbf66fa
     {
81d882d5
         fprintf(stderr, "AUTH-PAM: close\n");
     }
6fbf66fa
 
81d882d5
     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;
6fbf66fa
     }
 
81d882d5
     free(context);
6fbf66fa
 }
 
 OPENVPN_EXPORT void
81d882d5
 openvpn_plugin_abort_v1(openvpn_plugin_handle_t handle)
6fbf66fa
 {
81d882d5
     struct auth_pam_context *context = (struct auth_pam_context *) handle;
6fbf66fa
 
81d882d5
     /* tell background process to exit */
     if (context && context->foreground_fd >= 0)
6fbf66fa
     {
81d882d5
         send_control(context->foreground_fd, COMMAND_EXIT);
         close(context->foreground_fd);
         context->foreground_fd = -1;
6fbf66fa
     }
 }
 
 /*
  * PAM conversation function
  */
 static int
81d882d5
 my_conv(int n, const struct pam_message **msg_array,
         struct pam_response **response_array, void *appdata_ptr)
6fbf66fa
 {
81d882d5
     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);
                     }
7369d01b
                     else if (strstr(match_value, "OTP"))
                     {
                         aresp[i].resp = searchandreplace(match_value, "OTP", up->response);
                     }
81d882d5
                     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;
6fbf66fa
 }
 
 /*
  * Return 1 if authenticated and 0 if failed.
  * Called once for every username/password
  * to be authenticated.
  */
 static int
81d882d5
 pam_auth(const char *service, const struct user_pass *up)
6fbf66fa
 {
81d882d5
     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;
6fbf66fa
 }
 
 /*
  * Background process -- runs with privilege.
  */
 static void
81d882d5
 pam_server(int fd, const char *service, int verb, const struct name_value_list *name_value_list)
6fbf66fa
 {
81d882d5
     struct user_pass up;
     int command;
ce8271f5
 #ifdef USE_PAM_DLOPEN
81d882d5
     static const char pam_so[] = "libpam.so";
6fbf66fa
 #endif
 
81d882d5
     /*
      * Do initialization
      */
     if (DEBUG(verb))
     {
         fprintf(stderr, "AUTH-PAM: BACKGROUND: INIT service='%s'\n", service);
     }
6fbf66fa
 
ce8271f5
 #ifdef USE_PAM_DLOPEN
81d882d5
     /*
      * Load PAM shared object
      */
     if (!dlopen_pam(pam_so))
6fbf66fa
     {
81d882d5
         fprintf(stderr, "AUTH-PAM: BACKGROUND: could not load PAM lib %s: %s\n", pam_so, dlerror());
         send_control(fd, RESPONSE_INIT_FAILED);
         goto done;
6fbf66fa
     }
 #endif
 
81d882d5
     /*
      * Tell foreground that we initialized successfully
      */
     if (send_control(fd, RESPONSE_INIT_SUCCEEDED) == -1)
6fbf66fa
     {
81d882d5
         fprintf(stderr, "AUTH-PAM: BACKGROUND: write error on response socket [1]\n");
         goto done;
6fbf66fa
     }
 
81d882d5
     /*
      * Event loop
      */
     while (1)
6fbf66fa
     {
81d882d5
         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))
                 {
1e0b7141
 #if 0
81d882d5
                     fprintf(stderr, "AUTH-PAM: BACKGROUND: USER/PASS: %s/%s\n",
                             up.username, up.password);
1e0b7141
 #else
81d882d5
                     fprintf(stderr, "AUTH-PAM: BACKGROUND: USER: %s\n", up.username);
1e0b7141
 #endif
81d882d5
                 }
 
7369d01b
                 /* If password is of the form SCRV1:base64:base64 split it up */
                 split_scrv1_password(&up);
 
81d882d5
                 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;
                     }
                 }
f403b9a2
                 plugin_secure_memzero(up.password, sizeof(up.password));
81d882d5
                 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;
         }
7369d01b
         plugin_secure_memzero(up.response, sizeof(up.response));
81d882d5
     }
 done:
f403b9a2
     plugin_secure_memzero(up.password, sizeof(up.password));
7369d01b
     plugin_secure_memzero(up.response, sizeof(up.response));
ce8271f5
 #ifdef USE_PAM_DLOPEN
81d882d5
     dlclose_pam();
6fbf66fa
 #endif
81d882d5
     if (DEBUG(verb))
     {
         fprintf(stderr, "AUTH-PAM: BACKGROUND: EXIT\n");
     }
6fbf66fa
 
81d882d5
     return;
6fbf66fa
 }