📄 rpasswdd.c
字号:
/* Copyright (C) 2002, 2003, 2004, 2005 Thorsten Kukuk Author: Thorsten Kukuk <kukuk@suse.de> 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; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */#ifdef HAVE_CONFIG_H#include "config.h"#endif#define _GNU_SOURCE#include <getopt.h>#include <error.h>#include <errno.h>#include <grp.h>#include <pwd.h>#include <netdb.h>#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <arpa/inet.h>#include <sys/param.h>#include <sys/poll.h>#include <sys/socket.h>#include <sys/stat.h>#include <sys/wait.h>#include <sys/resource.h>#include <syslog.h>#include <signal.h>#include <string.h>#ifdef USE_GNUTLS#include <gnutls/gnutls.h>#else#include <openssl/rsa.h> /* SSLeay stuff */#include <openssl/crypto.h>#include <openssl/x509.h>#include <openssl/pem.h>#include <openssl/ssl.h>#include <openssl/err.h>#endif#include <security/pam_appl.h>#include <security/pam_misc.h>#include "i18n.h"#include "dbg_log.h"#include "use_slp.h"#include "logindefs.h"#include "error_codes.h"#include "rpasswd-client.h"#if !defined(IPV6_V6ONLY) && defined(__linux__)#define IPV6_V6ONLY 26#endif/* Path of the file where the PID of the running system is stored. */#define _PATH_RPASSWDDPID "/var/run/rpasswdd.pid"extern int setresuid(uid_t ruid, uid_t euid, uid_t suid);/* XXX This variable should not be global. */#ifdef USE_GNUTLS/* XXX */#elsestatic SSL_CTX *ctx;#endif/* Socket for incoming connections. */static struct pollfd pollfd_conn[10];static int pollfd_cnt = 0;#ifdef USE_SLP/* register/deregister at SLP server. */static int use_slp = 0;#endif/* Print the version information. */static voidprint_version (const char *program){ fprintf (stdout, "%s (%s) %s\n", program, PACKAGE, VERSION); fprintf (stdout, gettext ("\Copyright (C) %s Thorsten Kukuk.\n\This is free software; see the source for copying conditions. There is NO\n\warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\"), "2005"); /* fprintf (stdout, _("Written by %s.\n"), "Thorsten Kukuk"); */}static voidprint_usage (FILE *stream, const char *program){#ifdef USE_SLP fprintf (stream, _("Usage: %s [-4] [-6] [-d] [-c certificate] [-k privatekey] [-p port]\n [--slp [--slp-timeout timeout] [--slp-descr description]]\n"), program);#else fprintf (stream, _("Usage: %s [-4] [-6] [-d] [-c certificate] [-k privatekey] [-p port]\n"), program);#endif}static voidprint_help (const char *program){ print_usage (stdout, program); fprintf (stdout, _("%s - change password information\n\n"), program); fputs (_(" -4 Use IPv4\n"), stdout); fputs (_(" -6 Use IPv6\n"), stdout); fputs (_(" -c certificate Specify alternate certificate file\n"), stdout); fputs (_(" -k privatekey Specify alternate file with private key\n"), stdout); fputs (_(" -d Run in debug mode\n"), stdout); fputs (_(" -p port Port on which the server should listen\n"), stdout);#ifdef USE_SLP fputs (_(" --slp Register at local SLP server\n"), stdout); fputs (_(" --slp-timeout Set timeout for re-registration\n"), stdout); fputs (_(" --slp-descr Set a description shown to SLP clients\n"), stdout);#endif fputs (_(" --help Give this help list\n"), stdout); fputs (_(" --usage Give a short usage message\n"), stdout); fputs (_(" --version Print program version\n"), stdout);}static voidprint_error (const char *program){ fprintf (stderr, _("Try `%s --help' or `%s --usage' for more information.\n"), program, program);}/* Returns 1 if the process in pid file FILE is running, 0 if not. */static intcheck_pid (const char *file){ FILE *fp; fp = fopen (file, "r"); if (fp) { pid_t pid; int n; n = fscanf (fp, "%d", &pid); fclose (fp); if (n != 1 || kill (pid, 0) == 0) return 1; } return 0;}/* Write the current process id to the pid file. Returns 0 if successful, -1 if not. */static intwrite_pid (const char *file){ FILE *fp; fp = fopen (file, "w"); if (fp == NULL) return -1; fprintf (fp, "%d\n", getpid ()); if (fflush (fp) || ferror (fp)) return -1; fclose (fp); return 0;}/**************************************************************************************************************************************************** ****** Here is now the connection handling part ****** ****************************************************************************************************************************************************//* Initialize database information structures. */static voidserver_init (int port, int ipv4, int ipv6){ int have_usagi = 1; /* Assume we have a USAGI patched kernel. */ /* The Linux kernel without USAGI patch will handle IPv4 connections over an existing IPv6 binding. So we cannot bind explicit a IPv6 and a IPv4 socket. We use only a IPv6 socket in this case. */ if (ipv6) { struct sockaddr_in6 sock_addr; int on = 1; /* Create the socket. */ pollfd_conn[pollfd_cnt].fd = socket (AF_INET6, SOCK_STREAM, 0); if (pollfd_conn[pollfd_cnt].fd < 0) { dbg_log (_("cannot open socket: %s"), strerror (errno)); exit (1); }#ifdef IPV6_V6ONLY /* we try to bind to IPv6 only and to bind a second socket for IPv4. If the IPV6_V6ONLY option failed, we assume IPv6 will also handle IPv4 addresses. */ if (setsockopt (pollfd_conn[pollfd_cnt].fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&on, sizeof (on)) <0) { if (errno != ENOPROTOOPT) dbg_log ("setsockopt (IPPROTO_IPV6, IPV6_V6ONLY): %m"); have_usagi = 0; }#endif memset (&sock_addr, '\0', sizeof (sock_addr)); sock_addr.sin6_family = AF_INET6; sock_addr.sin6_addr = in6addr_any; sock_addr.sin6_port = port; if (bind (pollfd_conn[pollfd_cnt].fd, (struct sockaddr *) &sock_addr, sizeof (sock_addr)) < 0) { dbg_log ("bind: %s", strerror (errno)); exit (1); } /* Set the socket up to accept connections. */ if (listen (pollfd_conn[pollfd_cnt].fd, SOMAXCONN) < 0) { dbg_log (_("cannot enable socket to accept connections: %s"), strerror (errno)); exit (1); } ++pollfd_cnt; } if (ipv4 && (!ipv6 || have_usagi)) { struct sockaddr_in sock_addr; /* Create the socket. */ pollfd_conn[pollfd_cnt].fd = socket (AF_INET, SOCK_STREAM, 0); if (pollfd_conn[pollfd_cnt].fd < 0) { dbg_log (_("cannot open socket: %s"), strerror (errno)); exit (1); } memset (&sock_addr, '\0', sizeof (sock_addr)); sock_addr.sin_family = AF_INET; sock_addr.sin_addr.s_addr = INADDR_ANY; sock_addr.sin_port = port; if (bind (pollfd_conn[pollfd_cnt].fd, (struct sockaddr *) &sock_addr, sizeof (sock_addr)) < 0) { dbg_log ("bind: %s", strerror (errno)); exit (1); } /* Set the socket up to accept connections. */ if (listen (pollfd_conn[pollfd_cnt].fd, SOMAXCONN) < 0) { dbg_log (_("cannot enable socket to accept connections: %s"), strerror (errno)); exit (1); } ++pollfd_cnt; }}/* Close the connections. */static voidclose_sockets (void){ int i; for (i = 0; i < pollfd_cnt; i++) if (pollfd_conn[i].fd >= 0) close (pollfd_conn[i].fd);}#ifdef USE_GNUTLSstatic intsend_string (gnutls_session ssl, response_type type, const char *str){ response_header resp; resp.type = type; resp.data_len = strlen (str) + 1; if (gnutls_record_send (ssl, &resp, sizeof (resp)) <= 0) return -1; if (gnutls_record_send (ssl, str, resp.data_len) <= 0) return -1; return 0;}static ssize_tsafe_read (gnutls_session ssl, void *data, size_t count, int timeout){ struct pollfd conn; void *sock; sock = gnutls_transport_get_ptr (ssl); conn.fd = (long) sock; conn.events = POLLRDNORM; errno = 0; while (1) { int nr = poll (&conn, 1, timeout * 1000); if (nr < 0) { /* Don't print error messages if poll is only interupted by a signal. */ if (errno != EINTR) dbg_log ("poll() failed: %s", strerror (errno)); continue; } else if (nr == 0) { /* XXX TIMEOUT */ errno = ETIME; return -1; } else break; } /* We have new incoming data. */ if (conn.revents & (POLLRDNORM|POLLERR|POLLHUP|POLLNVAL)) { if (gnutls_record_recv (ssl, data, count) <= 0) return -1; else return count; } else { /* I don't know if this can ever happen. */ errno = EAGAIN; return -1; }}#elsestatic intsend_string (SSL *ssl, response_type type, const char *str){ response_header resp; resp.type = type; resp.data_len = strlen (str) + 1; if (TEMP_FAILURE_RETRY(SSL_write (ssl, &resp, sizeof (resp))) != sizeof (resp)) return -1; if (TEMP_FAILURE_RETRY(SSL_write (ssl, str, resp.data_len)) != resp.data_len) return -1; return 0;}static ssize_tsafe_read (SSL *ssl, void *data, size_t count, int timeout){ struct pollfd conn; conn.fd = SSL_get_fd (ssl); conn.events = POLLRDNORM; errno = 0; while (1) { int nr = poll (&conn, 1, timeout * 1000); if (nr < 0) { /* Don't print error messages if poll is only interupted by a signal. */ if (errno != EINTR) dbg_log ("poll() failed: %s", strerror (errno)); continue; } else if (nr == 0) { /* XXX TIMEOUT */ errno = ETIME; return -1; } else break; } /* We have new incoming data. */ if (conn.revents & (POLLRDNORM|POLLERR|POLLHUP|POLLNVAL)) return TEMP_FAILURE_RETRY (SSL_read (ssl, data, count)); else { /* I don't know if this can ever happen. */ errno = EAGAIN; return -1; }}#endifstatic int#ifdef USE_GNUTLSread_string (gnutls_session ssl, char **retstr)#elseread_string (SSL *ssl, char **retstr)#endif{ conv_header resp; *retstr = NULL; errno = 0; if (safe_read (ssl, &resp, sizeof (resp), 120) != sizeof (resp)) { char err_buf[256]; if (errno == 0) dbg_log (_("error while reading request: %s"), _("wrong data received")); else dbg_log (_("error while reading request: %s"), strerror_r (errno, err_buf, sizeof (err_buf))); return PAM_CONV_ERR; } /* 1024 bytes data should be enough. Don't allow more to avoid DOS attacks. */ if (resp.data_len > 0 && resp.data_len <= 1024) { char *buf = alloca (resp.data_len + 1); if (safe_read (ssl, buf, resp.data_len, 120) != resp.data_len) { char err_buf[256]; if (errno == 0) dbg_log (_("error while reading request data: %s"), _("wrong data received")); else dbg_log (_("error while reading request data: %s"), strerror_r (errno, err_buf, sizeof (err_buf))); return PAM_CONV_ERR; } buf[resp.data_len] = '\0'; *retstr = strdup (buf); } else return PAM_CONV_ERR; return resp.retval;}static intrpasswd_conv(int num_msg, const struct pam_message **msgm,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -