📄 auth-pam.c
字号:
/* * 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. * * Copyright (C) 2002-2005 OpenVPN Solutions LLC <info@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 * 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. * * You should have received a copy of the GNU General Public License * along with this program (see the file COPYING included with this * distribution); if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *//* * OpenVPN plugin module to do PAM authentication using a split * privilege model. */#if DLOPEN_PAM#include <dlfcn.h>#include "pamdl.h"#else#include <security/pam_appl.h>#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>#include "openvpn-plugin.h"#define DEBUG(verb) ((verb) >= 7)/* 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/* * Plugin state, used by foreground */struct auth_pam_context{ /* Foreground's socket to background process */ int foreground_fd; /* Process ID of background process */ pid_t background_pid; /* Verbosity level of OpenVPN */ int verb;};/* * Name/Value pairs for conversation function. * Special Values: * * "USERNAME" -- substitute client-supplied username * "PASSWORD" -- substitute client-specified password */#define N_NAME_VALUE 16struct name_value { const char *name; const char *value;};struct name_value_list { int len; struct name_value data[N_NAME_VALUE];};/* * Used to pass the username/password * to the PAM conversation function. */struct user_pass { int verb; char username[128]; char password[128]; 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);/* * Given an environmental variable name, search * the envp array for its value, returning it * if found or NULL otherwise. */static const char *get_env (const char *name, const char *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; } } } return NULL;}/* * Return the length of a string array */static intstring_array_len (const char *array[]){ int i = 0; if (array) { while (array[i]) ++i; } return i;}/* * Socket read/write functions. */static intrecv_control (int fd){ unsigned char c; const ssize_t size = read (fd, &c, sizeof (c)); if (size == sizeof (c)) return c; else { /*fprintf (stderr, "AUTH-PAM: DEBUG recv_control.read=%d\n", (int)size);*/ return -1; }}static intsend_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;}static intrecv_string (int fd, char *buffer, int len){ 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; } return -1;}static intsend_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;}/* * Daemonize if "daemon" env var is true. * Preserve stderr across daemonization if * "daemon_log_redirect" env var is true. */static voiddaemonize (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); } }}/* * 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 voidclose_fds_except (int keep){ int i; closelog (); for (i = 3; i <= 100; ++i) { if (i != keep) close (i); }}/* * Usually we ignore signals, because our parent will * deal with them. */static voidset_signals (void){ signal (SIGTERM, SIG_DFL); 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 intname_value_match (const char *query, const char *match){ while (!isalnum (*query)) { if (*query == '\0') return 0; ++query; } return strncasecmp (match, query, strlen (match)) == 0;}OPENVPN_EXPORT openvpn_plugin_handle_topenvpn_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)); 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) { fprintf (stderr, "AUTH-PAM: need PAM service parameter\n"); 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) { 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]; } } /* * 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) { fprintf (stderr, "AUTH-PAM: socketpair call failed\n"); goto error; } /* * Fork off the privileged process. It will remain privileged * even after the foreground process drops its privileges. */ pid = fork (); if (pid) { int status; /* * Foreground Process */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -