s3-winbindd: Add a hook to check if a user in an admin-specified OU master-ou-restrictions
authorAndrew Bartlett <abartlet@samba.org>
Thu, 23 Feb 2012 02:59:31 +0000 (13:59 +1100)
committerAndrew Bartlett <abartlet@samba.org>
Fri, 24 Feb 2012 00:06:05 +0000 (11:06 +1100)
This allows an easy to admin restriction on which users may access the
server, based on the OU membership.

Andrew Bartlett

24 files changed:
nsswitch/libwbclient/wbc_pam.c
nsswitch/libwbclient/wbclient.h
nsswitch/pam_winbind.c
nsswitch/wbinfo.c
nsswitch/winbind_struct_protocol.h
source3/Makefile.in
source3/include/proto.h
source3/librpc/idl/wbint.idl
source3/param/loadparm.c
source3/selftest/tests.py
source3/winbindd/wb_getpwsid.c
source3/winbindd/wb_queryuser.c
source3/winbindd/winbindd.c
source3/winbindd/winbindd.h
source3/winbindd/winbindd_ads.c
source3/winbindd/winbindd_cache.c
source3/winbindd/winbindd_dual_srv.c
source3/winbindd/winbindd_msrpc.c
source3/winbindd/winbindd_pam_account.c [new file with mode: 0644]
source3/winbindd/winbindd_proto.h
source3/winbindd/winbindd_reconnect.c
source3/winbindd/winbindd_rpc.c
source3/winbindd/winbindd_samr.c
source3/wscript_build

index 0aa180ca05c4f8b9e8ad19d7f0b4fbc304809df7..b51f10760f3e1815b2ced5315cfaed4baed981f8 100644 (file)
@@ -1291,3 +1291,36 @@ wbcErr wbcCredentialSave(const char *user, const char *password)
 
        return wbcRequestResponse(WINBINDD_CCACHE_SAVE, &request, &response);
 }
