⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 auth-pam.c

📁 OpenSSH 是 SSH (Secure SHell) 协议的免费开源实现。它用安全、加密的网络连接工具代替了 telnet、ftp、 rlogin、rsh 和 rcp 工具。OpenSSH 支持
💻 C
📖 第 1 页 / 共 2 页
字号:
/*- * Copyright (c) 2002 Networks Associates Technology, Inc. * All rights reserved. * * This software was developed for the FreeBSD Project by ThinkSec AS and * NAI Labs, the Security Research Division of Network Associates, Inc. * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the * DARPA CHATS research program. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright *    notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright *    notice, this list of conditions and the following disclaimer in the *    documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. *//* * Copyright (c) 2003,2004 Damien Miller <djm@mindrot.org> * Copyright (c) 2003,2004 Darren Tucker <dtucker@zip.com.au> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. *//* Based on $FreeBSD: src/crypto/openssh/auth2-pam-freebsd.c,v 1.11 2003/03/31 13:48:18 des Exp $ */#include "includes.h"RCSID("$Id: auth-pam.c,v 1.121 2005/01/20 02:29:51 dtucker Exp $");#ifdef USE_PAM#if defined(HAVE_SECURITY_PAM_APPL_H)#include <security/pam_appl.h>#elif defined (HAVE_PAM_PAM_APPL_H)#include <pam/pam_appl.h>#endif#include "auth.h"#include "auth-pam.h"#include "buffer.h"#include "bufaux.h"#include "canohost.h"#include "log.h"#include "monitor_wrap.h"#include "msg.h"#include "packet.h"#include "misc.h"#include "servconf.h"#include "ssh2.h"#include "xmalloc.h"#include "auth-options.h"extern ServerOptions options;extern Buffer loginmsg;extern int compat20;extern u_int utmp_len;#ifdef USE_POSIX_THREADS#include <pthread.h>/* * Avoid namespace clash when *not* using pthreads for systems *with* * pthreads, which unconditionally define pthread_t via sys/types.h * (e.g. Linux) */typedef pthread_t sp_pthread_t;#elsetypedef pid_t sp_pthread_t;#endifstruct pam_ctxt {	sp_pthread_t	 pam_thread;	int		 pam_psock;	int		 pam_csock;	int		 pam_done;};static void sshpam_free_ctx(void *);static struct pam_ctxt *cleanup_ctxt;#ifndef USE_POSIX_THREADS/* * Simulate threads with processes. */static int sshpam_thread_status = -1;static mysig_t sshpam_oldsig;static void sshpam_sigchld_handler(int sig){	signal(SIGCHLD, SIG_DFL);	if (cleanup_ctxt == NULL)		return;	/* handler called after PAM cleanup, shouldn't happen */	if (waitpid(cleanup_ctxt->pam_thread, &sshpam_thread_status, WNOHANG)	     <= 0) {		/* PAM thread has not exitted, privsep slave must have */		kill(cleanup_ctxt->pam_thread, SIGTERM);		if (waitpid(cleanup_ctxt->pam_thread, &sshpam_thread_status, 0)		    <= 0)			return; /* could not wait */	}	if (WIFSIGNALED(sshpam_thread_status) &&	    WTERMSIG(sshpam_thread_status) == SIGTERM)		return;	/* terminated by pthread_cancel */	if (!WIFEXITED(sshpam_thread_status))		fatal("PAM: authentication thread exited unexpectedly");	if (WEXITSTATUS(sshpam_thread_status) != 0)		fatal("PAM: authentication thread exited uncleanly");}static voidpthread_exit(void *value __unused){	_exit(0);}static intpthread_create(sp_pthread_t *thread, const void *attr __unused,    void *(*thread_start)(void *), void *arg){	pid_t pid;	sshpam_thread_status = -1;	switch ((pid = fork())) {	case -1:		error("fork(): %s", strerror(errno));		return (-1);	case 0:		thread_start(arg);		_exit(1);	default:		*thread = pid;		sshpam_oldsig = signal(SIGCHLD, sshpam_sigchld_handler);		return (0);	}}static intpthread_cancel(sp_pthread_t thread){	signal(SIGCHLD, sshpam_oldsig);	return (kill(thread, SIGTERM));}static intpthread_join(sp_pthread_t thread, void **value __unused){	int status;	if (sshpam_thread_status != -1)		return (sshpam_thread_status);	signal(SIGCHLD, sshpam_oldsig);	waitpid(thread, &status, 0);	return (status);}#endifstatic pam_handle_t *sshpam_handle = NULL;static int sshpam_err = 0;static int sshpam_authenticated = 0;static int sshpam_session_open = 0;static int sshpam_cred_established = 0;static int sshpam_account_status = -1;static char **sshpam_env = NULL;static Authctxt *sshpam_authctxt = NULL;static const char *sshpam_password = NULL;static char badpw[] = "\b\n\r\177INCORRECT";/* Some PAM implementations don't implement this */#ifndef HAVE_PAM_GETENVLISTstatic char **pam_getenvlist(pam_handle_t *pamh){	/*	 * XXX - If necessary, we can still support envrionment passing	 * for platforms without pam_getenvlist by searching for known	 * env vars (e.g. KRB5CCNAME) from the PAM environment.	 */	 return NULL;}#endif/* * Some platforms, notably Solaris, do not enforce password complexity * rules during pam_chauthtok() if the real uid of the calling process * is 0, on the assumption that it's being called by "passwd" run by root. * This wraps pam_chauthtok and sets/restore the real uid so PAM will do * the right thing. */#ifdef SSHPAM_CHAUTHTOK_NEEDS_RUIDstatic intsshpam_chauthtok_ruid(pam_handle_t *pamh, int flags){	int result;	if (sshpam_authctxt == NULL)		fatal("PAM: sshpam_authctxt not initialized");	if (setreuid(sshpam_authctxt->pw->pw_uid, -1) == -1)		fatal("%s: setreuid failed: %s", __func__, strerror(errno));	result = pam_chauthtok(pamh, flags);	if (setreuid(0, -1) == -1)		fatal("%s: setreuid failed: %s", __func__, strerror(errno));	return result;}# define pam_chauthtok(a,b)	(sshpam_chauthtok_ruid((a), (b)))#endifvoidsshpam_password_change_required(int reqd){	debug3("%s %d", __func__, reqd);	if (sshpam_authctxt == NULL)		fatal("%s: PAM authctxt not initialized", __func__);	sshpam_authctxt->force_pwchange = reqd;	if (reqd) {		no_port_forwarding_flag |= 2;		no_agent_forwarding_flag |= 2;		no_x11_forwarding_flag |= 2;	} else {		no_port_forwarding_flag &= ~2;		no_agent_forwarding_flag &= ~2;		no_x11_forwarding_flag &= ~2;	}}/* Import regular and PAM environment from subprocess */static voidimport_environments(Buffer *b){	char *env;	u_int i, num_env;	int err;	debug3("PAM: %s entering", __func__);#ifndef USE_POSIX_THREADS	/* Import variables set by do_pam_account */	sshpam_account_status = buffer_get_int(b);	sshpam_password_change_required(buffer_get_int(b));	/* Import environment from subprocess */	num_env = buffer_get_int(b);	sshpam_env = xmalloc((num_env + 1) * sizeof(*sshpam_env));	debug3("PAM: num env strings %d", num_env);	for(i = 0; i < num_env; i++)		sshpam_env[i] = buffer_get_string(b, NULL);	sshpam_env[num_env] = NULL;	/* Import PAM environment from subprocess */	num_env = buffer_get_int(b);	debug("PAM: num PAM env strings %d", num_env);	for(i = 0; i < num_env; i++) {		env = buffer_get_string(b, NULL);#ifdef HAVE_PAM_PUTENV		/* Errors are not fatal here */		if ((err = pam_putenv(sshpam_handle, env)) != PAM_SUCCESS) {			error("PAM: pam_putenv: %s",			    pam_strerror(sshpam_handle, sshpam_err));		}#endif	}#endif}/* * Conversation function for authentication thread. */static intsshpam_thread_conv(int n, struct pam_message **msg,    struct pam_response **resp, void *data){	Buffer buffer;	struct pam_ctxt *ctxt;	struct pam_response *reply;	int i;	debug3("PAM: %s entering, %d messages", __func__, n);	*resp = NULL;	if (data == NULL) {		error("PAM: conversation function passed a null context");		return (PAM_CONV_ERR);	}	ctxt = data;	if (n <= 0 || n > PAM_MAX_NUM_MSG)		return (PAM_CONV_ERR);	if ((reply = malloc(n * sizeof(*reply))) == NULL)		return (PAM_CONV_ERR);	memset(reply, 0, n * sizeof(*reply));	buffer_init(&buffer);	for (i = 0; i < n; ++i) {		switch (PAM_MSG_MEMBER(msg, i, msg_style)) {		case PAM_PROMPT_ECHO_OFF:			buffer_put_cstring(&buffer,			    PAM_MSG_MEMBER(msg, i, msg));			if (ssh_msg_send(ctxt->pam_csock,			    PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)				goto fail;			if (ssh_msg_recv(ctxt->pam_csock, &buffer) == -1)				goto fail;			if (buffer_get_char(&buffer) != PAM_AUTHTOK)				goto fail;			reply[i].resp = buffer_get_string(&buffer, NULL);			break;		case PAM_PROMPT_ECHO_ON:			buffer_put_cstring(&buffer,			    PAM_MSG_MEMBER(msg, i, msg));			if (ssh_msg_send(ctxt->pam_csock,			    PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)				goto fail;			if (ssh_msg_recv(ctxt->pam_csock, &buffer) == -1)				goto fail;			if (buffer_get_char(&buffer) != PAM_AUTHTOK)				goto fail;			reply[i].resp = buffer_get_string(&buffer, NULL);			break;		case PAM_ERROR_MSG:			buffer_put_cstring(&buffer,			    PAM_MSG_MEMBER(msg, i, msg));			if (ssh_msg_send(ctxt->pam_csock,			    PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)				goto fail;			break;		case PAM_TEXT_INFO:			buffer_put_cstring(&buffer,			    PAM_MSG_MEMBER(msg, i, msg));			if (ssh_msg_send(ctxt->pam_csock,			    PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1)				goto fail;			break;		default:			goto fail;		}		buffer_clear(&buffer);	}	buffer_free(&buffer);	*resp = reply;	return (PAM_SUCCESS); fail:	for(i = 0; i < n; i++) {		if (reply[i].resp != NULL)			xfree(reply[i].resp);	}	xfree(reply);	buffer_free(&buffer);	return (PAM_CONV_ERR);}/* * Authentication thread. */static void *sshpam_thread(void *ctxtp){	struct pam_ctxt *ctxt = ctxtp;	Buffer buffer;	struct pam_conv sshpam_conv;	int flags = (options.permit_empty_passwd == 0 ?	    PAM_DISALLOW_NULL_AUTHTOK : 0);#ifndef USE_POSIX_THREADS	extern char **environ;	char **env_from_pam;	u_int i;	const char *pam_user;	pam_get_item(sshpam_handle, PAM_USER, (void **)&pam_user);	environ[0] = NULL;	if (sshpam_authctxt != NULL) {		setproctitle("%s [pam]",		    sshpam_authctxt->valid ? pam_user : "unknown");	}#endif	sshpam_conv.conv = sshpam_thread_conv;	sshpam_conv.appdata_ptr = ctxt;	if (sshpam_authctxt == NULL)		fatal("%s: PAM authctxt not initialized", __func__);	buffer_init(&buffer);	sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,	    (const void *)&sshpam_conv);	if (sshpam_err != PAM_SUCCESS)		goto auth_fail;	sshpam_err = pam_authenticate(sshpam_handle, flags);	if (sshpam_err != PAM_SUCCESS)		goto auth_fail;	if (compat20) {		if (!do_pam_account())			goto auth_fail;		if (sshpam_authctxt->force_pwchange) {			sshpam_err = pam_chauthtok(sshpam_handle,			    PAM_CHANGE_EXPIRED_AUTHTOK);			if (sshpam_err != PAM_SUCCESS)				goto auth_fail;			sshpam_password_change_required(0);		}	}	buffer_put_cstring(&buffer, "OK");#ifndef USE_POSIX_THREADS	/* Export variables set by do_pam_account */	buffer_put_int(&buffer, sshpam_account_status);	buffer_put_int(&buffer, sshpam_authctxt->force_pwchange);	/* Export any environment strings set in child */	for(i = 0; environ[i] != NULL; i++)		; /* Count */	buffer_put_int(&buffer, i);	for(i = 0; environ[i] != NULL; i++)		buffer_put_cstring(&buffer, environ[i]);	/* Export any environment strings set by PAM in child */	env_from_pam = pam_getenvlist(sshpam_handle);	for(i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++)		; /* Count */	buffer_put_int(&buffer, i);	for(i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++)		buffer_put_cstring(&buffer, env_from_pam[i]);#endif /* USE_POSIX_THREADS */	/* XXX - can't do much about an error here */	ssh_msg_send(ctxt->pam_csock, sshpam_err, &buffer);	buffer_free(&buffer);	pthread_exit(NULL); auth_fail:	buffer_put_cstring(&buffer,	    pam_strerror(sshpam_handle, sshpam_err));	/* XXX - can't do much about an error here */	ssh_msg_send(ctxt->pam_csock, PAM_AUTH_ERR, &buffer);	buffer_free(&buffer);	pthread_exit(NULL);	return (NULL); /* Avoid warning for non-pthread case */}voidsshpam_thread_cleanup(void){	struct pam_ctxt *ctxt = cleanup_ctxt;	debug3("PAM: %s entering", __func__);	if (ctxt != NULL && ctxt->pam_thread != 0) {		pthread_cancel(ctxt->pam_thread);		pthread_join(ctxt->pam_thread, NULL);		close(ctxt->pam_psock);		close(ctxt->pam_csock);		memset(ctxt, 0, sizeof(*ctxt));		cleanup_ctxt = NULL;	}}static intsshpam_null_conv(int n, struct pam_message **msg,    struct pam_response **resp, void *data){	debug3("PAM: %s entering, %d messages", __func__, n);	return (PAM_CONV_ERR);}static struct pam_conv null_conv = { sshpam_null_conv, NULL };static intsshpam_store_conv(int n, struct pam_message **msg,    struct pam_response **resp, void *data){	struct pam_response *reply;	int i;	size_t len;	debug3("PAM: %s called with %d messages", __func__, n);	*resp = NULL;	if (n <= 0 || n > PAM_MAX_NUM_MSG)		return (PAM_CONV_ERR);	if ((reply = malloc(n * sizeof(*reply))) == NULL)		return (PAM_CONV_ERR);	memset(reply, 0, n * sizeof(*reply));	for (i = 0; i < n; ++i) {		switch (PAM_MSG_MEMBER(msg, i, msg_style)) {		case PAM_ERROR_MSG:		case PAM_TEXT_INFO:			len = strlen(PAM_MSG_MEMBER(msg, i, msg));			buffer_append(&loginmsg, PAM_MSG_MEMBER(msg, i, msg), len);			buffer_append(&loginmsg, "\n", 1 );			reply[i].resp_retcode = PAM_SUCCESS;			break;		default:			goto fail;		}	}	*resp = reply;	return (PAM_SUCCESS); fail:	for(i = 0; i < n; i++) {		if (reply[i].resp != NULL)			xfree(reply[i].resp);	}	xfree(reply);	return (PAM_CONV_ERR);}static struct pam_conv store_conv = { sshpam_store_conv, NULL };voidsshpam_cleanup(void){	debug("PAM: cleanup");	if (sshpam_handle == NULL)		return;	pam_set_item(sshpam_handle, PAM_CONV, (const void *)&null_conv);	if (sshpam_cred_established) {		pam_setcred(sshpam_handle, PAM_DELETE_CRED);		sshpam_cred_established = 0;	}	if (sshpam_session_open) {		pam_close_session(sshpam_handle, PAM_SILENT);		sshpam_session_open = 0;	}	sshpam_authenticated = 0;	pam_end(sshpam_handle, sshpam_err);	sshpam_handle = NULL;}static intsshpam_init(Authctxt *authctxt){	extern char *__progname;	const char *pam_rhost, *pam_user, *user = authctxt->user;	if (sshpam_handle != NULL) {		/* We already have a PAM context; check if the user matches */		sshpam_err = pam_get_item(sshpam_handle,		    PAM_USER, (void **)&pam_user);		if (sshpam_err == PAM_SUCCESS && strcmp(user, pam_user) == 0)			return (0);		pam_end(sshpam_handle, sshpam_err);		sshpam_handle = NULL;	}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -