summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--utils/gssd/gssd_proc.c82
1 files changed, 66 insertions, 16 deletions
diff --git a/utils/gssd/gssd_proc.c b/utils/gssd/gssd_proc.c
index 99537d9..b48d163 100644
--- a/utils/gssd/gssd_proc.c
+++ b/utils/gssd/gssd_proc.c
@@ -834,7 +834,6 @@ create_auth_rpc_client(struct clnt_info *clp,
CLIENT *rpc_clnt = NULL;
struct rpc_gss_sec sec;
AUTH *auth = NULL;
- uid_t save_uid = -1;
int retval = -1;
OM_uint32 min_stat;
char rpc_errmsg[1024];
@@ -843,16 +842,6 @@ create_auth_rpc_client(struct clnt_info *clp,
struct sockaddr *addr = (struct sockaddr *) &clp->addr;
socklen_t salen;
- /* Create the context as the user (not as root) */
- save_uid = geteuid();
- if (setfsuid(uid) != 0) {
- printerr(0, "WARNING: Failed to setfsuid for "
- "user with uid %d\n", uid);
- goto out_fail;
- }
- printerr(2, "creating context using fsuid %d (save_uid %d)\n",
- uid, save_uid);
-
sec.qop = GSS_C_QOP_DEFAULT;
sec.svc = RPCSEC_GSS_SVC_NONE;
sec.cred = cred;
@@ -951,11 +940,6 @@ create_auth_rpc_client(struct clnt_info *clp,
out:
if (sec.cred != GSS_C_NO_CREDENTIAL)
gss_release_cred(&min_stat, &sec.cred);
- /* Restore euid to original value */
- if (((int)save_uid != -1) && (setfsuid(save_uid) != (int)uid)) {
- printerr(0, "WARNING: Failed to restore fsuid"
- " to uid %d from %d\n", save_uid, uid);
- }
return retval;
out_fail:
@@ -966,6 +950,64 @@ create_auth_rpc_client(struct clnt_info *clp,
}
/*
+ * Create the context as the user (not as root).
+ *
+ * Note that we change the *real* uid here, as changing the effective uid is
+ * not sufficient. This is due to an unfortunate historical error in the MIT
+ * krb5 libs, where they used %{uid} in the default_ccache_name. Changing that
+ * now might break some applications so we're sort of stuck with it.
+ *
+ * Unfortunately, doing this leaves the forked child vulnerable to signals and
+ * renicing, but this is the best we can do. In the event that a child is
+ * signalled before downcalling, the kernel will just eventually time out the
+ * upcall attempt.
+ */
+static int
+change_identity(uid_t uid)
+{
+ struct passwd *pw;
+
+ /* drop list of supplimentary groups first */
+ if (setgroups(0, NULL) != 0) {
+ printerr(0, "WARNING: unable to drop supplimentary groups!");
+ return errno;
+ }
+
+ /* try to get pwent for user */
+ pw = getpwuid(uid);
+ if (!pw) {
+ /* if that doesn't work, try to get one for "nobody" */
+ errno = 0;
+ pw = getpwnam("nobody");
+ if (!pw) {
+ printerr(0, "WARNING: unable to determine gid for uid %u\n", uid);
+ return errno ? errno : ENOENT;
+ }
+ }
+
+ /*
+ * Switch the GIDs. Note that we leave the saved-set-gid alone in an
+ * attempt to prevent attacks via ptrace()
+ */
+ if (setresgid(pw->pw_gid, pw->pw_gid, -1) != 0) {
+ printerr(0, "WARNING: failed to set gid to %u!\n", pw->pw_gid);
+ return errno;
+ }
+
+ /*
+ * Switch UIDs, but leave saved-set-uid alone to prevent ptrace() by
+ * other processes running with this uid.
+ */
+ if (setresuid(uid, uid, -1) != 0) {
+ printerr(0, "WARNING: Failed to setuid for user with uid %u\n",
+ uid);
+ return errno;
+ }
+
+ return 0;
+}
+
+/*
* this code uses the userland rpcsec gss library to create a krb5
* context on behalf of the kernel
*/
@@ -1036,6 +1078,14 @@ process_krb5_upcall(struct clnt_info *clp, uid_t uid, int fd, char *tgtname,
service ? service : "<null>");
if (uid != 0 || (uid == 0 && root_uses_machine_creds == 0 &&
service == NULL)) {
+
+ err = change_identity(uid);
+ if (err) {
+ printerr(0, "WARNING: failed to change identity: %s",
+ strerror(err));
+ goto out_return_error;
+ }
+
/* Tell krb5 gss which credentials cache to use */
/* Try first to acquire credentials directly via GSSAPI */
err = gssd_acquire_user_cred(uid, &gss_cred);