Pārlūkot izejas kodu

Ticket #47384 - Plugin library path validation

Bug description: ldapmodify could replace the plugin path with
an invalid path.

Fix description: This patch adds the validation code to dse
callback.  If modify operation is requested for a plugin entry,
the registered callback check_plugin_path is called and check
the given plugin path.

This patch also has made get_plugin_name public as slapi_get_
plugin_name.  Now, the following plugin paths are allowed:
  /path/to/lib<plugin>.so, /path/to/lib<plugin>,
  lib<plugin>.so, lib<plugin>

https://fedorahosted.org/389/ticket/47384

Reviewed by Rich (Thanks!!)
Noriko Hosoi 12 gadi atpakaļ
vecāks
revīzija
a4b81c0ae5

+ 6 - 41
ldap/servers/slapd/dynalib.c

@@ -58,13 +58,6 @@ static struct dynalib {
 static void symload_report_error( const char *libpath, char *symbol, char *plugin,
 		int libopen );
 
-/* construct a full path and name of a plugin
-   very similar to PR_GetLibraryName except that function inserts
-   the string "lib" at the beginning of name, making that function
-   unsuitable for constructing plugin names
-*/
-static char *get_plugin_name(const char *dir, const char *name);
-
 static void free_plugin_name(char *name)
 {
 	PR_smprintf_free(name);
@@ -114,7 +107,11 @@ sym_load_with_flags( char *libpath, char *symbol, char *plugin, int report_error
 	}
 
 	if (PR_SUCCESS != PR_Access(libpath, PR_ACCESS_READ_OK)) {
-		libSpec.value.pathname = get_plugin_name(PLUGINDIR, libpath);
+		if (strncmp(libpath, PLUGINDIR, strlen(PLUGINDIR))) {
+			libSpec.value.pathname = slapi_get_plugin_name(PLUGINDIR, libpath);
+		} else {
+			libSpec.value.pathname = slapi_get_plugin_name(NULL, libpath);
+		}
 		/* then just handle that failure case with symload_report_error below */
 	}
 
@@ -123,7 +120,7 @@ sym_load_with_flags( char *libpath, char *symbol, char *plugin, int report_error
 			symload_report_error( libSpec.value.pathname, symbol, plugin, 0 /* lib not open */ );
 		}
 		if (libSpec.value.pathname != libpath) {
-			free_plugin_name((char *)libSpec.value.pathname); /* cast ok - allocated by get_plugin_name */
+			free_plugin_name((char *)libSpec.value.pathname); /* cast ok - allocated by slapi_get_plugin_name */
 		}
 		return( NULL );
 	}
@@ -171,35 +168,3 @@ symload_report_error( const char *libpath, char *symbol, char *plugin, int libop
 			libpath, plugin, 0 );
 	}
 }
-
-/* PR_GetLibraryName does almost everything we need, and unfortunately
-   a little bit more - it adds "lib" to be beginning of the library
-   name if the library name does not end with the current platform
-   DLL suffix - so
-   foo.so -> /path/foo.so
-   libfoo.so -> /path/libfoo.so
-   BUT
-   foo -> /path/libfoo.so
-   libfoo -> /path/liblibfoo.so
-*/
-static char *
-get_plugin_name(const char *path, const char *lib)
-{
-    const char *libstr = "/lib";
-    size_t libstrlen = 4;
-	char *fullname = PR_GetLibraryName(path, lib);
-	char *ptr = PL_strrstr(fullname, lib);
-
-    /* see if /lib was added */
-	if (ptr && ((ptr - fullname) >= libstrlen)) {
-        /* ptr is at the libname in fullname, and there is something before it */
-        ptr -= libstrlen; /* ptr now points at the "/" in "/lib" if it is there */
-        if (0 == PL_strncmp(ptr, libstr, libstrlen)) {
-            /* just copy the remainder of the string on top of here */
-            ptr++; /* ptr now points at the "l" in "/lib" - keep the "/" */
-            memmove(ptr, ptr+3, strlen(ptr+3)+1);
-        }
-	}
-
-	return fullname;
-}

