s3:auth: add auth3_user_info_dc_add_hints() and auth3_session_info_create()
authorStefan Metzmacher <metze@samba.org>
Tue, 6 Mar 2018 23:21:13 +0000 (00:21 +0100)
committerRalph Boehme <slow@samba.org>
Thu, 15 Mar 2018 20:54:17 +0000 (21:54 +0100)
These functions make it possible to construct a full auth_session_info
from the information available from an auth_user_info_dc structure.

This has all the logic from create_local_token() that is used
to transform a auth_serversupplied_info to a full auth_session_info.

In order to workarround the restriction that auth_user_info_dc
doesn't contain hints for the unix token/name, we use
the special S-1-5-88 (Unix_NFS) sids:

 - S-1-5-88-1-Y gives the uid=Y
 - S-1-5-88-2-Y gives the gid=Y
 - S-1-5-88-3-Y gives flags=Y AUTH3_UNIX_HINT_*

The currently implemented flags are:

- AUTH3_UNIX_HINT_QUALIFIED_NAME
  unix_name = DOMAIN+ACCOUNT

- AUTH3_UNIX_HINT_ISLOLATED_NAME
  unix_name = ACCOUNT

- AUTH3_UNIX_HINT_DONT_TRANSLATE_FROM_SIDS
  Don't translate the nt token SIDS into uid/gids
  using sid mapping.

- AUTH3_UNIX_HINT_DONT_TRANSLATE_TO_SIDS
  Don't translate the unix token uid/gids to S-1-22-X-Y SIDS

- AUTH3_UNIX_HINT_DONT_EXPAND_UNIX_GROUPS
  The unix token won't get expanded gid values
  from getgroups_unix_user()

By using the hints it is possible to keep the current logic
where an authentication backend provides uid/gid values and
the unix name.

Note the S-1-5-88-* SIDS never appear in the final security_token.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=13328

Signed-off-by: Stefan Metzmacher <metze@samba.org>
Reviewed-by: Ralph Boehme <slow@samba.org>
source3/auth/auth_util.c
source3/auth/proto.h

index 24d2e7d8f5851e404e718b877392b0487ad60855..e146ac3f35cce40e52a704a6f79404134ed0816e 100644 (file)
@@ -665,6 +665,558 @@ NTSTATUS create_local_token(TALLOC_CTX *mem_ctx,
        return NT_STATUS_OK;
 }
 