+
+
+/* Confirm the user is permitted to login by checkign with winbindd */
+wbcErr wbcCheckAccount(const char *user,
+                      struct wbcAuthErrorInfo **error)
+{
+       wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+       struct winbindd_request request;
+       struct winbindd_response response;
+
+       ZERO_STRUCT(request);
+       ZERO_STRUCT(response);
+
+       strncpy(request.data.username, user,
+               sizeof(request.data.username)-1);
+
+       wbc_status = wbcRequestResponse(WINBINDD_PAM_ACCOUNT, &request, &response);
+
+       if (response.data.auth.nt_status != 0) {
+               if (error) {
+                       wbc_status = wbc_create_error_info(&response,
+                                                          error);
+                       BAIL_ON_WBC_ERROR(wbc_status);
+               }
+
+               wbc_status = WBC_ERR_AUTH_ERROR;
+               BAIL_ON_WBC_ERROR(wbc_status);
+       }
+       BAIL_ON_WBC_ERROR(wbc_status);
+
+done:
+       return wbc_status;
+}
index 809e00a0076cdbe0e38248721d2854d53143aaa1..018a5353bb4fdb246de6056181aae7e95d297a45 100644 (file)
@@ -1262,6 +1262,17 @@ wbcErr wbcCredentialCache(struct wbcCredentialCacheParams *params,
  **/
 wbcErr wbcCredentialSave(const char *user, const char *password);
 
+/**
+ * @brief Confirm the user is permitted to login by checking with winbindd
+ *
+ * @param *user             Username
+ *
+ * @return #wbcErr
+ **/
+wbcErr wbcCheckAccount(const char *user,
+                      struct wbcAuthErrorInfo **error);
+
+
 /**********************************************************
  * Resolve functions
  **********************************************************/
index 831fa844626702573e16f348149f3e4b296020dc..c67b44c33730dd76e68c4f7c9798301cf7e8cd2b 100644 (file)
@@ -2034,10 +2034,8 @@ static int winbind_chauthtok_request(struct pwb_context *ctx,
 /*
  * Checks if a user has an account
  *
- * return values:
- *      1  = User not found
- *      0  = OK
- *     -1  = System error
+ * return values: PAM errors
+ *
  */
 static int valid_user(struct pwb_context *ctx,
                      const char *user)
@@ -2046,31 +2044,27 @@ static int valid_user(struct pwb_context *ctx,
         * sure it's really a winbind user, this is important when stacking PAM
         * modules in the 'account' or 'password' facility. */
 
+       int ret;
        wbcErr wbc_status;
        struct passwd *pwd = NULL;
-       struct passwd *wb_pwd = NULL;
+       struct wbcAuthErrorInfo *error = NULL;
 
        pwd = getpwnam(user);
        if (pwd == NULL) {
                return 1;
        }
 
-       wbc_status = wbcGetpwnam(user, &wb_pwd);
-       wbcFreeMemory(wb_pwd);
+       wbc_status = wbcCheckAccount(user, &error);
        if (!WBC_ERROR_IS_OK(wbc_status)) {
-               _pam_log(ctx, LOG_DEBUG, "valid_user: wbcGetpwnam gave %s\n",
+               _pam_log(ctx, LOG_DEBUG, "valid_user: wbcCheckAccount gave %s\n",
                        wbcErrorString(wbc_status));
        }
 
-       switch (wbc_status) {
-               case WBC_ERR_UNKNOWN_USER:
-                       return 1;
-               case WBC_ERR_SUCCESS:
-                       return 0;
-               default:
-                       break;
-       }
-       return -1;
+       ret = wbc_auth_error_to_pam_error(ctx, error, wbc_status,
+                                         user, "wbcCheckAccount");
+       
+       wbcFreeMemory(error);
+       return ret;
 }
 
 static char *_pam_delete(register char *xx)
@@ -3039,16 +3033,6 @@ int pam_sm_chauthtok(pam_handle_t * pamh, int flags,
 
        /* check if this is really a user in winbindd, not only in NSS */
        ret = valid_user(ctx, user);
-       switch (ret) {
-               case 1:
-                       ret = PAM_USER_UNKNOWN;
-                       goto out;
-               case -1:
-                       ret = PAM_SYSTEM_ERR;
-                       goto out;
-               default:
-                       break;
-       }
 
        /*
         * obtain and verify the current password (OLDAUTHTOK) for
index e7f902f08b160dba87e0fae8a43ae3e4f9258d16..0573414d72262c308d62e8ab0a943a3a452a001b 100644 (file)
@@ -1794,6 +1794,37 @@ static bool wbinfo_pam_logon(char *username)
        return true;
 }
 
+static bool wbinfo_check_account(const char *account)
+{
+       wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
+       struct wbcAuthErrorInfo *error = NULL;
+
+       if (!account) {
+               d_printf("checking the account requires an argument\n");
+               return false;
+       }
+
+       wbc_status = wbcCheckAccount(account, &error);
+
+       d_printf("checking the account %s as permitted to log in %s\n",
+               account,
+               WBC_ERROR_IS_OK(wbc_status) ? "succeeded" : "failed");
+
+       if (wbc_status == WBC_ERR_AUTH_ERROR) {
+               d_fprintf(stderr,
+                        "error code was %s (0x%x)\nerror messsage was: %s\n",
+                        error->nt_string,
+                        error->nt_status,
+                        error->display_string);
+               wbcFreeMemory(error);
+       }
+       if (!WBC_ERROR_IS_OK(wbc_status)) {
+               return false;
+       }
+
+       return true;
+}
+
 /* Save creds with winbind */
 
 static bool wbinfo_ccache_save(char *username)
@@ -2048,7 +2079,8 @@ enum {
        OPT_LOGOFF,
        OPT_LOGOFF_USER,
        OPT_LOGOFF_UID,
-       OPT_LANMAN
+       OPT_LANMAN, 
+       OPT_CHECK_ACCOUNT
 };
 
 int main(int argc, char **argv, char **envp)
@@ -2154,6 +2186,7 @@ int main(int argc, char **argv, char **envp)
                { "change-user-password", 0, POPT_ARG_STRING, &string_arg, OPT_CHANGE_USER_PASSWORD, "Change the password for a user", NULL },
                { "ntlmv2", 0, POPT_ARG_NONE, 0, OPT_NTLMV2, "Use NTLMv2 cryptography for user authentication", NULL},
                { "lanman", 0, POPT_ARG_NONE, 0, OPT_LANMAN, "Use lanman cryptography for user authentication", NULL},
+               { "check-account", 0, POPT_ARG_STRING, &string_arg, OPT_CHECK_ACCOUNT, "Check if nominated account is permitted to log in", NULL},
                POPT_COMMON_VERSION
                POPT_TABLEEND
        };
@@ -2605,6 +2638,11 @@ int main(int argc, char **argv, char **envp)
                case OPT_LOGOFF_USER:
                case OPT_LOGOFF_UID:
                        break;
+               case OPT_CHECK_ACCOUNT:
+                       if (!wbinfo_check_account(string_arg)) {
+                               goto done;
+                       }
+                       break;
                default:
                        d_fprintf(stderr, "Invalid option\n");
                        poptPrintHelp(pc, stderr, 0);
index e5ed8e1b3a148de71bc5fb770536973a2e9bb0ab..0e82b87adfd4a9048979ea495ede498a0b130e97 100644 (file)
@@ -56,8 +56,9 @@ typedef char fstring[FSTRING_LEN];
  *     removed WINBINDD_REMOVE_MAPPING
  * 26: added WINBINDD_DC_INFO
  * 27: added WINBINDD_LOOKUPSIDS
+ * 28: added WINBINDD_PAM_ACCOUNT
  */
-#define WINBIND_INTERFACE_VERSION 27
+#define WINBIND_INTERFACE_VERSION 28
 
 /* Have to deal with time_t being 4 or 8 bytes due to structure alignment.
    On a 64bit Linux box, we have to support a constant structure size
@@ -97,6 +98,7 @@ enum winbindd_cmd {
        WINBINDD_PAM_CHAUTHTOK,
        WINBINDD_PAM_LOGOFF,
        WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP,
+       WINBINDD_PAM_ACCOUNT,
 
        /* List various things */
 
index 0a189b54ddf74fbec8dd5f84158675c3cf57b764..39b2d013bbe7571ebfe0471807fec4b63a409a61 100644 (file)
@@ -1382,6 +1382,7 @@ WINBINDD_OBJ1 = \
                winbindd/winbindd_util.o  \
                winbindd/winbindd_cache.o \
                winbindd/winbindd_pam.o   \
+               winbindd/winbindd_pam_account.o   \
                winbindd/winbindd_misc.o  \
                winbindd/winbindd_cm.o    \
                winbindd/winbindd_wins_byip.o  \
index 30fc21615d475cc5859d6c6efeb88d2891f59ba8..f3c95af9feffdbdae9b93c7e4a7e0e41e9e4adf8 100644 (file)
@@ -1125,6 +1125,7 @@ const char *lp_netbios_name(void);
 const char *lp_workgroup(void);
 const char *lp_realm(void);
 const char *lp_dnsdomain(void);
+const char *lp_orgunit(void);
 const char *lp_afs_username_map(void);
 int lp_afs_token_lifetime(void);
 char *lp_log_nt_token_command(void);
index cb8a4e42a358a61404d43bc3da5674773b2c4234..cc739cf2f2dfaf1590658ceaf239715947ad5471 100644 (file)
@@ -94,10 +94,12 @@ interface wbint
        hyper primary_gid;
        dom_sid user_sid;
        dom_sid group_sid;
+       [string,charset(UTF8)] char *dn;
     } wbint_userinfo;
 
     NTSTATUS wbint_QueryUser(
        [in] dom_sid *sid,
+       [in] uint32 flags,
        [out] wbint_userinfo *info
        );
 
index 20a072d67f87066d1792bf1235501a0c219c5e72..51cd898761f2e4c421934946d1470d1015318789 100644 (file)
@@ -3495,6 +3495,15 @@ static struct parm_struct parm_table[] = {
                .flags          = FLAG_ADVANCED,
        },
 #endif
+       {
+               .label          = "orgunit",
+               .type           = P_USTRING,
+               .p_class        = P_GLOBAL,
+               .offset         = GLOBAL_VAR(szOrgUnit),
+               .special        = NULL,
+               .enum_list      = NULL,
+               .flags          = FLAG_ADVANCED,
+       },
        {
                .label          = "default service",
                .type           = P_STRING,
@@ -5093,6 +5102,7 @@ FN_GLOBAL_CONST_STRING(lp_netbios_name, szNetbiosName)
 FN_GLOBAL_CONST_STRING(lp_netbios_scope, szNetbiosScope)
 FN_GLOBAL_CONST_STRING(lp_realm, szRealmUpper)
 FN_GLOBAL_CONST_STRING(lp_dnsdomain, szDnsDomain)
+FN_GLOBAL_CONST_STRING(lp_orgunit, szOrgUnit)
 FN_GLOBAL_CONST_STRING(lp_afs_username_map, szAfsUsernameMap)
 FN_GLOBAL_INTEGER(lp_afs_token_lifetime, iAfsTokenLifetime)
 FN_GLOBAL_STRING(lp_log_nt_token_command, szLogNtTokenCommand)
index 3f8dcc1f564449eb0251a62d7de0e8bea8f94c10..e90f7175bbf3d444449a7e373bec521d5d25f3df 100755 (executable)
@@ -117,6 +117,7 @@ tests=["--ping", "--separator",
        #Didn't pass yet# "--domain-users",
        "--domain-groups",
        "--name-to-sid=$DC_USERNAME",
+       "--check-account=$DOMAIN\\\\$DC_USERNAME",
        "--name-to-sid=$DOMAIN\\\\$DC_USERNAME",
      #Didn't pass yet# "--user-info=$USERNAME",
        "--user-groups=$DOMAIN\\\\$DC_USERNAME",
index ef54ee5f48e9a0ef28c9e09eb85b2469369edd7c..02e5f40b7122a739e7a61ff1f390d9b5d3b410ee 100644 (file)
@@ -49,7 +49,7 @@ struct tevent_req *wb_getpwsid_send(TALLOC_CTX *mem_ctx,
        state->ev = ev;
        state->pw = pw;
 
-       subreq = wb_queryuser_send(state, ev, &state->sid);
+       subreq = wb_queryuser_send(state, ev, &state->sid, 0);
        if (tevent_req_nomem(subreq, req)) {
                return tevent_req_post(req, ev);
        }
index 33416b9017b2462fb9ee7d6174055330d79fc014..22df4e308eaedd52cbeb8cbc25c25c568b565733 100644 (file)
@@ -31,7 +31,8 @@ static void wb_queryuser_done(struct tevent_req *subreq);
 
 struct tevent_req *wb_queryuser_send(TALLOC_CTX *mem_ctx,
                                     struct tevent_context *ev,
-                                    const struct dom_sid *user_sid)
+                                    const struct dom_sid *user_sid, 
+                                    uint32_t flags)
 {
        struct tevent_req *req, *subreq;
        struct wb_queryuser_state *state;
@@ -55,7 +56,7 @@ struct tevent_req *wb_queryuser_send(TALLOC_CTX *mem_ctx,
        }
 
        subreq = dcerpc_wbint_QueryUser_send(state, ev, dom_child_handle(domain),
-                                            &state->sid, state->info);
+                                            &state->sid, flags, state->info);
        if (tevent_req_nomem(subreq, req)) {
                return tevent_req_post(req, ev);
        }
index 5e23859b4059d14f36e51641f5bccefd0341141d..e65af876bbf551368f4f3b0f37a2592cec5172ba 100644 (file)
@@ -540,6 +540,8 @@ static struct winbindd_async_dispatch_table async_nonpriv_table[] = {
        { WINBINDD_PAM_CHNG_PSWD_AUTH_CRAP, "PAM_CHNG_PSWD_AUTH_CRAP",
          winbindd_pam_chng_pswd_auth_crap_send,
          winbindd_pam_chng_pswd_auth_crap_recv },
+       { WINBINDD_PAM_ACCOUNT, "PAM_ACCOUNT",
+         winbindd_pam_account_send, winbindd_pam_account_recv },
        { WINBINDD_WINS_BYIP, "WINS_BYIP",
          winbindd_wins_byip_send, winbindd_wins_byip_recv },
        { WINBINDD_WINS_BYNAME, "WINS_BYNAME",
index 33c7bbe5c684f886fd87ecb061387115c0df93e0..abb101146fdfe6ef251f05514d3fb47c54d396ec 100644 (file)
@@ -226,6 +226,8 @@ struct wb_acct_info {
        uint32_t rid; /* domain-relative RID */
 };
 
+#define WB_QUERY_USER_FLAG_MUST_DN 1
+
 /* per-domain methods. This is how LDAP vs RPC is selected
  */
 struct winbindd_methods {
@@ -281,6 +283,7 @@ struct winbindd_methods {
        NTSTATUS (*query_user)(struct winbindd_domain *domain, 
                               TALLOC_CTX *mem_ctx, 
                               const struct dom_sid *user_sid,
+                              uint32_t flags,
                               struct wbint_userinfo *user_info);
 
        /* lookup all groups that a user is a member of. The backend
index 610db7a62b62e66a61f6056ab01988157297bc66..6aec092998eabeef474b2af8b537a240a8e5e6de 100644 (file)
@@ -466,6 +466,7 @@ static NTSTATUS rids_to_names(struct winbindd_domain *domain,
 static NTSTATUS query_user(struct winbindd_domain *domain, 
                           TALLOC_CTX *mem_ctx, 
                           const struct dom_sid *sid,
+                          uint32_t flags,
                           struct wbint_userinfo *info)
 {
        ADS_STRUCT *ads = NULL;
@@ -514,6 +515,13 @@ static NTSTATUS query_user(struct winbindd_domain *domain,
                DEBUG(8,("query_user: No incoming trust from domain %s\n",
                         domain->name));
 
+               if (flags & WB_QUERY_USER_FLAG_MUST_DN) {
+                       DEBUG(1,("query_user: No incoming trust from domain %s and DN required by caller\n",
+                                domain->name));
+                       
+                       return NT_STATUS_INVALID_DOMAIN_STATE;
+               }
+
                /* We still need to generate some basic information
                   about the user even if we cannot contact the 
                   domain.  Most of this stuff we can deduce. */
@@ -582,6 +590,8 @@ static NTSTATUS query_user(struct winbindd_domain *domain,
         */
        ads_name = ads_pull_string(ads, mem_ctx, msg, "name");
 
+       info->dn = ads_get_dn(ads, mem_ctx, msg);
+
        ads_msgfree(ads, msg);
        msg = NULL;
 
index 315202d618163b99499b41782c3a60bd2f9719a5..cfd2e2d923f42c72b407b03119980c8041d12db5 100644 (file)
@@ -996,6 +996,7 @@ static void wcache_save_user(struct winbindd_domain *domain, NTSTATUS status,
        centry_put_uint32(centry, info->primary_gid);
        centry_put_sid(centry, &info->user_sid);
        centry_put_sid(centry, &info->group_sid);
+       centry_put_string(centry, info->dn);
        centry_end(centry, "U/%s", sid_to_fstring(sid_string,
                                                  &info->user_sid));
        DEBUG(10,("wcache_save_user: %s (acct_name %s)\n", sid_string, info->acct_name));
@@ -1445,6 +1446,7 @@ do_fetch_cache:
                (*info)[i].shell = centry_string(centry, mem_ctx);
                centry_sid(centry, &(*info)[i].user_sid);
                centry_sid(centry, &(*info)[i].group_sid);
+               (*info)[i].dn = centry_string(centry, mem_ctx);
        }
 
 do_cached:     
@@ -1538,6 +1540,7 @@ do_query:
                centry_put_string(centry, (*info)[i].shell);
                centry_put_sid(centry, &(*info)[i].user_sid);
                centry_put_sid(centry, &(*info)[i].group_sid);
+               centry_put_string(centry, (*info)[i].dn);
                if (domain->backend && domain->backend->consistent) {
                        /* when the backend is consistent we can pre-prime some mappings */
                        wcache_save_name_to_sid(domain, NT_STATUS_OK, 
@@ -2202,6 +2205,7 @@ static NTSTATUS rids_to_names(struct winbindd_domain *domain,
 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
                           TALLOC_CTX *mem_ctx,
                           const struct dom_sid *user_sid,
+                          uint32_t flags,
                           struct wbint_userinfo *info)
 {
        struct winbind_cache *cache = get_cache(domain);
@@ -2250,6 +2254,12 @@ NTSTATUS wcache_query_user(struct winbindd_domain *domain,
                info->primary_gid = centry_uint32(centry);
                centry_sid(centry, &info->user_sid);
                centry_sid(centry, &info->group_sid);
+               info->dn = centry_string(centry, mem_ctx);
+
+               if (info->dn == NULL && (flags & WB_QUERY_USER_FLAG_MUST_DN)) {
+                       /* If the caller said they must get the DN, then we must not return an entry without it */
+                       status = NT_STATUS_NOT_FOUND;
+               }
        }
 
        DEBUG(10,("query_user: [Cached] - cached info for domain %s status: "
@@ -2263,13 +2273,14 @@ NTSTATUS wcache_query_user(struct winbindd_domain *domain,
 static NTSTATUS query_user(struct winbindd_domain *domain,
                           TALLOC_CTX *mem_ctx,
                           const struct dom_sid *user_sid,
+                          uint32_t flags,
                           struct wbint_userinfo *info)
 {
        NTSTATUS status;
        bool old_status;
 
        old_status = domain->online;
-       status = wcache_query_user(domain, mem_ctx, user_sid, info);
+       status = wcache_query_user(domain, mem_ctx, user_sid, flags, info);
        if (!NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
                return status;
        }
@@ -2284,7 +2295,7 @@ static NTSTATUS query_user(struct winbindd_domain *domain,
        DEBUG(10,("query_user: [Cached] - doing backend query for info for domain %s\n",
                domain->name ));
 
-       status = domain->backend->query_user(domain, mem_ctx, user_sid, info);
+       status = domain->backend->query_user(domain, mem_ctx, user_sid, flags, info);
 
        if (NT_STATUS_EQUAL(status, NT_STATUS_IO_TIMEOUT) ||
                NT_STATUS_EQUAL(status, NT_STATUS_DOMAIN_CONTROLLER_NOT_FOUND)) {
@@ -2295,7 +2306,7 @@ static NTSTATUS query_user(struct winbindd_domain *domain,
                        !domain->online &&
                        old_status) {
                        NTSTATUS cache_status;
-                       cache_status = wcache_query_user(domain, mem_ctx, user_sid, info);
+                       cache_status = wcache_query_user(domain, mem_ctx, user_sid, flags, info);
                        return cache_status;
                }
        }
@@ -3609,6 +3620,7 @@ static int validate_u(TALLOC_CTX *mem_ctx, const char *keystr, TDB_DATA dbuf,
        (void)centry_uint32(centry);
        (void)centry_sid(centry, &sid);
        (void)centry_sid(centry, &sid);
+       (void)centry_string(centry, mem_ctx);
 
        centry_free(centry);
 
index e9a964b70b7856e28b3b5f2baeb0550bff54d688..46042174527682d5b94ac0e77356ee98fb9f7796 100644 (file)
@@ -276,6 +276,7 @@ NTSTATUS _wbint_QueryUser(struct pipes_struct *p, struct wbint_QueryUser *r)
        }
 
        status = domain->methods->query_user(domain, p->mem_ctx, r->in.sid,
+                                            r->in.flags,
                                             r->out.info);
        reset_cm_connection_on_error(domain, status);
        return status;
index 455de3d54c3b9be5250e126a3690ef9f7dab4f86..18ccf8fa43d9b240d54ab3ebc5a461b1495f2d6d 100644 (file)
@@ -401,9 +401,10 @@ static NTSTATUS msrpc_rids_to_names(struct winbindd_domain *domain,
 
 /* Lookup user information from a rid or username. */
 static NTSTATUS msrpc_query_user(struct winbindd_domain *domain,
-                          TALLOC_CTX *mem_ctx, 
-                          const struct dom_sid *user_sid,
-                          struct wbint_userinfo *user_info)
+                                TALLOC_CTX *mem_ctx, 
+                                const struct dom_sid *user_sid,
+                                uint32_t flags,
+                                struct wbint_userinfo *user_info)
 {
        struct rpc_pipe_client *samr_pipe;
        struct policy_handle dom_pol;
@@ -413,6 +414,13 @@ static NTSTATUS msrpc_query_user(struct winbindd_domain *domain,
 
        DEBUG(3,("msrpc_query_user sid=%s\n", sid_string_dbg(user_sid)));
 
+       if (flags & WB_QUERY_USER_FLAG_MUST_DN) {
+               DEBUG(1,("query_user: Domain %s is not ADS and DN required by caller\n",
+                        domain->name));
+               
+               return NT_STATUS_INVALID_DOMAIN_STATE;
+       }
+
        tmp_ctx = talloc_stackframe();
        if (tmp_ctx == NULL) {
                return NT_STATUS_NO_MEMORY;
diff --git a/source3/winbindd/winbindd_pam_account.c b/source3/winbindd/winbindd_pam_account.c
new file mode 100644 (file)
index 0000000..0ea85b8
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+   Unix SMB/CIFS implementation.
+   async implementation of WINBINDD_PAM_ACCOUNT
+   Copyright (C) Volker Lendecke 2009
+   Copyright (C) Andrew Bartlett 2012
+
+   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 "includes.h"
+#include "winbindd.h"
+#include <ldb.h>
+#include "include/ads.h"
+#include "passdb/lookup_sid.h" /* only for LOOKUP_NAME_NO_NSS flag */
+
+struct winbindd_pam_account_state {
+       struct tevent_context *ev;
+       fstring domname;
+       fstring username;
+       struct dom_sid sid;
+       enum lsa_SidType type;
+       struct wbint_userinfo *userinfo;
+};
+
+static void winbindd_pam_account_lookupname_done(struct tevent_req *subreq);
+static void winbindd_pam_account_done(struct tevent_req *subreq);
+
+struct tevent_req *winbindd_pam_account_send(TALLOC_CTX *mem_ctx,
+                                         struct tevent_context *ev,
+                                         struct winbindd_cli_state *cli,
+                                         struct winbindd_request *request)
+{
+       struct tevent_req *req, *subreq;
+       struct winbindd_pam_account_state *state;
+       char *domuser, *mapped_user;
+       NTSTATUS status;
+
+       req = tevent_req_create(mem_ctx, &state,
+                               struct winbindd_pam_account_state);
+       if (req == NULL) {
+               return NULL;
+       }
+       state->ev = ev;
+
+       /* Ensure null termination */
+       request->data.username[sizeof(request->data.username)-1]='\0';
+
+       DEBUG(3, ("pam_account %s\n", request->data.username));
+
+       domuser = request->data.username;
+
+       status = normalize_name_unmap(state, domuser, &mapped_user);
+
+       if (NT_STATUS_IS_OK(status)
+           || NT_STATUS_EQUAL(status, NT_STATUS_FILE_RENAMED)) {
+               /* normalize_name_unmapped did something */
+               domuser = mapped_user;
+       }
+
+       if (!parse_domain_user(domuser, state->domname, state->username)) {
+               DEBUG(5, ("Could not parse domain user: %s\n", domuser));
+               return tevent_req_post(req, ev);
+       }
+
+       subreq = wb_lookupname_send(state, ev, state->domname, state->username,
+                                   LOOKUP_NAME_NO_NSS);
+       if (tevent_req_nomem(subreq, req)) {
+               return tevent_req_post(req, ev);
+       }
+       tevent_req_set_callback(subreq, winbindd_pam_account_lookupname_done,
+                               req);
+       return req;
+}
+
+static void winbindd_pam_account_lookupname_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct winbindd_pam_account_state *state = tevent_req_data(
+               req, struct winbindd_pam_account_state);
+       NTSTATUS status;
+       uint32_t flags = 0;
+
+       const char *orgunit = lp_orgunit();
+
+       if (orgunit && *orgunit && strequal(state->domname, lp_workgroup())) {
+               flags = WB_QUERY_USER_FLAG_MUST_DN;
+       }
+
+       status = wb_lookupname_recv(subreq, &state->sid, &state->type);
+       TALLOC_FREE(subreq);
+       if (!NT_STATUS_IS_OK(status)) {
+               tevent_req_nterror(req, status);
+               return;
+       }
+
+       subreq = wb_queryuser_send(state, state->ev, &state->sid, flags);
+       if (tevent_req_nomem(subreq, req)) {
+               return;
+       }
+       tevent_req_set_callback(subreq, winbindd_pam_account_done, req);
+}
+
+static void winbindd_pam_account_done(struct tevent_req *subreq)
+{
+       struct tevent_req *req = tevent_req_callback_data(
+               subreq, struct tevent_req);
+       struct winbindd_pam_account_state *state = tevent_req_data(
+               req, struct winbindd_pam_account_state);
+       NTSTATUS status;
+
+       status = wb_queryuser_recv(subreq, state, &state->userinfo);
+       TALLOC_FREE(subreq);
+       if (!NT_STATUS_IS_OK(status)) {
+               tevent_req_nterror(req, status);
+               return;
+       }
+
+       tevent_req_done(req);
+}
+
+static NTSTATUS winbindd_pam_account_recv_helper(struct tevent_req *req,
+                                         struct winbindd_response *response)
+{
+       struct ldb_dn *account_dn;
+       struct ldb_dn *orgunit_dn;
+       char *domain_dn_str;
+       struct ldb_context *ldb;
+       const char *orgunit = lp_orgunit();
+
+       struct winbindd_pam_account_state *state = tevent_req_data(
+               req, struct winbindd_pam_account_state);
+       NTSTATUS status;
+
+       if (tevent_req_is_nterror(req, &status)) {
+               DEBUG(5, ("Could not check DN for user %s\\%s: %s\n",
+                         state->domname, state->username, nt_errstr(status)));
+               return status;
+       }
+
+       if (orgunit == NULL || *orgunit == '\0') {
+               DEBUG(7,("winbindd_pam_account: no orgunit restriction specified -- "
+                        "allowing pam_account() for %s\\%s.\n",
+                        state->domname, state->username));
+               return NT_STATUS_OK;
+       }
+
+       if (strequal(state->domname, lp_netbios_name())) {
+               DEBUG(7,("winbindd_pam_account: local SAM domain -- "
+                        "allowing pam_account() for %s\\%s.\n",
+                        state->domname, state->username));
+               return NT_STATUS_OK;
+       }
+
+       if (!strequal(state->domname, lp_workgroup())) {
+               DEBUG(7,("winbindd_pam_account: Not primary domain -- "
+                        "rejecting pam_account() for %s\\%s.\n",
+                        state->domname, state->username));
+               return NT_STATUS_ACCESS_DENIED;
+       }
+
+       ldb = ldb_init(NULL, NULL);
+
+       /* Finally do the OU check */
+       domain_dn_str = ads_build_dn(lp_realm());
+
+       if (domain_dn_str == NULL) {
+               talloc_free(ldb);
+               return NT_STATUS_NO_MEMORY;
+       }
+       
+       orgunit_dn = ldb_dn_new(ldb, ldb, domain_dn_str);
+
+       free(domain_dn_str);
+
+       account_dn = ldb_dn_new(ldb, ldb, state->userinfo->dn);
+       if (!account_dn) {
+               talloc_free(ldb);
+               return NT_STATUS_NO_MEMORY;
+       }
+       /* For now, this only handles one level of org unit */
+       if (!ldb_dn_add_child_fmt(orgunit_dn, "ou=%s", orgunit)) {
+               talloc_free(ldb);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       /* FINALLY, we can check if the user is in that OU! */
+       
+       if (ldb_dn_compare_base(orgunit_dn, account_dn) == 0) {
+               DEBUG(7, ("User %s allowed to log in, as they are within %s\n", 
+                         ldb_dn_get_linearized(account_dn),
+                         ldb_dn_get_linearized(orgunit_dn)));
+               return NT_STATUS_OK;
+       }
+       
+       DEBUG(2, ("User %s not allowed to log in, as they are not within %s\n", 
+                 ldb_dn_get_linearized(account_dn),
+                 ldb_dn_get_linearized(orgunit_dn)));
+       return NT_STATUS_ACCESS_DENIED;
+}
+
+NTSTATUS winbindd_pam_account_recv(struct tevent_req *req,
+                                  struct winbindd_response *response)
+{
+       NTSTATUS status = winbindd_pam_account_recv_helper(req, response);
+       set_auth_errors(response, status);
+       return status;
+}
index b965fdaf1d63201db19320272570605e961471af..10f279a025446124ffe610f9296927b9abab21ba 100644 (file)
@@ -102,6 +102,7 @@ NTSTATUS wcache_name_to_sid(struct winbindd_domain *domain,
 NTSTATUS wcache_query_user(struct winbindd_domain *domain,
                           TALLOC_CTX *mem_ctx,
                           const struct dom_sid *user_sid,
+                          uint32_t flags,
                           struct wbint_userinfo *info);
 NTSTATUS wcache_lookup_useraliases(struct winbindd_domain *domain,
                                   TALLOC_CTX *mem_ctx,
@@ -364,6 +365,10 @@ enum winbindd_result winbindd_dual_pam_logoff(struct winbindd_domain *domain,
                                              struct winbindd_cli_state *state) ;
 enum winbindd_result winbindd_dual_pam_chng_pswd_auth_crap(struct winbindd_domain *domainSt, struct winbindd_cli_state *state);
 
+void winbindd_pam_account(struct winbindd_cli_state *state);
+enum winbindd_result winbindd_dual_pam_account(struct winbindd_domain *domain,
+                                              struct winbindd_cli_state *state) ;
+
 /* The following definitions come from winbindd/winbindd_util.c  */
 
 struct winbindd_domain *domain_list(void);
@@ -545,7 +550,8 @@ NTSTATUS winbindd_allocate_gid_recv(struct tevent_req *req,
 
 struct tevent_req *wb_queryuser_send(TALLOC_CTX *mem_ctx,
                                     struct tevent_context *ev,
-                                    const struct dom_sid *user_sid);
+                                    const struct dom_sid *user_sid, 
+                                    uint32_t flags);
 NTSTATUS wb_queryuser_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
                           struct wbint_userinfo **pinfo);
 
@@ -874,4 +880,11 @@ NTSTATUS open_internal_samr_conn(TALLOC_CTX *mem_ctx,
                                 struct rpc_pipe_client **samr_pipe,
                                 struct policy_handle *samr_domain_hnd);
 
+struct tevent_req *winbindd_pam_account_send(TALLOC_CTX *mem_ctx,
+                                         struct tevent_context *ev,
+                                         struct winbindd_cli_state *cli,
+                                         struct winbindd_request *request);
+NTSTATUS winbindd_pam_account_recv(struct tevent_req *req,
+                               struct winbindd_response *response);
+
 #endif /*  _WINBINDD_PROTO_H_  */
index 62bf57ea4add35b04eb2e51f9eb9ea9b77662698..eb3aa69952a6f6bbe02e5fc51db4335dd76e79c5 100644 (file)
@@ -197,16 +197,18 @@ static NTSTATUS rids_to_names(struct winbindd_domain *domain,
 static NTSTATUS query_user(struct winbindd_domain *domain, 
                           TALLOC_CTX *mem_ctx, 
                           const struct dom_sid *user_sid,
+                          uint32_t flags,
                           struct wbint_userinfo *user_info)
 {
        NTSTATUS result;
 
        result = msrpc_methods.query_user(domain, mem_ctx, user_sid,
+                                         flags, 
                                          user_info);
 
        if (reconnect_need_retry(result))
                result = msrpc_methods.query_user(domain, mem_ctx, user_sid,
-                                                 user_info);
+                                                 flags, user_info);
 
        return result;
 }
index 8a11cb24207222907eb0f6512e809e2e554eb2ea..b552c8cad743e930b70ce1722d8088d7ffcb0e60 100644 (file)
@@ -120,6 +120,7 @@ NTSTATUS rpc_query_user_list(TALLOC_CTX *mem_ctx,
 
                        dst->homedir = NULL;
                        dst->shell = NULL;
+                       dst->dn = NULL;
 
                        sid_compose(&dst->user_sid, domain_sid, rid);
 
@@ -533,6 +534,7 @@ NTSTATUS rpc_query_user(TALLOC_CTX *mem_ctx,
 
        user_info->homedir = NULL;
        user_info->shell = NULL;
+       user_info->dn = NULL;
        user_info->primary_gid = (gid_t)-1;
 
        return NT_STATUS_OK;
index c03f59381e5963f14aa9d5598f7f9a898c8adbb1..ef9a9d19b17ab2227d1f559dbc1e840686b61ce7 100644 (file)
@@ -300,6 +300,7 @@ done:
 static NTSTATUS sam_query_user(struct winbindd_domain *domain,
                               TALLOC_CTX *mem_ctx,
                               const struct dom_sid *user_sid,
+                              uint32_t flags,
                               struct wbint_userinfo *user_info)
 {
        struct rpc_pipe_client *samr_pipe;
@@ -310,6 +311,13 @@ static NTSTATUS sam_query_user(struct winbindd_domain *domain,
 
        DEBUG(3,("sam_query_user\n"));
 
+       if (flags & WB_QUERY_USER_FLAG_MUST_DN) {
+               DEBUG(1,("query_user: local domain %s is not ADS and DN required by caller\n",
+                        domain->name));
+               
+               return NT_STATUS_INVALID_DOMAIN_STATE;
+       }
+
        ZERO_STRUCT(dom_pol);
 
        /* Paranoia check */
@@ -411,7 +419,7 @@ done:
 static NTSTATUS sam_lookup_groupmem(struct winbindd_domain *domain,
                                    TALLOC_CTX *mem_ctx,
                                    const struct dom_sid *group_sid,
-                                   enum lsa_SidType type,
+                           enum lsa_SidType type,
                                    uint32_t *pnum_names,
                                    struct dom_sid **psid_mem,
                                    char ***pnames,
@@ -522,9 +530,10 @@ static NTSTATUS builtin_query_user_list(struct winbindd_domain *domain,
 
 /* Lookup user information from a rid or username. */
 static NTSTATUS builtin_query_user(struct winbindd_domain *domain,
-                               TALLOC_CTX *mem_ctx,
-                               const struct dom_sid *user_sid,
-                               struct wbint_userinfo *user_info)
+                                  TALLOC_CTX *mem_ctx,
+                                  const struct dom_sid *user_sid,
+                                  uint32_t flags,
+                                  struct wbint_userinfo *user_info)
 {
        return NT_STATUS_NO_SUCH_USER;
 }
index b24090e07db576601f713c1d613cefc7e589f212..7e3aa029ff27410c4bbed82b24d24b0b5945e48b 100755 (executable)
@@ -331,6 +331,7 @@ WINBINDD_SRC1 = '''winbindd/winbindd.c
                    winbindd/winbindd_pam_auth.c
                    winbindd/winbindd_pam_logoff.c
                    winbindd/winbindd_pam_chauthtok.c
+                   winbindd/winbindd_pam_account.c
                    winbindd/winbindd_pam_auth_crap.c
                    winbindd/winbindd_pam_chng_pswd_auth_crap.c'''
 
@@ -1224,6 +1225,7 @@ bld.SAMBA3_BINARY('winbindd/winbindd',
                  RPC_NCACN_NP
                  RPC_PIPE_REGISTER
                  WB_REQTRANS
+                 ldb
                  ''',
                  enabled=bld.env.build_winbind,
                  install_path='${SBINDIR}',