aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorColin Percival <cperciva@FreeBSD.org>2011-12-23 15:00:37 +0000
committerColin Percival <cperciva@FreeBSD.org>2011-12-23 15:00:37 +0000
commit795d49c5e0f91633fcd118c32d4adff995fdbd00 (patch)
tree202026c216109c19f9b35e677ca1d793b3423c84
parent7f3836953fe55f9a187459618933cb8ee0defe9e (diff)
downloadsrc-795d49c5e0f91633fcd118c32d4adff995fdbd00.tar.gz
src-795d49c5e0f91633fcd118c32d4adff995fdbd00.zip
Fix a problem whereby a corrupt DNS record can cause named to crash. [11:06]
Add an API for alerting internal libc routines to the presence of "unsafe" paths post-chroot, and use it in ftpd. [11:07] Fix a buffer overflow in telnetd. [11:08] Make pam_ssh ignore unpassphrased keys unless the "nullok" option is specified. [11:09] Add sanity checking of service names in pam_start. [11:10] Approved by: so (cperciva) Approved by: re (bz) Security: FreeBSD-SA-11:06.bind Security: FreeBSD-SA-11:07.chroot Security: FreeBSD-SA-11:08.telnetd Security: FreeBSD-SA-11:09.pam_ssh Security: FreeBSD-SA-11:10.pam
Notes
Notes: svn path=/releng/8.1/; revision=228843
-rw-r--r--UPDATING16
-rw-r--r--contrib/bind9/bin/named/query.c19
-rw-r--r--contrib/bind9/lib/dns/rbtdb.c7
-rw-r--r--contrib/openpam/lib/openpam_configure.c7
-rw-r--r--contrib/telnet/libtelnet/encrypt.c3
-rw-r--r--crypto/heimdal/appl/telnet/libtelnet/encrypt.c3
-rw-r--r--include/unistd.h1
-rw-r--r--lib/libc/Versions.def6
-rw-r--r--lib/libc/gen/Makefile.inc1
-rw-r--r--lib/libc/gen/Symbol.map4
-rw-r--r--lib/libc/gen/libc_dlopen.c61
-rw-r--r--lib/libc/include/libc_private.h11
-rw-r--r--lib/libc/net/nsdispatch.c4
-rw-r--r--lib/libpam/modules/pam_ssh/pam_ssh.c25
-rw-r--r--libexec/ftpd/ftpd.c1
-rw-r--r--libexec/ftpd/popen.c3
-rw-r--r--sys/conf/newvers.sh2
17 files changed, 150 insertions, 24 deletions
diff --git a/UPDATING b/UPDATING
index 4d8504f14744..3ade26282429 100644
--- a/UPDATING
+++ b/UPDATING
@@ -15,6 +15,22 @@ NOTE TO PEOPLE WHO THINK THAT FreeBSD 8.x IS SLOW ON IA64 OR SUN4V:
debugging tools present in HEAD were left in place because
sun4v support still needs work to become production ready.
+20111223: p7 FreeBSD-SA-11:06.bind, FreeBSD-SA-11:07.chroot
+ FreeBSD-SA-11:08.telnetd, FreeBSD-SA-11:09.pam_ssh
+ FreeBSD-SA-11:10.pam
+ Fix a problem whereby a corrupt DNS record can cause named to crash.
+ [11:06]
+
+ Add an API for alerting internal libc routines to the presence of
+ "unsafe" paths post-chroot, and use it in ftpd. [11:07]
+
+ Fix a buffer overflow in telnetd. [11:08]
+
+ Make pam_ssh ignore unpassphrased keys unless the "nullok" option is
+ specified. [11:09]
+
+ Add sanity checking of service names in pam_start. [11:10]
+
20111004: p6 FreeBSD-SA-11:05.unix (revised)
Fix a bug in UNIX socket handling in the linux emulator which was
exposed by the security fix in FreeBSD-SA-11:05.unix.
diff --git a/contrib/bind9/bin/named/query.c b/contrib/bind9/bin/named/query.c
index cef6d7f7c3a8..a5237eb72b5e 100644
--- a/contrib/bind9/bin/named/query.c
+++ b/contrib/bind9/bin/named/query.c
@@ -1282,11 +1282,9 @@ query_addadditional(void *arg, dns_name_t *name, dns_rdatatype_t qtype) {
goto addname;
if (result == DNS_R_NCACHENXRRSET) {
dns_rdataset_disassociate(rdataset);
- /*
- * Negative cache entries don't have sigrdatasets.
- */
- INSIST(sigrdataset == NULL ||
- ! dns_rdataset_isassociated(sigrdataset));
+ if (sigrdataset != NULL &&
+ dns_rdataset_isassociated(sigrdataset))
+ dns_rdataset_disassociate(sigrdataset);
}
if (result == ISC_R_SUCCESS) {
mname = NULL;
@@ -1327,8 +1325,9 @@ query_addadditional(void *arg, dns_name_t *name, dns_rdatatype_t qtype) {
goto addname;
if (result == DNS_R_NCACHENXRRSET) {
dns_rdataset_disassociate(rdataset);
- INSIST(sigrdataset == NULL ||
- ! dns_rdataset_isassociated(sigrdataset));
+ if (sigrdataset != NULL &&
+ dns_rdataset_isassociated(sigrdataset))
+ dns_rdataset_disassociate(sigrdataset);
}
if (result == ISC_R_SUCCESS) {
mname = NULL;
@@ -1777,10 +1776,8 @@ query_addadditional2(void *arg, dns_name_t *name, dns_rdatatype_t qtype) {
goto setcache;
if (result == DNS_R_NCACHENXRRSET) {
dns_rdataset_disassociate(rdataset);
- /*
- * Negative cache entries don't have sigrdatasets.
- */
- INSIST(! dns_rdataset_isassociated(sigrdataset));
+ if (dns_rdataset_isassociated(sigrdataset))
+ dns_rdataset_disassociate(sigrdataset);
}
if (result == ISC_R_SUCCESS) {
/* Remember the result as a cache */
diff --git a/contrib/bind9/lib/dns/rbtdb.c b/contrib/bind9/lib/dns/rbtdb.c
index df3a5f47e206..6ac024414dfa 100644
--- a/contrib/bind9/lib/dns/rbtdb.c
+++ b/contrib/bind9/lib/dns/rbtdb.c
@@ -278,6 +278,7 @@ typedef ISC_LIST(dns_rbtnode_t) rbtnodelist_t;
#define RDATASET_ATTR_RESIGN 0x0020
#define RDATASET_ATTR_STATCOUNT 0x0040
#define RDATASET_ATTR_OPTOUT 0x0080
+#define RDATASET_ATTR_NEGATIVE 0x0100
typedef struct acache_cbarg {
dns_rdatasetadditional_t type;
@@ -316,6 +317,8 @@ struct acachectl {
(((header)->attributes & RDATASET_ATTR_RESIGN) != 0)
#define OPTOUT(header) \
(((header)->attributes & RDATASET_ATTR_OPTOUT) != 0)
+#define NEGATIVE(header) \
+ (((header)->attributes & RDATASET_ATTR_NEGATIVE) != 0)
#define DEFAULT_NODE_LOCK_COUNT 7 /*%< Should be prime. */
@@ -4618,7 +4621,7 @@ cache_find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version,
rdataset);
if (need_headerupdate(found, search.now))
update = found;
- if (foundsig != NULL) {
+ if (!NEGATIVE(found) && foundsig != NULL) {
bind_rdataset(search.rbtdb, node, foundsig, search.now,
sigrdataset);
if (need_headerupdate(foundsig, search.now))
@@ -5245,7 +5248,7 @@ cache_findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
}
if (found != NULL) {
bind_rdataset(rbtdb, rbtnode, found, now, rdataset);
- if (foundsig != NULL)
+ if (!NEGATIVE(found) && foundsig != NULL)
bind_rdataset(rbtdb, rbtnode, foundsig, now,
sigrdataset);
}
diff --git a/contrib/openpam/lib/openpam_configure.c b/contrib/openpam/lib/openpam_configure.c
index f9197adcfa47..688b2acc50b9 100644
--- a/contrib/openpam/lib/openpam_configure.c
+++ b/contrib/openpam/lib/openpam_configure.c
@@ -285,6 +285,13 @@ openpam_load_chain(pam_handle_t *pamh,
size_t len;
int r;
+ /* don't allow to escape from policy_path */
+ if (strchr(service, '/')) {
+ openpam_log(PAM_LOG_ERROR, "invalid service name: %s",
+ service);
+ return (-PAM_SYSTEM_ERR);
+ }
+
for (path = openpam_policy_path; *path != NULL; ++path) {
len = strlen(*path);
if ((*path)[len - 1] == '/') {
diff --git a/contrib/telnet/libtelnet/encrypt.c b/contrib/telnet/libtelnet/encrypt.c
index 8bdf672ab3ba..f8e919414034 100644
--- a/contrib/telnet/libtelnet/encrypt.c
+++ b/contrib/telnet/libtelnet/encrypt.c
@@ -721,6 +721,9 @@ encrypt_keyid(struct key_info *kp, unsigned char *keyid, int len)
int dir = kp->dir;
int ret = 0;
+ if (len > MAXKEYLEN)
+ len = MAXKEYLEN;
+
if (!(ep = (*kp->getcrypt)(*kp->modep))) {
if (len == 0)
return;
diff --git a/crypto/heimdal/appl/telnet/libtelnet/encrypt.c b/crypto/heimdal/appl/telnet/libtelnet/encrypt.c
index 04dbe83d5c5e..a4669d234e83 100644
--- a/crypto/heimdal/appl/telnet/libtelnet/encrypt.c
+++ b/crypto/heimdal/appl/telnet/libtelnet/encrypt.c
@@ -736,6 +736,9 @@ encrypt_keyid(struct key_info *kp, unsigned char *keyid, int len)
int dir = kp->dir;
int ret = 0;
+ if (len > MAXKEYLEN)
+ len = MAXKEYLEN;
+
if (!(ep = (*kp->getcrypt)(*kp->modep))) {
if (len == 0)
return;
diff --git a/include/unistd.h b/include/unistd.h
index b4b7516bc6ac..4782380ad18b 100644
--- a/include/unistd.h
+++ b/include/unistd.h
@@ -513,6 +513,7 @@ int initgroups(const char *, gid_t);
int iruserok(unsigned long, int, const char *, const char *);
int iruserok_sa(const void *, int, int, const char *, const char *);
int issetugid(void);
+void __FreeBSD_libc_enter_restricted_mode(void);
long lpathconf(const char *, int);
#ifndef _MKDTEMP_DECLARED
char *mkdtemp(char *);
diff --git a/lib/libc/Versions.def b/lib/libc/Versions.def
index da0ca6f80632..bbd59a4e780b 100644
--- a/lib/libc/Versions.def
+++ b/lib/libc/Versions.def
@@ -19,6 +19,10 @@ FBSD_1.1 {
FBSD_1.2 {
} FBSD_1.1;
+# This version was first added to 10.0-current.
+FBSD_1.3 {
+} FBSD_1.2;
+
# This is our private namespace. Any global interfaces that are
# strictly for use only by other FreeBSD applications and libraries
# are listed here. We use a separate namespace so we can write
@@ -26,4 +30,4 @@ FBSD_1.2 {
#
# Please do NOT increment the version of this namespace.
FBSDprivate_1.0 {
-} FBSD_1.2;
+} FBSD_1.3;
diff --git a/lib/libc/gen/Makefile.inc b/lib/libc/gen/Makefile.inc
index d04dd7502057..ed61bd58a932 100644
--- a/lib/libc/gen/Makefile.inc
+++ b/lib/libc/gen/Makefile.inc
@@ -20,6 +20,7 @@ SRCS+= __getosreldate.c __xuname.c \
getpeereid.c getprogname.c getpwent.c getttyent.c \
getusershell.c getvfsbyname.c glob.c \
initgroups.c isatty.c isinf.c isnan.c jrand48.c lcong48.c \
+ libc_dlopen.c \
lockf.c lrand48.c mrand48.c nftw.c nice.c \
nlist.c nrand48.c opendir.c \
pause.c pmadvise.c popen.c posix_spawn.c \
diff --git a/lib/libc/gen/Symbol.map b/lib/libc/gen/Symbol.map
index 4c19a3de26a5..03edd607bf52 100644
--- a/lib/libc/gen/Symbol.map
+++ b/lib/libc/gen/Symbol.map
@@ -369,6 +369,10 @@ FBSD_1.2 {
getpagesizes;
};
+FBSD_1.3 {
+ __FreeBSD_libc_enter_restricted_mode;
+};
+
FBSDprivate_1.0 {
/* needed by thread libraries */
__thr_jtable;
diff --git a/lib/libc/gen/libc_dlopen.c b/lib/libc/gen/libc_dlopen.c
new file mode 100644
index 000000000000..2b1aa9e9d43d
--- /dev/null
+++ b/lib/libc/gen/libc_dlopen.c
@@ -0,0 +1,61 @@
+/*-
+ * Copyright (c) 2011 Xin Li <delphij@FreeBSD.org>
+ * All rights reserved.
+ *
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <dlfcn.h>
+#include <stddef.h>
+#include <unistd.h>
+
+#include "libc_private.h"
+
+/*
+ * Whether we want to restrict dlopen()s.
+ */
+static int __libc_restricted_mode = 0;
+
+void *
+libc_dlopen(const char *path, int mode)
+{
+
+ if (__libc_restricted_mode) {
+ _rtld_error("Service unavailable -- libc in restricted mode");
+ return (NULL);
+ } else
+ return (dlopen(path, mode));
+}
+
+void
+__FreeBSD_libc_enter_restricted_mode(void)
+{
+
+ __libc_restricted_mode = 1;
+ return;
+}
+
diff --git a/lib/libc/include/libc_private.h b/lib/libc/include/libc_private.h
index 052eb13003bb..d5f4212fd05f 100644
--- a/lib/libc/include/libc_private.h
+++ b/lib/libc/include/libc_private.h
@@ -44,6 +44,17 @@
extern int __isthreaded;
/*
+ * libc should use libc_dlopen internally, which respects a global
+ * flag where loading of new shared objects can be restricted.
+ */
+void *libc_dlopen(const char *, int);
+
+/*
+ * For dynamic linker.
+ */
+void _rtld_error(const char *fmt, ...);
+
+/*
* File lock contention is difficult to diagnose without knowing
* where locks were set. Allow a debug library to be built which
* records the source file and line number of each lock call.
diff --git a/lib/libc/net/nsdispatch.c b/lib/libc/net/nsdispatch.c
index 162d592acf68..08a0d183dd48 100644
--- a/lib/libc/net/nsdispatch.c
+++ b/lib/libc/net/nsdispatch.c
@@ -384,7 +384,7 @@ nss_configure(void)
confmod = statbuf.st_mtime;
#ifdef NS_CACHING
- handle = dlopen(NULL, RTLD_LAZY | RTLD_GLOBAL);
+ handle = libc_dlopen(NULL, RTLD_LAZY | RTLD_GLOBAL);
if (handle != NULL) {
nss_cache_cycle_prevention_func = dlsym(handle,
"_nss_cache_cycle_prevention_function");
@@ -497,7 +497,7 @@ nss_load_module(const char *source, nss_module_register_fn reg_fn)
if (snprintf(buf, sizeof(buf), "nss_%s.so.%d", mod.name,
NSS_MODULE_INTERFACE_VERSION) >= (int)sizeof(buf))
goto fin;
- mod.handle = dlopen(buf, RTLD_LOCAL|RTLD_LAZY);
+ mod.handle = libc_dlopen(buf, RTLD_LOCAL|RTLD_LAZY);
if (mod.handle == NULL) {
#ifdef _NSS_DEBUG
/* This gets pretty annoying since the built-in
diff --git a/lib/libpam/modules/pam_ssh/pam_ssh.c b/lib/libpam/modules/pam_ssh/pam_ssh.c
index 9d89045ac22f..4eeabe18d10b 100644
--- a/lib/libpam/modules/pam_ssh/pam_ssh.c
+++ b/lib/libpam/modules/pam_ssh/pam_ssh.c
@@ -91,7 +91,8 @@ static char *const pam_ssh_agent_envp[] = { NULL };
* struct pam_ssh_key containing the key and its comment.
*/
static struct pam_ssh_key *
-pam_ssh_load_key(const char *dir, const char *kfn, const char *passphrase)
+pam_ssh_load_key(const char *dir, const char *kfn, const char *passphrase,
+ int nullok)
{
struct pam_ssh_key *psk;
char fn[PATH_MAX];
@@ -101,7 +102,21 @@ pam_ssh_load_key(const char *dir, const char *kfn, const char *passphrase)
if (snprintf(fn, sizeof(fn), "%s/%s", dir, kfn) > (int)sizeof(fn))
return (NULL);
comment = NULL;
- key = key_load_private(fn, passphrase, &comment);
+ /*
+ * If the key is unencrypted, OpenSSL ignores the passphrase, so
+ * it will seem like the user typed in the right one. This allows
+ * a user to circumvent nullok by providing a dummy passphrase.
+ * Verify that the key really *is* encrypted by trying to load it
+ * with an empty passphrase, and if the key is not encrypted,
+ * accept only an empty passphrase.
+ */
+ key = key_load_private(fn, NULL, &comment);
+ if (key != NULL && !(*passphrase == '\0' && nullok)) {
+ key_free(key);
+ return (NULL);
+ }
+ if (key == NULL)
+ key = key_load_private(fn, passphrase, &comment);
if (key == NULL) {
openpam_log(PAM_LOG_DEBUG, "failed to load key from %s\n", fn);
return (NULL);
@@ -168,9 +183,6 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags __unused,
if (pam_err != PAM_SUCCESS)
return (pam_err);
- if (*passphrase == '\0' && !nullok)
- goto skip_keys;
-
/* switch to user credentials */
pam_err = openpam_borrow_cred(pamh, pwd);
if (pam_err != PAM_SUCCESS)
@@ -178,7 +190,7 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags __unused,
/* try to load keys from all keyfiles we know of */
for (kfn = pam_ssh_keyfiles; *kfn != NULL; ++kfn) {
- psk = pam_ssh_load_key(pwd->pw_dir, *kfn, passphrase);
+ psk = pam_ssh_load_key(pwd->pw_dir, *kfn, passphrase, nullok);
if (psk != NULL) {
pam_set_data(pamh, *kfn, psk, pam_ssh_free_key);
++nkeys;
@@ -188,7 +200,6 @@ pam_sm_authenticate(pam_handle_t *pamh, int flags __unused,
/* switch back to arbitrator credentials */
openpam_restore_cred(pamh);
- skip_keys:
/*
* If we tried an old token and didn't get anything, and
* try_first_pass was specified, try again after prompting the
diff --git a/libexec/ftpd/ftpd.c b/libexec/ftpd/ftpd.c
index 32c15f8df1d1..644851689784 100644
--- a/libexec/ftpd/ftpd.c
+++ b/libexec/ftpd/ftpd.c
@@ -1543,6 +1543,7 @@ skip:
reply(550, "Can't change root.");
goto bad;
}
+ __FreeBSD_libc_enter_restricted_mode();
} else /* real user w/o chroot */
homedir = pw->pw_dir;
/*
diff --git a/libexec/ftpd/popen.c b/libexec/ftpd/popen.c
index 8a739dc2ffed..9f80507a839f 100644
--- a/libexec/ftpd/popen.c
+++ b/libexec/ftpd/popen.c
@@ -143,6 +143,9 @@ ftpd_popen(char *program, char *type)
}
(void)close(pdes[1]);
}
+ /* Drop privileges before proceeding */
+ if (getuid() != geteuid() && setuid(geteuid()) < 0)
+ _exit(1);
if (strcmp(gargv[0], _PATH_LS) == 0) {
/* Reset getopt for ls_main() */
optreset = optind = optopt = 1;
diff --git a/sys/conf/newvers.sh b/sys/conf/newvers.sh
index fc406f69c662..2ddb0b986112 100644
--- a/sys/conf/newvers.sh
+++ b/sys/conf/newvers.sh
@@ -32,7 +32,7 @@
TYPE="FreeBSD"
REVISION="8.1"
-BRANCH="RELEASE-p6"
+BRANCH="RELEASE-p7"
if [ "X${BRANCH_OVERRIDE}" != "X" ]; then
BRANCH=${BRANCH_OVERRIDE}
fi