+NTSTATUS auth3_user_info_dc_add_hints(struct auth_user_info_dc *user_info_dc,
+                                     uid_t uid,
+                                     gid_t gid,
+                                     uint32_t flags)
+{
+       uint32_t orig_num_sids = user_info_dc->num_sids;
+       struct dom_sid tmp_sid = { 0, };
+       NTSTATUS status;
+
+       /*
+        * We add S-5-88-1-X in order to pass the uid
+        * for the unix token.
+        */
+       sid_compose(&tmp_sid,
+                   &global_sid_Unix_NFS_Users,
+                   (uint32_t)uid);
+       status = add_sid_to_array_unique(user_info_dc->sids,
+                                        &tmp_sid,
+                                        &user_info_dc->sids,
+                                        &user_info_dc->num_sids);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0, ("add_sid_to_array_unique failed: %s\n",
+                         nt_errstr(status)));
+               goto fail;
+       }
+
+       /*
+        * We add S-5-88-2-X in order to pass the gid
+        * for the unix token.
+        */
+       sid_compose(&tmp_sid,
+                   &global_sid_Unix_NFS_Groups,
+                   (uint32_t)gid);
+       status = add_sid_to_array_unique(user_info_dc->sids,
+                                        &tmp_sid,
+                                        &user_info_dc->sids,
+                                        &user_info_dc->num_sids);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0, ("add_sid_to_array_unique failed: %s\n",
+                         nt_errstr(status)));
+               goto fail;
+       }
+
+       /*
+        * We add S-5-88-3-X in order to pass some flags
+        * (AUTH3_UNIX_HINT_*) to auth3_create_session_info().
+        */
+       sid_compose(&tmp_sid,
+                   &global_sid_Unix_NFS_Mode,
+                   flags);
+       status = add_sid_to_array_unique(user_info_dc->sids,
+                                        &tmp_sid,
+                                        &user_info_dc->sids,
+                                        &user_info_dc->num_sids);
+       if (!NT_STATUS_IS_OK(status)) {
+               DEBUG(0, ("add_sid_to_array_unique failed: %s\n",
+                         nt_errstr(status)));
+               goto fail;
+       }
+
+       return NT_STATUS_OK;
+
+fail:
+       user_info_dc->num_sids = orig_num_sids;
+       return status;
+}
+
+NTSTATUS auth3_session_info_create(TALLOC_CTX *mem_ctx,
+                                  const struct auth_user_info_dc *user_info_dc,
+                                  const char *original_user_name,
+                                  uint32_t session_info_flags,
+                                  struct auth_session_info **session_info_out)
+{
+       TALLOC_CTX *frame = talloc_stackframe();
+       struct auth_session_info *session_info = NULL;
+       uid_t hint_uid = -1;
+       bool found_hint_uid = false;
+       uid_t hint_gid = -1;
+       bool found_hint_gid = false;
+       uint32_t hint_flags = 0;
+       bool found_hint_flags = false;
+       bool need_getpwuid = false;
+       struct unixid *ids = NULL;
+       uint32_t num_gids = 0;
+       gid_t *gids = NULL;
+       struct dom_sid tmp_sid = { 0, };
+       fstring tmp = { 0, };
+       NTSTATUS status;
+       size_t i;
+       bool ok;
+
+       *session_info_out = NULL;
+
+       if (user_info_dc->num_sids == 0) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_INVALID_TOKEN;
+       }
+
+       if (user_info_dc->info == NULL) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_INVALID_TOKEN;
+       }
+
+       if (user_info_dc->info->account_name == NULL) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_INVALID_TOKEN;
+       }
+
+       session_info = talloc_zero(mem_ctx, struct auth_session_info);
+       if (session_info == NULL) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
+       }
+       /* keep this under frame for easier cleanup */
+       talloc_reparent(mem_ctx, frame, session_info);
+
+       session_info->info = auth_user_info_copy(session_info,
+                                                user_info_dc->info);
+       if (session_info->info == NULL) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       session_info->security_token = talloc_zero(session_info,
+                                                  struct security_token);
+       if (session_info->security_token == NULL) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       /*
+        * Avoid a lot of reallocations and allocate what we'll
+        * use in most cases.
+        */
+       session_info->security_token->sids = talloc_zero_array(
+                                               session_info->security_token,
+                                               struct dom_sid,
+                                               user_info_dc->num_sids);
+       if (session_info->security_token->sids == NULL) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       for (i = PRIMARY_USER_SID_INDEX; i < user_info_dc->num_sids; i++) {
+               struct security_token *nt_token = session_info->security_token;
+               int cmp;
+
+               /*
+                * S-1-5-88-X-Y sids are only used to give hints
+                * to the unix token construction.
+                *
+                * S-1-5-88-1-Y gives the uid=Y
+                * S-1-5-88-2-Y gives the gid=Y
+                * S-1-5-88-3-Y gives flags=Y: AUTH3_UNIX_HINT_*
+                */
+               cmp = dom_sid_compare_domain(&global_sid_Unix_NFS,
+                                            &user_info_dc->sids[i]);
+               if (cmp == 0) {
+                       bool match;
+                       uint32_t hint = 0;
+
+                       match = sid_peek_rid(&user_info_dc->sids[i], &hint);
+                       if (!match) {
+                               continue;
+                       }
+
+                       match = dom_sid_in_domain(&global_sid_Unix_NFS_Users,
+                                                 &user_info_dc->sids[i]);
+                       if (match) {
+                               if (found_hint_uid) {
+                                       TALLOC_FREE(frame);
+                                       return NT_STATUS_INVALID_TOKEN;
+                               }
+                               found_hint_uid = true;
+                               hint_uid = (uid_t)hint;
+                               continue;
+                       }
+
+                       match = dom_sid_in_domain(&global_sid_Unix_NFS_Groups,
+                                                 &user_info_dc->sids[i]);
+                       if (match) {
+                               if (found_hint_gid) {
+                                       TALLOC_FREE(frame);
+                                       return NT_STATUS_INVALID_TOKEN;
+                               }
+                               found_hint_gid = true;
+                               hint_gid = (gid_t)hint;
+                               continue;
+                       }
+
+                       match = dom_sid_in_domain(&global_sid_Unix_NFS_Mode,
+                                                 &user_info_dc->sids[i]);
+                       if (match) {
+                               if (found_hint_flags) {
+                                       TALLOC_FREE(frame);
+                                       return NT_STATUS_INVALID_TOKEN;
+                               }
+                               found_hint_flags = true;
+                               hint_flags = hint;
+                               continue;
+                       }
+
+                       continue;
+               }
+
+               status = add_sid_to_array_unique(nt_token->sids,
+                                                &user_info_dc->sids[i],
+                                                &nt_token->sids,
+                                                &nt_token->num_sids);
+               if (!NT_STATUS_IS_OK(status)) {
+                       TALLOC_FREE(frame);
+                       return status;
+               }
+       }
+
+       /*
+        * We need at least one usable SID
+        */
+       if (session_info->security_token->num_sids == 0) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_INVALID_TOKEN;
+       }
+
+       /*
+        * We need all tree hints: uid, gid, flags
+        * or none of them.
+        */
+       if (found_hint_uid || found_hint_gid || found_hint_flags) {
+               if (!found_hint_uid) {
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_INVALID_TOKEN;
+               }
+
+               if (!found_hint_gid) {
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_INVALID_TOKEN;
+               }
+
+               if (!found_hint_flags) {
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_INVALID_TOKEN;
+               }
+       }
+
+       if (session_info->info->authenticated) {
+               session_info_flags |= AUTH_SESSION_INFO_AUTHENTICATED;
+       }
+
+       status = finalize_local_nt_token(session_info->security_token,
+                                        session_info_flags);
+       if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(frame);
+               return status;
+       }
+
+       /*
+        * unless set otherwise, the session key is the user session
+        * key from the auth subsystem
+        */
+       if (user_info_dc->user_session_key.length != 0) {
+               session_info->session_key = data_blob_dup_talloc(session_info,
+                                               user_info_dc->user_session_key);
+               if (session_info->session_key.data == NULL) {
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_NO_MEMORY;
+               }
+       }
+
+       if (!(session_info_flags & AUTH_SESSION_INFO_UNIX_TOKEN)) {
+               goto done;
+       }
+
+       session_info->unix_token = talloc_zero(session_info, struct security_unix_token);
+       if (session_info->unix_token == NULL) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
+       }
+       session_info->unix_token->uid = -1;
+       session_info->unix_token->gid = -1;
+
+       session_info->unix_info = talloc_zero(session_info, struct auth_user_info_unix);
+       if (session_info->unix_info == NULL) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       /* Convert the SIDs to uid/gids. */
+
+       ids = talloc_zero_array(frame, struct unixid,
+                               session_info->security_token->num_sids);
+       if (ids == NULL) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       if (!(hint_flags & AUTH3_UNIX_HINT_DONT_TRANSLATE_FROM_SIDS)) {
+               ok = sids_to_unixids(session_info->security_token->sids,
+                                    session_info->security_token->num_sids,
+                                    ids);
+               if (!ok) {
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_NO_MEMORY;
+               }
+       }
+
+       if (found_hint_uid) {
+               session_info->unix_token->uid = hint_uid;
+       } else if (ids[0].type == ID_TYPE_UID) {
+               /*
+                * The primary SID resolves to a UID only.
+                */
+               session_info->unix_token->uid = ids[0].id;
+       } else if (ids[0].type == ID_TYPE_BOTH) {
+               /*
+                * The primary SID resolves to a UID and GID,
+                * use it as uid and add it as first element
+                * to the groups array.
+                */
+               session_info->unix_token->uid = ids[0].id;
+
+               ok = add_gid_to_array_unique(session_info->unix_token,
+                                            session_info->unix_token->uid,
+                                            &session_info->unix_token->groups,
+                                            &session_info->unix_token->ngroups);
+               if (!ok) {
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_NO_MEMORY;
+               }
+       } else {
+               /*
+                * It we can't get a uid, we can't imporsonate
+                * the user.
+                */
+               TALLOC_FREE(frame);
+               return NT_STATUS_INVALID_TOKEN;
+       }
+
+       if (found_hint_gid) {
+               session_info->unix_token->gid = hint_gid;
+       } else {
+               need_getpwuid = true;
+       }
+
+       if (hint_flags & AUTH3_UNIX_HINT_QUALIFIED_NAME) {
+               session_info->unix_info->unix_name =
+                       talloc_asprintf(session_info->unix_info,
+                                       "%s%c%s",
+                                       session_info->info->domain_name,
+                                       *lp_winbind_separator(),
+                                       session_info->info->account_name);
+               if (session_info->unix_info->unix_name == NULL) {
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_NO_MEMORY;
+               }
+       } else if (hint_flags & AUTH3_UNIX_HINT_ISLOLATED_NAME) {
+               session_info->unix_info->unix_name =
+                       talloc_strdup(session_info->unix_info,
+                                     session_info->info->account_name);
+               if (session_info->unix_info->unix_name == NULL) {
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_NO_MEMORY;
+               }
+       } else {
+               need_getpwuid = true;
+       }
+
+       if (need_getpwuid) {
+               struct passwd *pwd = NULL;
+
+               /*
+                * Ask the system for the primary gid
+                * and the real unix name.
+                */
+               pwd = getpwuid_alloc(frame, session_info->unix_token->uid);
+               if (pwd == NULL) {
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_INVALID_TOKEN;
+               }
+               if (!found_hint_gid) {
+                       session_info->unix_token->gid = pwd->pw_gid;
+               }
+
+               session_info->unix_info->unix_name =
+                       talloc_strdup(session_info->unix_info, pwd->pw_name);
+               if (session_info->unix_info->unix_name == NULL) {
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_NO_MEMORY;
+               }
+
+               TALLOC_FREE(pwd);
+       }
+
+       ok = add_gid_to_array_unique(session_info->unix_token,
+                                    session_info->unix_token->gid,
+                                    &session_info->unix_token->groups,
+                                    &session_info->unix_token->ngroups);
+       if (!ok) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       /* This is a potentially untrusted username for use in %U */
+       alpha_strcpy(tmp, original_user_name, ". _-$", sizeof(tmp));
+       session_info->unix_info->sanitized_username =
+                               talloc_strdup(session_info->unix_info, tmp);
+       if (session_info->unix_info->sanitized_username == NULL) {
+               TALLOC_FREE(frame);
+               return NT_STATUS_NO_MEMORY;
+       }
+
+       for (i=0; i < session_info->security_token->num_sids; i++) {
+
+               if (ids[i].type != ID_TYPE_GID &&
+                   ids[i].type != ID_TYPE_BOTH) {
+                       struct security_token *nt_token =
+                               session_info->security_token;
+
+                       DEBUG(10, ("Could not convert SID %s to gid, "
+                                  "ignoring it\n",
+                                  sid_string_dbg(&nt_token->sids[i])));
+                       continue;
+               }
+
+               ok = add_gid_to_array_unique(session_info->unix_token,
+                                            ids[i].id,
+                                            &session_info->unix_token->groups,
+                                            &session_info->unix_token->ngroups);
+               if (!ok) {
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_NO_MEMORY;
+               }
+       }
+       TALLOC_FREE(ids);
+
+       /*
+        * Now we must get any groups this user has been
+        * added to in /etc/group and merge them in.
+        * This has to be done in every code path
+        * that creates an NT token, as remote users
+        * may have been added to the local /etc/group
+        * database. Tokens created merely from the
+        * info3 structs (via the DC or via the krb5 PAC)
+        * won't have these local groups. Note the
+        * groups added here will only be UNIX groups
+        * (S-1-22-2-XXXX groups) as getgroups_unix_user()
+        * turns off winbindd before calling getgroups().
+        *
+        * NB. This is duplicating work already
+        * done in the 'unix_user:' case of
+        * create_token_from_sid() but won't
+        * do anything other than be inefficient
+        * in that case.
+        */
+       if (!(hint_flags & AUTH3_UNIX_HINT_DONT_EXPAND_UNIX_GROUPS)) {
+               ok = getgroups_unix_user(frame,
+                                        session_info->unix_info->unix_name,
+                                        session_info->unix_token->gid,
+                                        &gids, &num_gids);
+               if (!ok) {
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_INVALID_TOKEN;
+               }
+       }
+
+       for (i=0; i < num_gids; i++) {
+
+               ok = add_gid_to_array_unique(session_info->unix_token,
+                                            gids[i],
+                                            &session_info->unix_token->groups,
+                                            &session_info->unix_token->ngroups);
+               if (!ok) {
+                       TALLOC_FREE(frame);
+                       return NT_STATUS_NO_MEMORY;
+               }
+       }
+       TALLOC_FREE(gids);
+
+       if (hint_flags & AUTH3_UNIX_HINT_DONT_TRANSLATE_TO_SIDS) {
+               /*
+                * We should not translate the unix token uid/gids
+                * to S-1-22-X-Y SIDs.
+                */
+               goto done;
+       }
+
+       /*
+        * Add the "Unix Group" SID for each gid to catch mapped groups
+        * and their Unix equivalent.  This is to solve the backwards
+        * compatibility problem of 'valid users = +ntadmin' where
+        * ntadmin has been paired with "Domain Admins" in the group
+        * mapping table.  Otherwise smb.conf would need to be changed
+        * to 'valid user = "Domain Admins"'.  --jerry
+        *
+        * For consistency we also add the "Unix User" SID,
+        * so that the complete unix token is represented within
+        * the nt token.
+        */
+
+       uid_to_unix_users_sid(session_info->unix_token->uid, &tmp_sid);
+       status = add_sid_to_array_unique(session_info->security_token, &tmp_sid,
+                                        &session_info->security_token->sids,
+                                        &session_info->security_token->num_sids);
+       if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(frame);
+               return status;
+       }
+
+       gid_to_unix_groups_sid(session_info->unix_token->gid, &tmp_sid);
+       status = add_sid_to_array_unique(session_info->security_token, &tmp_sid,
+                                        &session_info->security_token->sids,
+                                        &session_info->security_token->num_sids);
+       if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(frame);
+               return status;
+       }
+
+       for (i=0; i < session_info->unix_token->ngroups; i++ ) {
+               struct security_token *nt_token = session_info->security_token;
+
+               gid_to_unix_groups_sid(session_info->unix_token->groups[i],
+                                      &tmp_sid);
+               status = add_sid_to_array_unique(nt_token->sids,
+                                                &tmp_sid,
+                                                &nt_token->sids,
+                                                &nt_token->num_sids);
+               if (!NT_STATUS_IS_OK(status)) {
+                       TALLOC_FREE(frame);
+                       return status;
+               }
+       }
+
+done:
+       security_token_debug(DBGC_AUTH, 10, session_info->security_token);
+       if (session_info->unix_token != NULL) {
+               debug_unix_user_token(DBGC_AUTH, 10,
+                                     session_info->unix_token->uid,
+                                     session_info->unix_token->gid,
+                                     session_info->unix_token->ngroups,
+                                     session_info->unix_token->groups);
+       }
+
+       status = log_nt_token(session_info->security_token);
+       if (!NT_STATUS_IS_OK(status)) {
+               TALLOC_FREE(frame);
+               return status;
+       }
+
+       *session_info_out = talloc_move(mem_ctx, &session_info);
+       TALLOC_FREE(frame);
+       return NT_STATUS_OK;
+}
+
 /***************************************************************************
  Make (and fill) a server_info struct from a 'struct passwd' by conversion
  to a struct samu
index e47a34733fbd58c67171f29a06dad91ae3945ff6..dc59fa969ac3130ca5776e3dd1c1ab1491b3a145 100644 (file)
@@ -221,6 +221,38 @@ NTSTATUS create_local_token(TALLOC_CTX *mem_ctx,
                            DATA_BLOB *session_key,
                            const char *smb_name,
                            struct auth_session_info **session_info_out);
+
+/*
+ * The unix name should be constructed as DOMAIN+ACCOUNT,
+ * while '+' will be the "winbind separator" character.
+ */
+#define AUTH3_UNIX_HINT_QUALIFIED_NAME             0x00000001
+/*
+ * The unix name will be just ACCOUNT
+ */
+#define AUTH3_UNIX_HINT_ISLOLATED_NAME             0x00000002
+/*
+ * Don't translate the nt token SIDS into uid/gids
+ */
+#define AUTH3_UNIX_HINT_DONT_TRANSLATE_FROM_SIDS   0x00000004
+/*
+ * Don't translate the unix token uid/gids to S-1-22-X-Y SIDS
+ */
+#define AUTH3_UNIX_HINT_DONT_TRANSLATE_TO_SIDS     0x00000008
+/*
+ * The unix token won't get expanded gid values
+ * from getgroups_unix_user()
+ */
+#define AUTH3_UNIX_HINT_DONT_EXPAND_UNIX_GROUPS    0x00000010
+NTSTATUS auth3_user_info_dc_add_hints(struct auth_user_info_dc *user_info_dc,
+                                     uid_t uid,
+                                     gid_t gid,
+                                     uint32_t flags);
+NTSTATUS auth3_session_info_create(TALLOC_CTX *mem_ctx,
+                                  const struct auth_user_info_dc *user_info_dc,
+                                  const char *original_user_name,
+                                  uint32_t session_info_flags,
+                                  struct auth_session_info **session_info_out);
 NTSTATUS create_token_from_username(TALLOC_CTX *mem_ctx, const char *username,
                                    bool is_guest,
                                    uid_t *uid, gid_t *gid,