+ 50 - 0
ldap/servers/slapd/fedse.c

@@ -1551,6 +1551,8 @@ static const char *easter_egg_photos[NUM_EASTER_EGG_PHOTOS + 1];
 
 static struct dse *pfedse= NULL;
 
+static int check_plugin_path(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode, char *returntext, void *arg);
+
 static void
 internal_add_helper(Slapi_Entry *e, int dont_write_file)
 {
@@ -1786,6 +1788,7 @@ setup_internal_backends(char *configdir)
 		Slapi_Backend *be;
 		Slapi_DN encryption;
 		Slapi_DN saslmapping;
+		Slapi_DN plugins;
 
 		slapi_sdn_init_ndn_byref(&monitor,"cn=monitor");
 		slapi_sdn_init_ndn_byref(&counters,"cn=counters,cn=monitor");
@@ -1794,6 +1797,7 @@ setup_internal_backends(char *configdir)
 
 		slapi_sdn_init_ndn_byref(&encryption,"cn=encryption,cn=config");
 		slapi_sdn_init_ndn_byref(&saslmapping,"cn=mapping,cn=sasl,cn=config");
+		slapi_sdn_init_ndn_byref(&plugins,"cn=plugins,cn=config");
 
 		/* Search */
 		dse_register_callback(pfedse,SLAPI_OPERATION_SEARCH,DSE_FLAG_PREOP,&config,LDAP_SCOPE_BASE,"(objectclass=*)",read_config_dse,NULL);
@@ -1809,6 +1813,7 @@ setup_internal_backends(char *configdir)
 		dse_register_callback(pfedse,SLAPI_OPERATION_MODIFY,DSE_FLAG_POSTOP,&config,LDAP_SCOPE_BASE,"(objectclass=*)",postop_modify_config_dse,NULL);
 		dse_register_callback(pfedse,SLAPI_OPERATION_MODIFY,DSE_FLAG_PREOP,&root,LDAP_SCOPE_BASE,"(objectclass=*)",modify_root_dse,NULL);
 		dse_register_callback(pfedse,SLAPI_OPERATION_MODIFY,DSE_FLAG_PREOP,&saslmapping,LDAP_SCOPE_SUBTREE,"(objectclass=nsSaslMapping)",sasl_map_config_modify,NULL);
+		dse_register_callback(pfedse,SLAPI_OPERATION_MODIFY,DSE_FLAG_PREOP,&plugins,LDAP_SCOPE_SUBTREE,"(objectclass=nsSlapdPlugin)",check_plugin_path,NULL);
 		
 		/* Delete */
 		dse_register_callback(pfedse,SLAPI_OPERATION_DELETE,DSE_FLAG_PREOP,&config,LDAP_SCOPE_BASE,"(objectclass=*)",dont_allow_that,NULL);
@@ -1839,6 +1844,7 @@ setup_internal_backends(char *configdir)
 		slapi_sdn_done(&snmp);
 		slapi_sdn_done(&root);
 		slapi_sdn_done(&saslmapping);
+		slapi_sdn_done(&plugins);
 	} else {
 		slapi_log_error( SLAPI_LOG_FATAL, "dse",
 				"Please edit the file to correct the reported problems"
@@ -1903,3 +1909,47 @@ int fedse_create_startOK(char *filename,  char *startokfilename, const char *con
 
     return rc;
 }
+
+static int
+check_plugin_path(Slapi_PBlock *pb,
+                  Slapi_Entry* entryBefore,
+                  Slapi_Entry* e,
+                  int *returncode,
+                  char *returntext,
+                  void *arg)
+{
+    /* check for invalid nsslapd-pluginPath */
+    char **vals = slapi_entry_attr_get_charray (e, ATTR_PLUGIN_PATH);
+    int plugindir_len = sizeof(PLUGINDIR)-1;
+    int j = 0;
+    int rc = SLAPI_DSE_CALLBACK_OK;
+    for (j = 0; vals && vals[j]; j++) {
+        char *full_path = NULL;
+        char *resolved_path = NULL;
+        char *res = NULL;
+ 
+        if ( *vals[j] == '/' ) { /* absolute path */
+            full_path = slapi_get_plugin_name(NULL, vals[j]);
+        } else {                 /* relative path */
+            full_path = slapi_get_plugin_name(PLUGINDIR, vals[j]);
+        }
+        resolved_path = slapi_ch_malloc(strlen(full_path) + 1);
+        res = realpath( full_path, resolved_path );
+        if (res) {
+            if (strncmp(PLUGINDIR, resolved_path, plugindir_len) != 0) {
+                *returncode = LDAP_UNWILLING_TO_PERFORM;
+                returntext = "Invalid plugin path";
+                rc = SLAPI_DSE_CALLBACK_ERROR;
+            }
+        } else {
+            *returncode = LDAP_UNWILLING_TO_PERFORM;
+            returntext = "Invalid plugin path";
+            rc = SLAPI_DSE_CALLBACK_ERROR;
+        }
+        slapi_ch_free_string(&full_path);
+        slapi_ch_free_string(&resolved_path);
+    }
+    slapi_ch_array_free(vals);
+
+    return rc;
+}

+ 10 - 0
ldap/servers/slapd/slapi-plugin.h

@@ -7495,6 +7495,16 @@ int slapi_eq_cancel(Slapi_Eq_Context ctx);
  */
 void *slapi_eq_get_arg (Slapi_Eq_Context ctx);
 
+/**
+ * Construct a full path and name of a plugin.
+ *
+ * \param dir The Directory where the plugin is located.
+ * \param name The name of the plugin.
+ *
+ * \return absolute path of the plugin.  Caller is responsible to free it.
+ */
+char *slapi_get_plugin_name(const char *dir, const char *name);
+
 #ifdef __cplusplus
 }
 #endif

+ 39 - 0
ldap/servers/slapd/util.c

@@ -1395,3 +1395,42 @@ slapi_str_to_u64(const char *s)
 		(v6 << 36) | (v7 << 32) | (v8 << 28) | (v9 << 24) | (v10 << 20) | (v11 << 16) |
 		(v12 << 12) | (v13 << 8) | (v14 << 4) | v15;
 }
