From 119ea57e460e68618c734cfd22bd5753e4ba8b6d Mon Sep 17 00:00:00 2001
From: Stephen Gallagher <sgallagh@redhat.com>
Date: Mon, 20 Dec 2010 16:05:14 -0500
Subject: [PATCH 2/2] Add authorizedService support

https://fedorahosted.org/sssd/ticket/670
---
 src/config/SSSDConfig.py                 |    1 +
 src/config/etc/sssd.api.d/sssd-ldap.conf |    1 +
 src/db/sysdb.h                           |    2 +
 src/man/sssd-ldap.5.xml                  |   26 ++++++
 src/providers/ipa/ipa_common.c           |    3 +-
 src/providers/ldap/ldap_common.c         |    6 +-
 src/providers/ldap/ldap_init.c           |    2 +
 src/providers/ldap/sdap.h                |    1 +
 src/providers/ldap/sdap_access.c         |  135 ++++++++++++++++++++++++++++++
 src/providers/ldap/sdap_access.h         |    2 +
 10 files changed, 176 insertions(+), 3 deletions(-)

diff --git a/src/config/SSSDConfig.py b/src/config/SSSDConfig.py
index de7f66a6aa690de8f320e2b29627186b5f94be0d..b117717882193c206be88332abf2698d68052943 100644
--- a/src/config/SSSDConfig.py
+++ b/src/config/SSSDConfig.py
@@ -166,6 +166,7 @@ option_strings = {
     'ldap_user_shadow_inactive' : _('shadowInactive attribute'),
     'ldap_user_shadow_expire' : _('shadowExpire attribute'),
     'ldap_user_shadow_flag' : _('shadowFlag attribute'),
+    'ldap_user_authorized_service' : _('Attribute listing authorized PAM services'),
     'ldap_user_krb_last_pwd_change' : _('krbLastPwdChange attribute'),
     'ldap_user_krb_password_expiration' : _('krbPasswordExpiration attribute'),
     'ldap_pwd_attribute' : _('Attribute indicating that server side password policies are active'),
diff --git a/src/config/etc/sssd.api.d/sssd-ldap.conf b/src/config/etc/sssd.api.d/sssd-ldap.conf
index c41ff7cc6bdde234cef6d78ad3e315892f402945..c2179f2daee57ade1fbce830fef8b4bdd4e805aa 100644
--- a/src/config/etc/sssd.api.d/sssd-ldap.conf
+++ b/src/config/etc/sssd.api.d/sssd-ldap.conf
@@ -55,6 +55,7 @@ ldap_user_shadow_expire = str, None, false
 ldap_user_shadow_flag = str, None, false
 ldap_user_krb_last_pwd_change = str, None, false
 ldap_user_krb_password_expiration = str, None, false
+ldap_user_authorized_service = str, None, false
 ldap_pwd_attribute = str, None, false
 ldap_group_search_base = str, None, false
 ldap_group_search_scope = str, None, false
diff --git a/src/db/sysdb.h b/src/db/sysdb.h
index b5eed49b4ae83b195eed80a8b35df4c2906330bf..3fefdf21536257edff6a314bef5310502d204e09 100644
--- a/src/db/sysdb.h
+++ b/src/db/sysdb.h
@@ -78,6 +78,8 @@
 #define SYSDB_CACHE_EXPIRE "dataExpireTimestamp"
 #define SYSDB_INITGR_EXPIRE "initgrExpireTimestamp"
 
+#define SYSDB_AUTHORIZED_SERVICE "authorizedService"
+
 #define SYSDB_NETGROUP_TRIPLE "netgroupTriple"
 #define SYSDB_ORIG_NETGROUP_MEMBER "originalMemberNisNetgroup"
 #define SYSDB_NETGROUP_MEMBER "memberNisNetgroup"
diff --git a/src/man/sssd-ldap.5.xml b/src/man/sssd-ldap.5.xml
index 3406dc469c41d009e90fb351d81079e77ccf474e..7a73346226e4ff2197de4a496cb384cc0e6d842c 100644
--- a/src/man/sssd-ldap.5.xml
+++ b/src/man/sssd-ldap.5.xml
@@ -524,6 +524,27 @@
                 </varlistentry>
 
                 <varlistentry>
+                    <term>ldap_user_authorized_service (string)</term>
+                    <listitem>
+                        <para>
+                            If access_provider=ldap and
+                            ldap_access_order=authorized_service, SSSD will
+                            use the presence of the authorizedService
+                            attribute in the user's LDAP entry to determine
+                            access privilege.
+                        </para>
+                        <para>
+                            An explicit deny (!svc) is resolved first. Second,
+                            SSSD searches for explicit allow (svc) and finally
+                            for allow_all (*).
+                        </para>
+                        <para>
+                            Default: authorizedService
+                        </para>
+                    </listitem>
+                </varlistentry>
+
+                <varlistentry>
                     <term>ldap_group_object_class (string)</term>
                     <listitem>
                         <para>
@@ -1109,6 +1130,11 @@ ldap_access_filter = memberOf=cn=allowedusers,ou=Groups,dc=example,dc=com
                             ldap_account_expire_policy
                         </para>
                         <para>
+                            <emphasis>authorized_service</emphasis>: use
+                            the authorizedService attribute to determine
+                            access
+                        </para>
+                        <para>
                             Default: filter
                         </para>
                         <para>
diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c
index 2e7724a62cf530966c03ebf64f25cf0233358e95..a42cc3b4d4984e1994c1f38931aa45b0fb480142 100644
--- a/src/providers/ipa/ipa_common.c
+++ b/src/providers/ipa/ipa_common.c
@@ -113,7 +113,8 @@ struct sdap_attr_map ipa_user_map[] = {
     { "ldap_user_shadow_flag", "shadowFlag", SYSDB_SHADOWPW_FLAG, NULL },
     { "ldap_user_krb_last_pwd_change", "krbLastPwdChange", SYSDB_KRBPW_LASTCHANGE, NULL },
     { "ldap_user_krb_password_expiration", "krbPasswordExpiration", SYSDB_KRBPW_EXPIRATION, NULL },
-    { "ldap_pwd_attribute", "pwdAttribute", SYSDB_PWD_ATTRIBUTE, NULL }
+    { "ldap_pwd_attribute", "pwdAttribute", SYSDB_PWD_ATTRIBUTE, NULL },
+    { "ldap_user_authorized_service", "authorizedService", SYSDB_AUTHORIZED_SERVICE, NULL }
 };
 
 struct sdap_attr_map ipa_group_map[] = {
diff --git a/src/providers/ldap/ldap_common.c b/src/providers/ldap/ldap_common.c
index a38d5cc2081677962c07c6b621519b5c82d1a78d..f0db53f222e21662e79b20180216ebd3da076465 100644
--- a/src/providers/ldap/ldap_common.c
+++ b/src/providers/ldap/ldap_common.c
@@ -118,7 +118,8 @@ struct sdap_attr_map rfc2307_user_map[] = {
     { "ldap_user_shadow_flag", "shadowFlag", SYSDB_SHADOWPW_FLAG, NULL },
     { "ldap_user_krb_last_pwd_change", "krbLastPwdChange", SYSDB_KRBPW_LASTCHANGE, NULL },
     { "ldap_user_krb_password_expiration", "krbPasswordExpiration", SYSDB_KRBPW_EXPIRATION, NULL },
-    { "ldap_pwd_attribute", "pwdAttribute", SYSDB_PWD_ATTRIBUTE, NULL }
+    { "ldap_pwd_attribute", "pwdAttribute", SYSDB_PWD_ATTRIBUTE, NULL },
+    { "ldap_user_authorized_service", "authorizedService", SYSDB_AUTHORIZED_SERVICE, NULL }
 };
 
 struct sdap_attr_map rfc2307_group_map[] = {
@@ -157,7 +158,8 @@ struct sdap_attr_map rfc2307bis_user_map[] = {
     { "ldap_user_shadow_flag", "shadowFlag", SYSDB_SHADOWPW_FLAG, NULL },
     { "ldap_user_krb_last_pwd_change", "krbLastPwdChange", SYSDB_KRBPW_LASTCHANGE, NULL },
     { "ldap_user_krb_password_expiration", "krbPasswordExpiration", SYSDB_KRBPW_EXPIRATION, NULL },
-    { "ldap_pwd_attribute", "pwdAttribute", SYSDB_PWD_ATTRIBUTE, NULL }
+    { "ldap_pwd_attribute", "pwdAttribute", SYSDB_PWD_ATTRIBUTE, NULL },
+    { "ldap_user_authorized_service", "authorizedService", SYSDB_AUTHORIZED_SERVICE, NULL }
 };
 
 struct sdap_attr_map rfc2307bis_group_map[] = {
diff --git a/src/providers/ldap/ldap_init.c b/src/providers/ldap/ldap_init.c
index a1d8f05d7f70f1fb70bf4e8f29eae0be97f34532..d5dad36078fdbda2bea52040b871076eacddf55f 100644
--- a/src/providers/ldap/ldap_init.c
+++ b/src/providers/ldap/ldap_init.c
@@ -352,6 +352,8 @@ int sssm_ldap_access_init(struct be_ctx *bectx,
                     goto done;
                 }
             }
+        } else if (strcasecmp(order_list[c], LDAP_ACCESS_SERVICE_NAME) == 0) {
+            access_ctx->access_rule[c] = LDAP_ACCESS_SERVICE;
         } else {
             DEBUG(1, ("Unexpected access rule name [%s].\n", order_list[c]));
             ret = EINVAL;
diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h
index 8c629a3b46793d58a9d55655b642c4a144772ab6..57865fe097ba11f59b325cdcd07efac086a72ad8 100644
--- a/src/providers/ldap/sdap.h
+++ b/src/providers/ldap/sdap.h
@@ -229,6 +229,7 @@ enum sdap_user_attrs {
     SDAP_AT_KP_LASTCHANGE,
     SDAP_AT_KP_EXPIRATION,
     SDAP_AT_PWD_ATTRIBUTE,
+    SDAP_AT_AUTH_SVC,
 
     SDAP_OPTS_USER /* attrs counter */
 };
diff --git a/src/providers/ldap/sdap_access.c b/src/providers/ldap/sdap_access.c
index fa3f522a15b5f0a9d06c1c9035e993bd36e37e36..4767812c08077ca1fe60a1f3a8dde66308ad1d4c 100644
--- a/src/providers/ldap/sdap_access.c
+++ b/src/providers/ldap/sdap_access.c
@@ -72,6 +72,17 @@ static struct tevent_req *sdap_account_expired_send(TALLOC_CTX *mem_ctx,
                                              struct sdap_access_ctx *access_ctx,
                                              const char *username,
                                              struct ldb_message *user_entry);
+static errno_t sdap_access_service_recv(struct tevent_req *req,
+                                        int *pam_status);
+static void sdap_access_service_done(struct tevent_req *subreq);
+
+static struct tevent_req *sdap_access_service_send(
+        TALLOC_CTX *mem_ctx,
+        struct tevent_context *ev,
+        struct sdap_access_ctx *access_ctx,
+        struct pam_data *pd,
+        struct ldb_message *user_entry);
+
 static void sdap_account_expired_done(struct tevent_req *subreq);
 
 static void sdap_access_done(struct tevent_req *req);
@@ -235,6 +246,19 @@ static errno_t select_next_rule(struct tevent_req *req)
 
             tevent_req_set_callback(subreq, sdap_account_expired_done, req);
             return EOK;
+
+        case LDAP_ACCESS_SERVICE:
+            subreq = sdap_access_service_send(state, state->ev,
+                                              state->access_ctx,
+                                              state->pd,
+                                              state->user_entry);
+            if (subreq == NULL) {
+                DEBUG(1, ("sdap_access_service_send failed.\n"));
+                return ENOMEM;
+            }
+            tevent_req_set_callback(subreq, sdap_access_service_done, req);
+            return EOK;
+
         default:
             DEBUG(1, ("Unexpected access rule type. Access denied.\n"));
     }
@@ -759,6 +783,117 @@ static void sdap_access_filter_done(struct tevent_req *subreq)
     return;
 }
 
+
+struct sdap_access_service_ctx {
+    int pam_status;
+};
+
+static struct tevent_req *sdap_access_service_send(
+        TALLOC_CTX *mem_ctx,
+        struct tevent_context *ev,
+        struct sdap_access_ctx *access_ctx,
+        struct pam_data *pd,
+        struct ldb_message *user_entry)
+{
+    errno_t ret;
+    struct tevent_req *req;
+    struct sdap_access_service_ctx *state;
+    struct ldb_message_element *el;
+    unsigned int i;
+    char *service;
+
+    req = tevent_req_create(mem_ctx, &state, struct sdap_access_service_ctx);
+    if (!req) {
+        return NULL;
+    }
+
+    state->pam_status = PAM_PERM_DENIED;
+
+    el = ldb_msg_find_element(user_entry, SYSDB_AUTHORIZED_SERVICE);
+    if (!el || el->num_values == 0) {
+        DEBUG(1, ("Missing authorized services. Access denied\n"));
+        ret = EOK;
+        goto done;
+    }
+
+    for (i = 0; i < el->num_values; i++) {
+        service = (char *)el->values[i].data;
+        if (service[0] == '!' &&
+                strcasecmp(pd->service, service+1) == 0) {
+            /* This service is explicitly denied */
+            state->pam_status = PAM_PERM_DENIED;
+            DEBUG(4, ("Access denied by [%s]\n", service));
+            /* A denial trumps all. Break here */
+            break;
+
+        } else if (strcasecmp(pd->service, service) == 0) {
+            /* This service is explicitly allowed */
+            state->pam_status = PAM_SUCCESS;
+            DEBUG(4, ("Access granted for [%s]\n", service));
+            /* We still need to loop through to make sure
+             * that it's not also explicitly denied
+             */
+        } else if (strcmp("*", service) == 0) {
+            /* This user has access to all services */
+            state->pam_status = PAM_SUCCESS;
+            DEBUG(4, ("Access granted to all services\n"));
+            /* We still need to loop through to make sure
+             * that it's not also explicitly denied
+             */
+        }
+    }
+
+    if (state->pam_status != PAM_SUCCESS) {
+        DEBUG(4, ("No matching service rule found\n"));
+    }
+
+    ret = EOK;
+
+done:
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else {
+        tevent_req_error(req, ret);
+    }
+    tevent_req_post(req, ev);
+
+    return req;
+}
+
+static errno_t sdap_access_service_recv(struct tevent_req *req,
+                                        int *pam_status)
+{
+    struct sdap_access_service_ctx *state =
+            tevent_req_data(req, struct sdap_access_service_ctx);
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    *pam_status = state->pam_status;
+
+    return EOK;
+}
+
+static void sdap_access_service_done(struct tevent_req *subreq)
+{
+    errno_t ret;
+    struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req);
+    struct sdap_access_req_ctx *state =
+            tevent_req_data(req, struct sdap_access_req_ctx);
+
+    ret = sdap_access_service_recv(subreq, &state->pam_status);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        DEBUG(1, ("Error retrieving access check result.\n"));
+        state->pam_status = PAM_SYSTEM_ERR;
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    next_access_rule(req);
+
+    return;
+}
+
 static errno_t sdap_access_recv(struct tevent_req *req, int *pam_status)
 {
     struct sdap_access_req_ctx *state =
diff --git a/src/providers/ldap/sdap_access.h b/src/providers/ldap/sdap_access.h
index 9b8e45bddd6d98bcabf13ab30a7697049b06b968..66193a6ddab01222311c40cc7e0da5c103bdc78b 100644
--- a/src/providers/ldap/sdap_access.h
+++ b/src/providers/ldap/sdap_access.h
@@ -31,6 +31,7 @@
 
 #define LDAP_ACCESS_FILTER_NAME "filter"
 #define LDAP_ACCESS_EXPIRE_NAME "expire"
+#define LDAP_ACCESS_SERVICE_NAME "authorized_service"
 
 #define LDAP_ACCOUNT_EXPIRE_SHADOW "shadow"
 
@@ -38,6 +39,7 @@ enum ldap_access_rule {
     LDAP_ACCESS_EMPTY = -1,
     LDAP_ACCESS_FILTER = 0,
     LDAP_ACCESS_EXPIRE,
+    LDAP_ACCESS_SERVICE,
     LDAP_ACCESS_LAST
 };
 
-- 
1.7.3.3

