[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

bug in pam_rhosts_auth handling of NFS



pam_rhosts_auth does seteuid calls around the user .rhosts file open to
ensure that the file can be opened.  Problem: unless the file is cached
on the linux box root cannot _read_ from the file.  Thus in this sequence

  become-user
  open-file
  become-root
  read-from-file

the read from file part fails, unless the euid _still_ is the users uid.
The euid when the handle was opened _is_ not used, the euid at the time
of the read is.  Furthermore, this kind of thing is just what the
setfsuid call is for.  Thus:

  set-fs-uid-to-user-uid
  open-file
  read-from-file
  set-fs-uid-to-root

I've made the patch relative to the 0.57 preview C again, and it includes
my previous NIS patch.  It's at 
  http://www.math.uio.no/~janl/pam/pam_rhosts_auth.c.diff
the full source is in that dir too.  The diff is enclosed here as well.
I'm still not on the pam mailinglists, so mail comments to me personaly.

Thank you,
  Nicolai

--- pam_rhosts_auth.c.orig	Mon Mar 17 16:57:27 1997
+++ pam_rhosts_auth.c	Fri Mar 21 16:13:59 1997
@@ -79,6 +79,31 @@
 
 #define PAM_SM_AUTH  /* only defines this management group */
 
+/*
+
+  This is the solaris manpage entry for innetgr.  It's declared in
+  <netdb.h> on solaris2 machines.
+
+SYNOPSIS
+     int innetgr(const char *netgroup, const char *machine,
+          const char *user, const char *domain);
+
+DESCRIPTION
+     The function innetgr() returns 1 if there is a netgroup net-
+     group that contains the specified machine, user, domain tri-
+     ple as a member; otherwise it returns 0.  Any  of  the  sup-
+     plied pointers machine, user, and domain may be NULL, signi-
+     fying a "wild card" that matches all values in that position
+     of the triple.
+
+     The innetgr() function is safe for  use  in  single-threaded
+     and multi-threaded applications. 
+
+*/
+
+int innetgr(const char *, const char *, const char *,const char *);
+
+
 #include <security/pam_modules.h>
 
 /*
@@ -121,6 +146,7 @@
     }
 
     if (strcmp (arg, "debug") == 0) {
+        _pam_log(LOG_WARNING,"debugging is on\n");
 	opts->opt_debug = 1;
 	return;
     }
@@ -200,60 +226,109 @@
 }
 
 /*
- * Returns 0 if match, 1 if no match.
+ * Returns 1 if positive match, 0 if no match, -1 if negative match.
  */
 
 static int
-__icheckhost (pam_handle_t *pamh, struct _options *opts, u_long raddr
-	      , register char *lhost)
+__icheckhost (pam_handle_t *pamh, struct _options *opts, u_long raddr,
+	      register char *lhost, const char *rhost)
 {
     struct hostent *hp;
     u_long laddr;
-    char **pp;
+    int negate=1;  /* Multiply return with this to get -1 instead of 1 */
+    char **pp, *user;
 
-    if (strcmp("+",lhost) == 0) {
-	if (opts->opt_promiscuous) {
-	    return (0);                     /* asking for trouble, but ok.. */
-	} else {
-	    if (opts->opt_debug) {
-		const char *user;
-		(void) pam_get_item(pamh, PAM_USER, (const void **)&user);
-		_pam_log(LOG_WARNING, "user %s has a `+' entry", user);
-	    }
-	    return (1);                     /* tough luck */
-	}
+    /* Check nis netgroup.  We assume that pam has done all needed
+       paranoia checking before we are handed the rhost */
+    if (strncmp("+@",lhost,2) == 0)
+      return(innetgr(&lhost[2],rhost,NULL,NULL));
+
+    if (strncmp("-@",lhost,2) == 0)
+      return(-innetgr(&lhost[2],rhost,NULL,NULL));
+
+    /* -host */
+    if (strncmp("-",lhost,1) == 0) {
+      negate=-1;
+      lhost++;
+    } else if (strcmp("+",lhost) == 0) {
+        (void) pam_get_item(pamh, PAM_USER, (const void **)&user);
+        _pam_log(LOG_WARNING, "user %s has a `+' host entry", user);
+	if (opts->opt_promiscuous)
+	    return (1);                     /* asking for trouble, but ok.. */
+	/* If not promiscuous: handle as negative */
+	return (-1);                     
     }
 
     /* Try for raw ip address first. */
     if (isdigit(*lhost) && (long)(laddr = inet_addr(lhost)) != -1)
-	return (!! (raddr ^ laddr));
+	return (negate*(! (raddr ^ laddr)));
 
     /* Better be a hostname. */
     hp = gethostbyname(lhost);
     if (hp == NULL)
-	return (1);
+	return (0);
     
     /* Spin through ip addresses. */
     for (pp = hp->h_addr_list; *pp; ++pp)
 	if (!memcmp (&raddr, *pp, sizeof (unsigned long)))
-	    return (0);
+	    return (negate);
 
     /* No match. */
-    return (1);
+    return (0);
+}
+
+/* Returns 1 on positive match, 0 on no match, -1 on negative match */
+
+static int
+__icheckuser(pam_handle_t *pamh, struct _options *opts, 
+	     const char *luser, const char *ruser, const char *rhost)
+
+
+{
+  /*
+    luser is user entry from .rhosts/hosts.equiv file
+    ruser is user id on remote host
+    rhost is the remote host name
+  */
+  char *user;
+
+  /* [-+]@netgroup */
+  if (strncmp("+@",luser,2) == 0)
+    return (innetgr(&luser[2],NULL,ruser,NULL));
+
+  if (strncmp("-@",luser,2) == 0)
+    return (-innetgr(&luser[2],NULL,ruser,NULL));
+
+  /* -user */
+  if (strncmp("-",luser,1) == 0)
+    return(-(strcmp(&luser[1],ruser) == 0));
+
+  /* + */
+  if (strcmp("+",luser) == 0) {
+    (void) pam_get_item(pamh, PAM_USER, (const void **)&user);
+    _pam_log(LOG_WARNING, "user %s has a `+' user entry", user);
+    if (opts->opt_promiscuous)
+      return(1);
+    /* If not promiscuous we handle it as a negative match */
+    return(-1);
+  }
+
+  /* simple string match */
+  return (strcmp(ruser, luser) == 0);
 }
 
 /*
- * Returns 0 if match, 1 if not ok.
+ * Returns 0 if positive match, 1 if _not_ ok.
  */
 
 static int
 __ivaliduser (pam_handle_t *pamh, struct _options *opts,
 	      FILE *hostf, u_long raddr,
-	      const char *luser, const char *ruser)
+	      const char *luser, const char *ruser, const char *rhost)
 {
     register const char *user;
     register char *p;
-    int ch;
+    int ch,hcheck,ucheck;
     char buf[MAXHOSTNAMELEN + 128];                       /* host + login */
 
     buf[sizeof (buf)-1] = '\0';                 	/* terminate line */
@@ -298,11 +373,28 @@
 
 	/* buf -> host(?) ; user -> username(?) */
 
-	if (__icheckhost(pamh, opts, raddr, buf) == 0) {
-	    if (! (*user))
-	        user = luser;
-	    if (strcmp(ruser, user) == 0)
-	        return (0);
+	/* First check host part */
+	hcheck=__icheckhost(pamh, opts, raddr, buf, rhost);
+
+	if (hcheck<0)
+	  return(1);
+
+	if (hcheck) {
+	  /* Then check user part */
+	  if (! (*user))
+	    user = luser;
+	  
+	  ucheck=__icheckuser(pamh, opts, user, ruser, rhost);
+
+	  /* Positive 'host user' match? */
+	  if (ucheck>0)
+	    return(0);
+
+	  /* Negative 'host -user' match? */
+	  if (ucheck<0)
+	    return(1);
+
+	  /* Neither, go on looking for match */
 	}
     }
 
@@ -322,7 +414,7 @@
 static int
 iruserok(pam_handle_t *pamh,
 	 struct _options *opts, u_long raddr, int superuser,
-	 const char *ruser, const char *luser)
+	 const char *ruser, const char *luser, const char *rhost)
 {
     const char *cp;
     struct stat sbuf;
@@ -337,7 +429,7 @@
 	/* try to open system hosts.equiv file */
 	hostf = fopen (_PATH_HEQUIV, "r");
 	if (hostf) {
-	    answer = __ivaliduser(pamh, opts, hostf, raddr, luser, ruser);
+	    answer = __ivaliduser(pamh, opts, hostf, raddr, luser, ruser, rhost);
 	    (void) fclose(hostf);
 	    if (answer == 0)
 		return 0;      /* remote host is equivalent to localhost */
@@ -374,18 +466,28 @@
     (void)strcat(pbuf, USER_RHOSTS_FILE);
 
     /*
-     * Change effective uid while opening .rhosts.  If root and
-     * reading an NFS mounted file system, can't read files that
-     * are protected read/write owner only.
+     * Change effective uid while _reading_ .rhosts. (not just
+     * opening.  If root and reading an NFS mounted file system,
+     * can't read files that are 0600 as .rhosts files should be.
      */
 
+    /* We are root, this will not fail */
+#ifdef linux
+    /* If we are on linux the better way is setfsuid */
+    uid = setfsuid(pwd->pw_uid);
+    hostf = fopen(pbuf, "r");
+#else
     uid = geteuid();
     (void)seteuid(pwd->pw_uid);
     hostf = fopen(pbuf, "r");
-    (void)seteuid(uid);
+#endif
 	
-    if (hostf == NULL)
-	return 1;
+    if (hostf == NULL) {
+        if (opts->opt_debug)
+	    _pam_log(LOG_DEBUG,"Could not open %s file",pbuf);
+	answer=1;
+	goto exit;
+    }
 
     /*
      * If not a regular file, or is owned by someone other than
@@ -406,12 +508,26 @@
     /* If there were any problems, quit. */
     if (cp) {
 	opts->last_error = cp;
-	fclose(hostf);
-	return 1;
+	answer = 1;
+	goto exit;
     }
 
-    answer = __ivaliduser (pamh, opts, hostf, raddr, luser, ruser);
-    (void) fclose(hostf);
+    answer = __ivaliduser (pamh, opts, hostf, raddr, luser, ruser, rhost);
+
+exit:
+    /* 
+     * Go here to exit after the fsuid/euid has been adjusted so that
+     * they are reset before we exit
+     */
+
+#ifdef linux
+    setfsuid(uid);
+#else
+    (void)seteuid(uid);
+#endif
+
+    if (hostf != NULL)
+        (void) fclose(hostf);
     return answer;
 }
 
@@ -434,7 +550,7 @@
 	    memcpy (&addr, *ap, sizeof(addr));
 
 	    /* check user on remote host */
-	    if (iruserok(pamh, opts, addr, superuser, ruser, luser) == 0) {
+	    if (iruserok(pamh, opts, addr, superuser, ruser, luser, rhost) == 0) {
 		answer = 0;                                /* success */
 		break;
 	    }




[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index] []