+
+/* PR_GetLibraryName does almost everything we need, and unfortunately
+   a little bit more - it adds "lib" to be beginning of the library
+   name if the library name does not end with the current platform
+   DLL suffix - so
+   foo.so -> /path/foo.so
+   libfoo.so -> /path/libfoo.so
+   BUT
+   foo -> /path/libfoo.so
+   libfoo -> /path/liblibfoo.so
+   /path/libfoo -> lib/path/libfoo.so
+*/
+char *
+slapi_get_plugin_name(const char *path, const char *lib)
+{
+    const char *libstr = "/lib";
+    size_t libstrlen = 4;
+    char *fullname = PR_GetLibraryName(path, lib);
+    char *ptr = PL_strrstr(fullname, lib);
+
+    /* see if /lib was added */
+    if (ptr && ((ptr - fullname) >= libstrlen)) {
+        /* ptr is at the libname in fullname, and there is something before it */
+        ptr -= libstrlen; /* ptr now points at the "/" in "/lib" if it is there */
+        if (0 == PL_strncmp(ptr, libstr, libstrlen)) {
+            /* just copy the remainder of the string on top of here */
+            ptr++; /* ptr now points at the "l" in "/lib" - keep the "/" */
+            memmove(ptr, ptr+3, strlen(ptr+3)+1);
+        }
+    } else if ((NULL == path) && (0 == strncmp(fullname, "lib", 3))) {
+        /* 
+         * case: /path/libfoo -> lib/path/libfoo.so
+         * remove "lib".
+         */
+        memmove(fullname, fullname+3, strlen(fullname)-2);
+    }
+
+    return fullname;
+}