Hello,

here is a small patch for monit-4.10.1 to make monit Linux-PAM aware.

With this patch it is possible to setup monit to use the 
posix-group-membership to distinguish between user who 

1)  can't see any information from the monit webserver
2) get a readonly view 
3) can restart services, enable/disable monitoring, etc.

together with autorization via Linux-PAM.

Therefore one can define in the monitrc:
--
# to give users of posix-group 'group' readonly view
allow @group readonly 

# to give users of posix-group 'service' full view
allow @service
--
Users who are not authenticated via pam don't see anything.

The patch is most usefull if the system where monit runs is setup with 
nss (name service switch) and PAM using a centralized user database. 
In most cases this would be LDAP. Group membership is resolved via 
nss and authorization is done via PAM-Service 'monit'. If one uses 
LDAP as centralized user-DB nss-ldap and pam-ldap are necessary 
components.

Enjoy,
-- 
Wilhelm
diff -rup monit-4.10.1/configure monit-4.10.1-pam/configure
--- monit-4.10.1/configure	2007-11-06 21:34:57.000000000 +0100
+++ monit-4.10.1-pam/configure	2008-04-01 20:56:18.000000000 +0200
@@ -3854,6 +3854,77 @@ _ACEOF
 fi
 
 
+{ echo "$as_me:$LINENO: checking for pam_start in -lpam" >&5
+echo $ECHO_N "checking for pam_start in -lpam... $ECHO_C" >&6; }
+if test "${ac_cv_lib_pam_pam_start+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lpam  $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any GCC internal prototype to avoid an error.
+   Use char because int might match the return type of a GCC
+   builtin and then its argument prototype would still apply.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+char pam_start ();
+int
+main ()
+{
+return pam_start ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (ac_try="$ac_link"
+case "(($ac_try" in
+  *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+  *) ac_try_echo=$ac_try;;
+esac
+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5
+  (eval "$ac_link") 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } && {
+	 test -z "$ac_c_werror_flag" ||
+	 test ! -s conftest.err
+       } && test -s conftest$ac_exeext &&
+       $as_test_x conftest$ac_exeext; then
+  ac_cv_lib_pam_pam_start=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+	ac_cv_lib_pam_pam_start=no
+fi
+
+rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+{ echo "$as_me:$LINENO: result: $ac_cv_lib_pam_pam_start" >&5
+echo "${ECHO_T}$ac_cv_lib_pam_pam_start" >&6; }
+if test $ac_cv_lib_pam_pam_start = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBPAM 1
+_ACEOF
+
+  LIBS="-lpam $LIBS"
+
+fi
+
+
 # Wacky pthread madness
 pthread_libs=""
 { echo "$as_me:$LINENO: checking for pthread_create in -lpthread" >&5
@@ -4501,6 +4572,9 @@ done
 
 
 
+
+
+
 for ac_header in  \
         alloca.h \
 	arpa/inet.h \
@@ -4567,6 +4641,9 @@ for ac_header in  \
         uvm/uvm.h \
         uvm/uvm_extern.h \
         vm/vm.h \
+	pwd.h \
+	grp.h \
+	security/pam_appl.h
 
 do
 as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
diff -rup monit-4.10.1/configure.ac monit-4.10.1-pam/configure.ac
--- monit-4.10.1/configure.ac	2007-11-06 21:32:30.000000000 +0100
+++ monit-4.10.1-pam/configure.ac	2008-04-01 20:44:03.000000000 +0200
@@ -40,6 +40,7 @@ AC_CHECK_LIB([inet], [socket])
 AC_CHECK_LIB([nsl], [inet_addr])
 AC_CHECK_LIB([resolv], [inet_aton])
 AC_CHECK_LIB([crypt], [crypt])
+AC_CHECK_LIB([pam], [pam_start])
 
 # Wacky pthread madness
 pthread_libs=""
@@ -130,6 +131,9 @@ AC_CHECK_HEADERS([ \
         uvm/uvm.h \
         uvm/uvm_extern.h \
         vm/vm.h \
+	pwd.h \
+	grp.h \
+	security/pam_appl.h
 	])
 
 AC_CHECK_HEADERS([ \
Only in monit-4.10.1-pam: monit.cnf
Only in monit-4.10.1-pam: monit.pem
diff -rup monit-4.10.1/monitor.h monit-4.10.1-pam/monitor.h
--- monit-4.10.1/monitor.h	2007-10-16 21:10:43.000000000 +0200
+++ monit-4.10.1-pam/monitor.h	2008-03-28 12:58:43.000000000 +0100
@@ -160,6 +160,7 @@
 #define DIGEST_CLEARTEXT   1
 #define DIGEST_CRYPT       2
 #define DIGEST_MD5         3
+#define DIGEST_PAM         4
 
 #define UNIT_BYTE          1
 #define UNIT_KILOBYTE      1024
@@ -346,7 +347,9 @@ typedef struct mymailserver {
 
 typedef struct myauthentication {
   char *uname;                  /**< User allowed to connect to monit httpd */
-  char *passwd;                                /**< The users password data */
+  char *passwd;                                /**< The users password
+						  data */
+  char* groupname;
   int   digesttype;                      /**< How did we store the password */
   int   is_readonly;     /**< TRUE if this is a read-only authenticated user*/
   struct myauthentication *next;       /**< Next credential or NULL if last */
diff -rup monit-4.10.1/monitrc monit-4.10.1-pam/monitrc
--- monit-4.10.1/monitrc	2007-10-24 22:23:08.000000000 +0200
+++ monit-4.10.1-pam/monitrc	2008-04-01 20:52:19.000000000 +0200
@@ -89,6 +89,8 @@
 #     use address localhost  # only accept connection from localhost
 #     allow localhost        # allow localhost to connect to the server and
 #     allow admin:monit      # require user 'admin' with password 'monit'
+#     allow @monit           # allow users of group 'monit' to connect (rw)
+#     allow @users readonly  # allow users of group 'users' to connect readonly
 #
 #
 ###############################################################################
Only in monit-4.10.1-pam: openssl.rnd
diff -rup monit-4.10.1/p.y monit-4.10.1-pam/p.y
--- monit-4.10.1/p.y	2007-10-24 22:23:08.000000000 +0200
+++ monit-4.10.1-pam/p.y	2008-03-28 13:43:26.000000000 +0100
@@ -194,6 +194,7 @@
   static void  addcollector(URL_T, int, int, char *);
   static void  addmailserver(MailServer_T);
   static int   addcredentials(char *, char *, int, int);
+  static void  addpamauth(char*, int);
   static void  addhtpasswdentry(char *, char *, int);
   static uid_t get_uid(char *, uid_t);
   static gid_t get_gid(char *, gid_t);
@@ -661,6 +662,10 @@ allowselfcert   : /* EMPTY */ { 
 allow           : ALLOW STRING':'STRING readonly {
                     addcredentials($2,$4, DIGEST_CLEARTEXT, $<number>5);
                   }
+                | ALLOW '@'STRING readonly {
+		    printf("Group: %s readonly: %d\n", $3, $<number>4);
+                    addpamauth($3, $<number>4);
+                }
                 | ALLOW PATH {
                     addhtpasswdentry($2, NULL, DIGEST_CLEARTEXT);
                     FREE($2);
@@ -2959,6 +2964,41 @@ static void addhtpasswdentry(char *filen
   
 }
 
+static void addpamauth(char* groupname, int readonly) {
+    Auth_T c = NULL, prev = NULL;
+    ASSERT(groupname);
+
+    if (Run.credentials == NULL) {
+	NEW(Run.credentials);
+    }
+
+    c=Run.credentials;
+    do {
+	if (c->groupname != NULL && strcmp(c->groupname, groupname) == 0) {
+	    yywarning2("groupname %s were already added, entry ignored",
+		       groupname);
+	    FREE(groupname);
+	    return;
+	}
+	prev = c;
+	c=c->next;
+    }
+    while (c != NULL);
+    
+    NEW(prev->next);
+    c=prev->next;
+    
+    c->next=NULL;
+    c->uname=NULL;
+    c->passwd=NULL;
+    c->groupname=groupname;
+    c->digesttype=DIGEST_PAM;
+    c->is_readonly= readonly;
+    
+    DEBUG("%s: Debug: Adding pam-group '%s'.\n", prog, groupname); 
+    
+    return;
+}
 
 /*
  * Add Basic Authentication credentials
@@ -3001,6 +3041,7 @@ static int addcredentials(char *uname, c
   }
   
   c->next=NULL;
+  c->groupname=NULL;
   c->uname=uname;
   c->passwd=passwd;
   c->digesttype=dtype;
diff -rup monit-4.10.1/util.c monit-4.10.1-pam/util.c
--- monit-4.10.1/util.c	2007-10-16 21:10:44.000000000 +0200
+++ monit-4.10.1-pam/util.c	2008-04-01 20:55:26.000000000 +0200
@@ -102,6 +102,9 @@
 #include "process.h"
 #include "event.h"
 
+#include <security/pam_appl.h>
+#include <pwd.h>
+#include <grp.h>
 
 /* Private prototypes */
 static char x2c(char *hex);
@@ -1595,15 +1598,73 @@ void Util_closeFds() {
   errno= 0;
 }
 
+Auth_T Util_checkUserGroup(const char* uname) {
+    DEBUG("Util_checkUserGroup\n");
+
+    struct passwd* pwd = NULL; 
+    ASSERT(uname);
+    if ((pwd = getpwnam(uname)) == NULL) {
+	return NULL;
+    }
+
+    struct group* grp = NULL;
+    if ((grp = getgrgid(pwd->pw_gid)) == NULL) {
+	return NULL;
+    }
+
+    DEBUG("Util_checkUserGroup: check groups for user: %s in group: %s\n", uname, grp->gr_name);
+
+    Auth_T c= Run.credentials;
+    while (c != NULL) {
+	DEBUG("Util_checkUserGroup: iter \n");
+	if (c->groupname != NULL) {
+	    DEBUG("Util_checkUserGroup: check group %s \n", c->groupname);
+	    if (strcmp(c->groupname, grp->gr_name) == 0) {
+		// primary group matches
+		DEBUG("Util_checkUserGroup: user: %s matches group: %s\n",
+		      uname, c->groupname);
+		return c;
+	    }
+	    DEBUG("Util_checkUserGroup: check secondaries \n");
+	    struct group* sgrp = NULL;
+	    if ((sgrp = getgrnam(c->groupname)) != NULL) {
+		DEBUG("Util_checkUserGroup: check secondary group: %s \n", c->groupname);
+		char** g = NULL;
+		for(g = sgrp->gr_mem; *g != NULL; g++) {
+		    DEBUG("Util_checkUserGroup: check group member %s\n", *g);
+		    if (strcmp(*g, uname) == 0) {
+			// secondary group matches
+			DEBUG("Util_checkUserGroup: user: %s matches group: %s\n",
+			      uname, c->groupname);
+			return c;
+		    }
+		}
+	    }
+
+	    
+	}
+	c=c->next;
+    }
+    DEBUG("Util_checkUserGroup: user: %s no match\n", uname);
+    // no match
+    return NULL;
+}
 
 /*
  * Check if monit does have credentials for this user.  If successful
  * a pointer to the password is returned.
  */
 Auth_T Util_getUserCredentials(char *uname) {
+    Auth_T lc = Util_checkUserGroup(uname);
+    // get group and check group and readonly
+
+    if (lc != NULL) { // got a valid user
+	return lc;
+    }
+    
   Auth_T c= Run.credentials;
   while ( c != NULL ) {
-    if ( strcmp(c->uname, uname) == 0 ) {
+    if (c->uname != NULL && strcmp(c->uname, uname) == 0 ) {
       return c;
     }
     c=c->next;
@@ -1611,6 +1672,90 @@ Auth_T Util_getUserCredentials(char *una
   return NULL;
 }
 
+#define msgi(i) (*(msg[i]))
+
+struct ad_user {
+    const char* login;
+    const char* passwd;
+};
+
+// Linux-PAM conversation function
+int Util_PAMconv (int num_msg, const struct pam_message **msg,
+		  struct pam_response **resp, void *appdata_ptr) {
+
+    struct ad_user *user= (struct ad_user *)appdata_ptr;
+    struct pam_response *response;
+    int i;
+
+    /* Sanity checking */
+    if (msg == NULL || resp == NULL || user == NULL)
+    	return PAM_CONV_ERR;
+
+    response= (struct pam_response *)
+    	malloc(num_msg * sizeof(struct pam_response));
+
+    for (i= 0; i < num_msg; i++) {
+	response[i].resp_retcode= 0;
+	response[i].resp= NULL;
+
+	switch (msgi(i).msg_style) {
+	case PAM_PROMPT_ECHO_ON:
+	    /* Store the login as the response */
+	    /* This likely never gets called, since login was on pam_start() */
+	    response[i].resp= appdata_ptr ? (char *)strdup(user->login) : NULL;
+	    break;
+
+	case PAM_PROMPT_ECHO_OFF:
+	    DEBUG("Util_PAMconv: echo off \n");
+	    /* Store the password as the response */
+	    response[i].resp= appdata_ptr ? (char *)strdup(user->passwd) : NULL;
+	    break;
+
+	case PAM_TEXT_INFO:
+	case PAM_ERROR_MSG:
+	    /* Shouldn't happen since we have PAM_SILENT set. If it happens
+	     * anyway, ignore it. */
+	    break;
+
+	default:
+	    /* Something strange... */
+	    if (response != NULL) free(response);
+	    return PAM_CONV_ERR;
+	}
+    }
+    /* On success, return the response structure */
+    *resp = response;
+    return PAM_SUCCESS;
+}
+
+// validate login/passwd via pam-service "monit"
+int Util_checkPamPasswd(const char* login, const char* passwd) {
+    struct ad_user user_info= {login, passwd};
+    struct pam_conv conv = { Util_PAMconv, (void *)&user_info };
+    pam_handle_t *pamh= NULL;
+    int retval;
+
+    DEBUG("Util_checkPamPasswd: user: %s\n", login);
+    
+    retval= pam_start("monit", login, &conv, &pamh);
+
+    if (retval == PAM_SUCCESS)
+	retval= pam_authenticate(pamh, PAM_SILENT);
+
+    /*
+    if (retval == PAM_SUCCESS)
+	retval= pam_acct_mgmt(pamh, PAM_SILENT);		
+    */
+    
+    if (pam_end(pamh,retval) != PAM_SUCCESS) {
+	pamh= NULL;
+    }
+
+    if (retval == PAM_SUCCESS) {
+	return TRUE;
+    }
+    return FALSE;
+}
 
 /**
  * Check if the given password match the registred password for the
@@ -1627,6 +1772,9 @@ int Util_checkCredentials(char *uname, c
     return FALSE;
   }
   switch (c->digesttype) {
+  case DIGEST_PAM:
+      return Util_checkPamPasswd(uname, outside);
+      break;
   case DIGEST_CLEARTEXT:
     {
       strncpy(outside_crypt, outside, STRLEN); 
--
To unsubscribe:
http://lists.nongnu.org/mailman/listinfo/monit-general

Reply via email to