summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--contrib/sssd.spec.in2
-rw-r--r--src/Makefile.am35
-rw-r--r--src/config/SSSDConfig.py4
-rwxr-xr-xsrc/config/SSSDConfigTest.py1
-rw-r--r--src/config/etc/sssd.api.d/sssd-simple.conf5
-rw-r--r--src/man/sssd-simple.5.xml124
-rw-r--r--src/man/sssd.conf.5.xml7
-rw-r--r--src/providers/simple/simple_access.c160
-rw-r--r--src/providers/simple/simple_access.h36
-rw-r--r--src/tests/simple_access-tests.c173
10 files changed, 542 insertions, 5 deletions
diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in
index 63f55c5c8..16e2b4078 100644
--- a/contrib/sssd.spec.in
+++ b/contrib/sssd.spec.in
@@ -253,6 +253,7 @@ rm -f \
$RPM_BUILD_ROOT/%{_libdir}/sssd/libsss_proxy.la \
$RPM_BUILD_ROOT/%{_libdir}/sssd/libsss_krb5.la \
$RPM_BUILD_ROOT/%{_libdir}/sssd/libsss_ipa.la \
+ $RPM_BUILD_ROOT/%{_libdir}/sssd/libsss_simple.la \
$RPM_BUILD_ROOT/%{_libdir}/krb5/plugins/libkrb5/sssd_krb5_locator_plugin.la \
$RPM_BUILD_ROOT/%{python_sitearch}/pysss.la
@@ -301,6 +302,7 @@ rm -rf $RPM_BUILD_ROOT
%{_mandir}/man5/sssd-ipa.5*
%{_mandir}/man5/sssd-krb5.5*
%{_mandir}/man5/sssd-ldap.5*
+%{_mandir}/man5/sssd-simple.5*
%{_mandir}/man8/sssd.8*
%{_mandir}/man8/sss_groupadd.8*
%{_mandir}/man8/sss_groupdel.8*
diff --git a/src/Makefile.am b/src/Makefile.am
index aa7fccb9d..9da383b5d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -75,7 +75,8 @@ if HAVE_CHECK
fail_over-tests \
find_uid-tests \
auth-tests \
- ipa_ldap_opt-tests
+ ipa_ldap_opt-tests \
+ simple_access-tests
endif
check_PROGRAMS = \
@@ -90,7 +91,8 @@ sssdlib_LTLIBRARIES = \
libsss_ldap.la \
libsss_krb5.la \
libsss_proxy.la \
- libsss_ipa.la
+ libsss_ipa.la \
+ libsss_simple.la
ldblib_LTLIBRARIES = \
memberof.la
@@ -324,6 +326,7 @@ dist_noinst_HEADERS = \
providers/fail_over.h \
providers/providers.h \
providers/child_common.h \
+ providers/simple/simple_access.h \
providers/krb5/krb5_auth.h \
providers/krb5/krb5_common.h \
providers/krb5/krb5_utils.h \
@@ -635,6 +638,17 @@ ipa_ldap_opt_tests_LDADD = \
$(CHECK_LIBS) \
libsss_test_common.la
+simple_access_tests_SOURCES = \
+ tests/simple_access-tests.c \
+ providers/simple/simple_access.c \
+ $(SSSD_UTIL_OBJ)
+simple_access_tests_CFLAGS = \
+ $(AM_CFLAGS) \
+ $(CHECK_CFLAGS)
+simple_access_tests_LDADD = \
+ $(SSSD_LIBS) \
+ $(CHECK_LIBS)
+
endif
stress_tests_SOURCES = \
@@ -721,6 +735,16 @@ libsss_proxy_la_LDFLAGS = \
-version-info 1:0:0 \
-module
+libsss_simple_la_SOURCES = \
+ providers/simple/simple_access.c
+libsss_simple_la_CFLAGS = \
+ $(AM_CFLAGS)
+libsss_simple_la_LIBADD = \
+ $(PAM_LIBS)
+libsss_simple_la_LDFLAGS = \
+ -version-info 1:0:0 \
+ -module
+
libsss_krb5_la_SOURCES = \
util/find_uid.c \
providers/child_common.c \
@@ -860,8 +884,8 @@ XSLTPROC_FLAGS = --catalogs --xinclude --nonet
dist_man_MANS = man/sss_useradd.8 man/sss_userdel.8 man/sss_usermod.8 \
man/sss_groupadd.8 man/sss_groupdel.8 man/sss_groupmod.8 \
man/sssd.8 man/sssd.conf.5 man/sssd-ldap.5 man/sssd-krb5.5 \
- man/sssd-ipa.5 man/sssd_krb5_locator_plugin.8 \
- man/sss_groupshow.8 man/pam_sss.8
+ man/sssd-ipa.5 man/sssd-simple.5 \
+ man/sssd_krb5_locator_plugin.8 man/sss_groupshow.8 man/pam_sss.8
SUFFIXES = .1.xml .1 .3.xml .3 .5.xml .5 .8.xml .8
.1.xml.1:
@@ -901,7 +925,8 @@ dist_sssdapiplugin_DATA = \
config/etc/sssd.api.d/sssd-krb5.conf \
config/etc/sssd.api.d/sssd-ldap.conf \
config/etc/sssd.api.d/sssd-local.conf \
- config/etc/sssd.api.d/sssd-proxy.conf
+ config/etc/sssd.api.d/sssd-proxy.conf \
+ config/etc/sssd.api.d/sssd-simple.conf
installsssddirs::
mkdir -p \
diff --git a/src/config/SSSDConfig.py b/src/config/SSSDConfig.py
index 2697c71ba..c9e08caf0 100644
--- a/src/config/SSSDConfig.py
+++ b/src/config/SSSDConfig.py
@@ -144,6 +144,10 @@ option_strings = {
# [provider/ldap/auth]
'ldap_pwd_policy' : _('Policy to evaluate the password expiration'),
+ # [provider/simple/access]
+ 'simple_allow_users' : _('Comma separated list of allowed users'),
+ 'simple_deny_users' : _('Comma separated list of prohibited users'),
+
# [provider/local/id]
'default_shell' : _('Default shell, /bin/bash'),
'base_directory' : _('Base for home directories'),
diff --git a/src/config/SSSDConfigTest.py b/src/config/SSSDConfigTest.py
index ef761a2ba..4e9d09144 100755
--- a/src/config/SSSDConfigTest.py
+++ b/src/config/SSSDConfigTest.py
@@ -629,6 +629,7 @@ class SSSDConfigTestSSSDDomain(unittest.TestCase):
'ldap': ['id', 'auth', 'chpass'],
'krb5': ['auth', 'chpass'],
'proxy': ['id', 'auth'],
+ 'simple': ['access'],
'permit': ['access'],
'deny': ['access']}
diff --git a/src/config/etc/sssd.api.d/sssd-simple.conf b/src/config/etc/sssd.api.d/sssd-simple.conf
new file mode 100644
index 000000000..13fbeb9e9
--- /dev/null
+++ b/src/config/etc/sssd.api.d/sssd-simple.conf
@@ -0,0 +1,5 @@
+[provider/simple]
+
+[provider/simple/access]
+simple_allow_users = str, None, false
+simple_deny_users = str, None, false
diff --git a/src/man/sssd-simple.5.xml b/src/man/sssd-simple.5.xml
new file mode 100644
index 000000000..260d15ab8
--- /dev/null
+++ b/src/man/sssd-simple.5.xml
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE reference PUBLIC "-//OASIS//DTD DocBook V4.4//EN"
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
+<reference>
+<title>SSSD Manual pages</title>
+<refentry>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="include/upstream.xml" />
+
+ <refmeta>
+ <refentrytitle>sssd-simple</refentrytitle>
+ <manvolnum>5</manvolnum>
+ <refmiscinfo class="manual">File Formats and Conventions</refmiscinfo>
+ </refmeta>
+
+ <refnamediv id='name'>
+ <refname>sssd-simple</refname>
+ <refpurpose>the configuration file for SSSD's 'simple' access-control
+ provider</refpurpose>
+ </refnamediv>
+
+ <refsect1 id='description'>
+ <title>DESCRIPTION</title>
+ <para>
+ This manual page describes the configuration of the simple
+ access-control provider for
+ <citerefentry>
+ <refentrytitle>sssd</refentrytitle>
+ <manvolnum>8</manvolnum>
+ </citerefentry>.
+ For a detailed syntax reference, refer to the
+ <quote>FILE FORMAT</quote> section of the
+ <citerefentry>
+ <refentrytitle>sssd.conf</refentrytitle>
+ <manvolnum>5</manvolnum>
+ </citerefentry> manual page.
+ </para>
+ <para>
+ The simple access provider grants or denies access based on an
+ access or deny list of user names. Here to following rules apply:
+ <itemizedlist>
+ <listitem>
+ <para>If both lists are empty, access is granted</para>
+ </listitem>
+ <listitem>
+ <para>If simple_allow_users is set, only users from this
+ list are allowed access.</para>
+ <para>This setting supersedes the simple_deny_users list
+ (which would be redundant).</para>
+ </listitem>
+ <listitem>
+ <para>If the simple_allow_users list is empty, users are
+ allowed access unless they appear in the
+ simple_deny_users list</para>
+ </listitem>
+ </itemizedlist>
+ </para>
+ </refsect1>
+
+ <refsect1 id='file-format'>
+ <title>CONFIGURATION OPTIONS</title>
+ <para>Refer to the section <quote>DOMAIN SECTIONS</quote> of the
+ <citerefentry>
+ <refentrytitle>sssd.conf</refentrytitle>
+ <manvolnum>5</manvolnum>
+ </citerefentry> manual page for details on the configuration of an
+ SSSD domain.
+ <variablelist>
+ <varlistentry>
+ <term>simple_allow_users (string)</term>
+ <listitem>
+ <para>
+ Comma separated list of users who are allowed to log
+ in.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>simple_deny_users (string)</term>
+ <listitem>
+ <para>
+ Comma separated list of users who are rejected if
+ simple_allow_users is not set.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ <para>
+ Please note that it is an configuration error if both,
+ simple_allow_users and simple_deny_users, are defined.
+ </para>
+ </refsect1>
+
+ <refsect1 id='example'>
+ <title>EXAMPLE</title>
+ <para>
+ The following example assumes that SSSD is correctly
+ configured and example.com is one of the domains in the
+ <replaceable>[sssd]</replaceable> section. This examples shows only
+ the simple access provider-specific options.
+ </para>
+ <para>
+<programlisting>
+ [domain/example.com]
+ access_provider = simple
+ simple_allow_users = user1, user2
+</programlisting>
+ </para>
+ </refsect1>
+
+ <refsect1 id='see_also'>
+ <title>SEE ALSO</title>
+ <para>
+ <citerefentry>
+ <refentrytitle>sssd.conf</refentrytitle><manvolnum>5</manvolnum>
+ </citerefentry>,
+ <citerefentry>
+ <refentrytitle>sssd</refentrytitle><manvolnum>8</manvolnum>
+ </citerefentry>
+ </para>
+ </refsect1>
+</refentry>
+</reference>
diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
index 6303fcbc9..daf61afc4 100644
--- a/src/man/sssd.conf.5.xml
+++ b/src/man/sssd.conf.5.xml
@@ -565,6 +565,13 @@
<quote>deny</quote> always deny access.
</para>
<para>
+ <quote>simple</quote> access control based on access
+ or deny lists. See <citerefentry>
+ <refentrytitle>sssd-simple</refentrytitle>
+ <manvolnum>5</manvolnum></citerefentry> for more
+ information on configuring the simple access module.
+ </para>
+ <para>
Default: <quote>permit</quote>
</para>
</listitem>
diff --git a/src/providers/simple/simple_access.c b/src/providers/simple/simple_access.c
new file mode 100644
index 000000000..4d6135fa4
--- /dev/null
+++ b/src/providers/simple/simple_access.c
@@ -0,0 +1,160 @@
+/*
+ SSSD
+
+ Simple access control
+
+ Copyright (C) Sumit Bose <sbose@redhat.com> 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <errno.h>
+
+#include <security/pam_modules.h>
+
+#include "util/util.h"
+#include "providers/dp_backend.h"
+#include "db/sysdb.h"
+#include "providers/simple/simple_access.h"
+
+#define CONFDB_SIMPLE_ALLOW_USERS "simple_allow_users"
+#define CONFDB_SIMPLE_DENY_USERS "simple_deny_users"
+
+errno_t simple_access_check(struct simple_ctx *ctx, const char *username,
+ bool *access_granted)
+{
+ int i;
+
+ *access_granted = false;
+ if (ctx->allow_users != NULL) {
+ for(i = 0; ctx->allow_users[i] != NULL; i++) {
+ if (strcmp(username, ctx->allow_users[i]) == 0) {
+ DEBUG(9, ("User [%s] found in allow list, access granted.\n",
+ username));
+ *access_granted = true;
+ return EOK;
+ }
+ }
+ } else {
+ *access_granted = true;
+ if (ctx->deny_users != NULL) {
+ for(i = 0; ctx->deny_users[i] != NULL; i++) {
+ if (strcmp(username, ctx->deny_users[i]) == 0) {
+ DEBUG(9, ("User [%s] found in deny list, access denied.\n",
+ username));
+ *access_granted = false;
+ return EOK;
+ }
+ }
+ }
+ }
+
+ return EOK;
+}
+
+void simple_access_handler(struct be_req *be_req)
+{
+ int ret;
+ bool access_granted = false;
+ struct pam_data *pd;
+ struct simple_ctx *ctx;
+
+ pd = talloc_get_type(be_req->req_data, struct pam_data);
+
+ pd->pam_status = PAM_SYSTEM_ERR;
+
+ if (pd->cmd != SSS_PAM_ACCT_MGMT) {
+ DEBUG(4, ("simple access does not handles pam task %d.\n", pd->cmd));
+ pd->pam_status = PAM_MODULE_UNKNOWN;
+ goto done;
+ }
+
+ ctx = talloc_get_type(be_req->be_ctx->bet_info[BET_ACCESS].pvt_bet_data,
+ struct simple_ctx);
+
+ ret = simple_access_check(ctx, pd->user, &access_granted);
+ if (ret != EOK) {
+ pd->pam_status = PAM_SYSTEM_ERR;
+ goto done;
+ }
+
+ if (access_granted) {
+ pd->pam_status = PAM_SUCCESS;
+ } else {
+ pd->pam_status = PAM_PERM_DENIED;
+ }
+
+done:
+ be_req->fn(be_req, DP_ERR_OK, pd->pam_status, NULL);
+}
+
+struct bet_ops simple_access_ops = {
+ .handler = simple_access_handler,
+ .finalize = NULL
+};
+
+int sssm_simple_access_init(struct be_ctx *bectx, struct bet_ops **ops,
+ void **pvt_data)
+{
+ int ret = EINVAL;
+ struct simple_ctx *ctx;
+
+ ctx = talloc_zero(bectx, struct simple_ctx);
+ if (ctx == NULL) {
+ DEBUG(1, ("talloc_zero failed.\n"));
+ return ENOMEM;
+ }
+
+ ret = confdb_get_string_as_list(bectx->cdb, ctx, bectx->conf_path,
+ CONFDB_SIMPLE_ALLOW_USERS,
+ &ctx->allow_users);
+ if (ret != EOK) {
+ if (ret == ENOENT) {
+ DEBUG(9, ("Allow user list is empty.\n"));
+ ctx->allow_users = NULL;
+ } else {
+ DEBUG(1, ("confdb_get_string_as_list failed.\n"));
+ goto failed;
+ }
+ }
+
+ ret = confdb_get_string_as_list(bectx->cdb, ctx, bectx->conf_path,
+ CONFDB_SIMPLE_DENY_USERS,
+ &ctx->deny_users);
+ if (ret != EOK) {
+ if (ret == ENOENT) {
+ DEBUG(9, ("Deny user list is empty.\n"));
+ ctx->deny_users = NULL;
+ } else {
+ DEBUG(1, ("confdb_get_string_as_list failed.\n"));
+ goto failed;
+ }
+ }
+
+ if (ctx->allow_users != NULL && ctx->deny_users != NULL) {
+ DEBUG(1, ("Access and deny list are defined, only one is allowed.\n"));
+ ret = EINVAL;
+ goto failed;
+ }
+
+
+ *ops = &simple_access_ops;
+ *pvt_data = ctx;
+
+ return EOK;
+
+failed:
+ talloc_free(ctx);
+ return ret;
+}
diff --git a/src/providers/simple/simple_access.h b/src/providers/simple/simple_access.h
new file mode 100644
index 000000000..0aac42a5f
--- /dev/null
+++ b/src/providers/simple/simple_access.h
@@ -0,0 +1,36 @@
+/*
+ SSSD
+
+ Simple access control
+
+ Copyright (C) Sumit Bose <sbose@redhat.com> 2010
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef __SIMPLE_ACCESS_H__
+#define __SIMPLE_ACCESS_H__
+
+#include <stdlib.h>
+
+#include "util/util.h"
+
+struct simple_ctx {
+ char **allow_users;
+ char **deny_users;
+};
+
+errno_t simple_access_check(struct simple_ctx *ctx, const char *username,
+ bool *access_granted);
+#endif /* __SIMPLE_ACCESS_H__ */
diff --git a/src/tests/simple_access-tests.c b/src/tests/simple_access-tests.c
new file mode 100644
index 000000000..f1d3775a3
--- /dev/null
+++ b/src/tests/simple_access-tests.c
@@ -0,0 +1,173 @@
+/*
+ SSSD
+
+ Simple access module -- Tests
+
+ Authors:
+ Sumit Bose <sbose@redhat.com>
+
+ Copyright (C) 2010 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ 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, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdlib.h>
+#include <popt.h>
+#include <check.h>
+
+#include "providers/simple/simple_access.h"
+
+const char *ulist_1[] = {"u1", "u2", NULL};
+
+struct simple_ctx *ctx = NULL;
+
+void setup_simple(void)
+{
+ fail_unless(ctx == NULL, "Simple context already initialized.");
+ ctx = talloc_zero(NULL, struct simple_ctx);
+ fail_unless(ctx != NULL, "Cannot create simple context.");
+}
+
+void teardown_simple(void)
+{
+ int ret;
+ fail_unless(ctx != NULL, "Simple context already freed.");
+ ret = talloc_free(ctx);
+ ctx = NULL;
+ fail_unless(ret == 0, "Connot free simple context.");
+}
+
+START_TEST(test_both_empty)
+{
+ int ret;
+ bool access_granted = false;
+
+ ctx->allow_users = NULL;
+ ctx->deny_users = NULL;
+
+ ret = simple_access_check(ctx, "u1", &access_granted);
+ fail_unless(ret == EOK, "access_simple_check failed.");
+ fail_unless(access_granted == true, "Access denied "
+ "while both lists are empty.");
+}
+END_TEST
+
+START_TEST(test_allow_empty)
+{
+ int ret;
+ bool access_granted = true;
+
+ ctx->allow_users = NULL;
+ ctx->deny_users = discard_const(ulist_1);
+
+ ret = simple_access_check(ctx, "u1", &access_granted);
+ fail_unless(ret == EOK, "access_simple_check failed.");
+ fail_unless(access_granted == false, "Access granted "
+ "while user is in deny list.");
+
+ ret = simple_access_check(ctx, "u3", &access_granted);
+ fail_unless(ret == EOK, "access_simple_check failed.");
+ fail_unless(access_granted == true, "Access denied "
+ "while user is not in deny list.");
+}
+END_TEST
+
+START_TEST(test_deny_empty)
+{
+ int ret;
+ bool access_granted = false;
+
+ ctx->allow_users = discard_const(ulist_1);
+ ctx->deny_users = NULL;
+
+ ret = simple_access_check(ctx, "u1", &access_granted);
+ fail_unless(ret == EOK, "access_simple_check failed.");
+ fail_unless(access_granted == true, "Access denied "
+ "while user is in allow list.");
+
+ ret = simple_access_check(ctx, "u3", &access_granted);
+ fail_unless(ret == EOK, "access_simple_check failed.");
+ fail_unless(access_granted == false, "Access granted "
+ "while user is not in allow list.");
+}
+END_TEST
+
+START_TEST(test_both_set)
+{
+ int ret;
+ bool access_granted = false;
+
+ ctx->allow_users = discard_const(ulist_1);
+ ctx->deny_users = discard_const(ulist_1);
+
+ ret = simple_access_check(ctx, "u1", &access_granted);
+ fail_unless(ret == EOK, "access_simple_check failed.");
+ fail_unless(access_granted == true, "Access denied "
+ "while user is in allow list.");
+
+ ret = simple_access_check(ctx, "u3", &access_granted);
+ fail_unless(ret == EOK, "access_simple_check failed.");
+ fail_unless(access_granted == false, "Access granted "
+ "while user is not in allow list.");
+}
+END_TEST
+
+Suite *access_simple_suite (void)
+{
+ Suite *s = suite_create("access_simple");
+
+ TCase *tc_allow_deny = tcase_create("allow/deny");
+ tcase_add_checked_fixture(tc_allow_deny, setup_simple, teardown_simple);
+ tcase_add_test(tc_allow_deny, test_both_empty);
+ tcase_add_test(tc_allow_deny, test_allow_empty);
+ tcase_add_test(tc_allow_deny, test_deny_empty);
+ tcase_add_test(tc_allow_deny, test_both_set);
+ suite_add_tcase(s, tc_allow_deny);
+
+ return s;
+}
+
+int main(int argc, const char *argv[])
+{
+ int opt;
+ poptContext pc;
+ int number_failed;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ SSSD_MAIN_OPTS
+ { NULL }
+ };
+
+ pc = poptGetContext(argv[0], argc, argv, long_options, 0);
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch(opt) {
+ default:
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ poptPrintUsage(pc, stderr, 0);
+ return 1;
+ }
+ }
+ poptFreeContext(pc);
+
+
+ Suite *s = access_simple_suite();
+ SRunner *sr = srunner_create(s);
+ srunner_run_all(sr, CK_ENV);
+ number_failed = srunner_ntests_failed(sr);
+ srunner_free(sr);
+ return (number_failed==0 ? EXIT_SUCCESS : EXIT_FAILURE);
+}
+