xref: /openbsd-src/usr.sbin/ldapd/validate.c (revision 684a6a8c3a1c7a2af7bdcd3a27717cc34f679474)
1*684a6a8cSclaudio /*	$OpenBSD: validate.c,v 1.13 2021/12/20 13:18:29 claudio Exp $ */
25d465952Smartinh 
35d465952Smartinh /*
45d465952Smartinh  * Copyright (c) 2010 Martin Hedenfalk <martin@bzero.se>
55d465952Smartinh  *
65d465952Smartinh  * Permission to use, copy, modify, and distribute this software for any
75d465952Smartinh  * purpose with or without fee is hereby granted, provided that the above
85d465952Smartinh  * copyright notice and this permission notice appear in all copies.
95d465952Smartinh  *
105d465952Smartinh  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
115d465952Smartinh  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
125d465952Smartinh  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
135d465952Smartinh  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
145d465952Smartinh  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
155d465952Smartinh  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
165d465952Smartinh  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
175d465952Smartinh  */
185d465952Smartinh 
195d465952Smartinh #include <sys/types.h>
205d465952Smartinh #include <sys/queue.h>
215d465952Smartinh 
225d465952Smartinh #include <stdlib.h>
235d465952Smartinh #include <string.h>
245d465952Smartinh 
255d465952Smartinh #include "ldapd.h"
26fdd30f56Sbenno #include "log.h"
275d465952Smartinh 
285d465952Smartinh static int
validate_required_attributes(struct ber_element * entry,struct object * obj)295d465952Smartinh validate_required_attributes(struct ber_element *entry, struct object *obj)
305d465952Smartinh {
315d465952Smartinh 	struct attr_ptr		*ap;
325d465952Smartinh 	struct attr_type	*at;
335d465952Smartinh 
345d465952Smartinh 	if (obj->must == NULL)
355d465952Smartinh 		return LDAP_SUCCESS;
365d465952Smartinh 
375d465952Smartinh 	SLIST_FOREACH(ap, obj->must, next) {
385d465952Smartinh 		at = ap->attr_type;
395d465952Smartinh 
405d465952Smartinh 		if (ldap_find_attribute(entry, at) == NULL) {
415d465952Smartinh 			log_debug("missing required attribute %s",
425d465952Smartinh 			    ATTR_NAME(at));
435d465952Smartinh 			return LDAP_OBJECT_CLASS_VIOLATION;
445d465952Smartinh 		}
455d465952Smartinh 	}
465d465952Smartinh 
475d465952Smartinh 	return LDAP_SUCCESS;
485d465952Smartinh }
495d465952Smartinh 
505d465952Smartinh static int
validate_attribute(struct attr_type * at,struct ber_element * vals)515d465952Smartinh validate_attribute(struct attr_type *at, struct ber_element *vals)
525d465952Smartinh {
535d465952Smartinh 	int			 nvals = 0;
545d465952Smartinh 	struct ber_element	*elm;
557c686fcdSmartinh 	char			*val;
565d465952Smartinh 
575d465952Smartinh 	if (vals == NULL) {
585d465952Smartinh 		log_debug("missing values");
595d465952Smartinh 		return LDAP_OTHER;
605d465952Smartinh 	}
615d465952Smartinh 
625d465952Smartinh 	if (vals->be_type != BER_TYPE_SET) {
635d465952Smartinh 		log_debug("values should be a set");
645d465952Smartinh 		return LDAP_OTHER;
655d465952Smartinh 	}
665d465952Smartinh 
675d465952Smartinh 	for (elm = vals->be_sub; elm != NULL; elm = elm->be_next) {
68696b5899Stb 		if (ober_get_string(elm, &val) == -1) {
695d465952Smartinh 			log_debug("attribute value not an octet-string");
705d465952Smartinh 			return LDAP_PROTOCOL_ERROR;
715d465952Smartinh 		}
725d465952Smartinh 
735d465952Smartinh 		if (++nvals > 1 && at->single) {
745d465952Smartinh 			log_debug("multiple values for single-valued"
755d465952Smartinh 			    " attribute %s", ATTR_NAME(at));
765d465952Smartinh 			return LDAP_CONSTRAINT_VIOLATION;
775d465952Smartinh 		}
787c686fcdSmartinh 
797c686fcdSmartinh 		if (at->syntax->is_valid != NULL &&
807c686fcdSmartinh 		    !at->syntax->is_valid(conf->schema, val, elm->be_len)) {
817c686fcdSmartinh 			log_debug("%s: invalid syntax", ATTR_NAME(at));
827c686fcdSmartinh 			log_debug("syntax = %s", at->syntax->desc);
83c0785a05Sreyk 			log_debug("value: [%.*s]", (int)elm->be_len, val);
847c686fcdSmartinh 			return LDAP_INVALID_SYNTAX;
857c686fcdSmartinh 		}
865d465952Smartinh 	}
875d465952Smartinh 
885d465952Smartinh 	/* There must be at least one value in an attribute. */
895d465952Smartinh 	if (nvals == 0) {
905d465952Smartinh 		log_debug("missing value in attribute %s", ATTR_NAME(at));
915d465952Smartinh 		return LDAP_CONSTRAINT_VIOLATION;
925d465952Smartinh 	}
935d465952Smartinh 
94561be2e9Smartinh 	/* FIXME: validate that values are unique */
95561be2e9Smartinh 
965d465952Smartinh 	return LDAP_SUCCESS;
975d465952Smartinh }
985d465952Smartinh 
995d465952Smartinh /* FIXME: doesn't handle escaped characters.
1005d465952Smartinh  */
1015d465952Smartinh static int
validate_dn(const char * dn,struct ber_element * entry)1025d465952Smartinh validate_dn(const char *dn, struct ber_element *entry)
1035d465952Smartinh {
1045d465952Smartinh 	char			*copy;
1055d465952Smartinh 	char			*sup_dn, *na, *dv, *p;
1065d465952Smartinh 	struct namespace	*ns;
1075d465952Smartinh 	struct attr_type	*at;
1085d465952Smartinh 	struct ber_element	*vals;
1095d465952Smartinh 
1105d465952Smartinh 	if ((copy = strdup(dn)) == NULL)
1115d465952Smartinh 		return LDAP_OTHER;
1125d465952Smartinh 
1135d465952Smartinh 	sup_dn = strchr(copy, ',');
1145d465952Smartinh 	if (sup_dn++ == NULL)
1155d465952Smartinh 		sup_dn = strrchr(copy, '\0');
1165d465952Smartinh 
1175d465952Smartinh 	/* Validate naming attributes and distinguished values in the RDN.
1185d465952Smartinh 	 */
1195d465952Smartinh 	p = copy;
1205d465952Smartinh 	for (;p < sup_dn;) {
1215d465952Smartinh 		na = p;
1225d465952Smartinh 		p = na + strcspn(na, "=");
1235d465952Smartinh 		if (p == na || p >= sup_dn) {
1245d465952Smartinh 			free(copy);
1255d465952Smartinh 			return LDAP_INVALID_DN_SYNTAX;
1265d465952Smartinh 		}
1275d465952Smartinh 		*p = '\0';
1285d465952Smartinh 		dv = p + 1;
1295d465952Smartinh 		p = dv + strcspn(dv, "+,");
1305d465952Smartinh 		if (p == dv) {
1315d465952Smartinh 			free(copy);
1325d465952Smartinh 			return LDAP_INVALID_DN_SYNTAX;
1335d465952Smartinh 		}
1345d465952Smartinh 		*p++ = '\0';
1355d465952Smartinh 
136a2a43363Smartinh 		if ((at = lookup_attribute(conf->schema, na)) == NULL) {
1375d465952Smartinh 			log_debug("attribute %s not defined in schema", na);
1385d465952Smartinh 			goto fail;
1395d465952Smartinh 		}
1405d465952Smartinh 		if (at->usage != USAGE_USER_APP) {
1415d465952Smartinh 			log_debug("naming attribute %s is operational", na);
1425d465952Smartinh 			goto fail;
1435d465952Smartinh 		}
1445d465952Smartinh 		if (at->collective) {
1455d465952Smartinh 			log_debug("naming attribute %s is collective", na);
1465d465952Smartinh 			goto fail;
1475d465952Smartinh 		}
1485d465952Smartinh 		if (at->obsolete) {
1495d465952Smartinh 			log_debug("naming attribute %s is obsolete", na);
1505d465952Smartinh 			goto fail;
1515d465952Smartinh 		}
15278a17c8cSmartinh 		if (at->equality == NULL) {
1535d465952Smartinh 			log_debug("naming attribute %s doesn't define equality",
1545d465952Smartinh 			    na);
1555d465952Smartinh 			goto fail;
1565d465952Smartinh 		}
1575d465952Smartinh 		if ((vals = ldap_find_attribute(entry, at)) == NULL) {
1585d465952Smartinh 			log_debug("missing distinguished value for %s", na);
1595d465952Smartinh 			goto fail;
1605d465952Smartinh 		}
1615d465952Smartinh 		if (ldap_find_value(vals->be_next, dv) == NULL) {
1625d465952Smartinh 			log_debug("missing distinguished value %s"
1635d465952Smartinh 			    " in naming attribute %s", dv, na);
1645d465952Smartinh 			goto fail;
1655d465952Smartinh 		}
1665d465952Smartinh 	}
1675d465952Smartinh 
1685d465952Smartinh 	/* Check that the RDN immediate superior exists, or it is a
1695d465952Smartinh 	 * top-level namespace.
1705d465952Smartinh 	 */
1715d465952Smartinh 	if (*sup_dn != '\0') {
1725d465952Smartinh 		TAILQ_FOREACH(ns, &conf->namespaces, next) {
1735d465952Smartinh 			if (strcmp(dn, ns->suffix) == 0)
1745d465952Smartinh 				goto done;
1755d465952Smartinh 		}
1765d465952Smartinh 		ns = namespace_for_base(sup_dn);
1775d465952Smartinh 		if (ns == NULL || !namespace_exists(ns, sup_dn)) {
1785d465952Smartinh 			free(copy);
1795d465952Smartinh 			return LDAP_NO_SUCH_OBJECT;
1805d465952Smartinh 		}
1815d465952Smartinh 	}
1825d465952Smartinh 
1835d465952Smartinh done:
1845d465952Smartinh 	free(copy);
1855d465952Smartinh 	return LDAP_SUCCESS;
1865d465952Smartinh fail:
1875d465952Smartinh 	free(copy);
1885d465952Smartinh 	return LDAP_NAMING_VIOLATION;
1895d465952Smartinh }
1905d465952Smartinh 
1915d465952Smartinh static int
has_attribute(struct attr_type * at,struct attr_list * alist)192561be2e9Smartinh has_attribute(struct attr_type *at, struct attr_list *alist)
1935d465952Smartinh {
194561be2e9Smartinh 	struct attr_ptr		*ap;
1955d465952Smartinh 
196561be2e9Smartinh 	if (alist == NULL)
197561be2e9Smartinh 		return 0;
198561be2e9Smartinh 
199561be2e9Smartinh 	SLIST_FOREACH(ap, alist, next) {
200561be2e9Smartinh 		if (at == ap->attr_type)
201561be2e9Smartinh 			return 1;
2025d465952Smartinh 	}
203561be2e9Smartinh 	return 0;
2045d465952Smartinh }
2055d465952Smartinh 
206561be2e9Smartinh /* Validate that the attribute type is allowed by any object class.
207561be2e9Smartinh  */
208561be2e9Smartinh static int
validate_allowed_attribute(struct attr_type * at,struct obj_list * olist)209561be2e9Smartinh validate_allowed_attribute(struct attr_type *at, struct obj_list *olist)
210561be2e9Smartinh {
211561be2e9Smartinh 	struct object		*obj;
212561be2e9Smartinh 	struct obj_ptr		*optr;
213561be2e9Smartinh 
214561be2e9Smartinh 	if (olist == NULL)
215561be2e9Smartinh 		return LDAP_OBJECT_CLASS_VIOLATION;
216561be2e9Smartinh 
217561be2e9Smartinh 	SLIST_FOREACH(optr, olist, next) {
218561be2e9Smartinh 		obj = optr->object;
219561be2e9Smartinh 
220561be2e9Smartinh 		if (has_attribute(at, obj->may) ||
221561be2e9Smartinh 		    has_attribute(at, obj->must))
222561be2e9Smartinh 			return LDAP_SUCCESS;
223561be2e9Smartinh 
224561be2e9Smartinh 		if (validate_allowed_attribute(at, obj->sup) == LDAP_SUCCESS)
225561be2e9Smartinh 			return LDAP_SUCCESS;
226561be2e9Smartinh 	}
227561be2e9Smartinh 
228561be2e9Smartinh 	return LDAP_OBJECT_CLASS_VIOLATION;
229561be2e9Smartinh }
230561be2e9Smartinh 
231561be2e9Smartinh static void
olist_push(struct obj_list * olist,struct object * obj)232561be2e9Smartinh olist_push(struct obj_list *olist, struct object *obj)
233561be2e9Smartinh {
234561be2e9Smartinh 	struct obj_ptr		*optr, *sup;
235561be2e9Smartinh 
236561be2e9Smartinh 	SLIST_FOREACH(optr, olist, next)
237561be2e9Smartinh 		if (optr->object == obj)
238561be2e9Smartinh 			return;
239561be2e9Smartinh 
240561be2e9Smartinh 	if ((optr = calloc(1, sizeof(*optr))) == NULL)
241561be2e9Smartinh 		return;
242561be2e9Smartinh 	optr->object = obj;
243561be2e9Smartinh 	SLIST_INSERT_HEAD(olist, optr, next);
244561be2e9Smartinh 
2455d98f3b0Smartinh 	/* Expand the list of object classes along the superclass chain.
246561be2e9Smartinh 	 */
247561be2e9Smartinh 	if (obj->sup != NULL)
248561be2e9Smartinh 		SLIST_FOREACH(sup, obj->sup, next)
249561be2e9Smartinh 			olist_push(olist, sup->object);
250561be2e9Smartinh }
251561be2e9Smartinh 
252241e5f5bSmartinh static void
olist_free(struct obj_list * olist)253241e5f5bSmartinh olist_free(struct obj_list *olist)
254241e5f5bSmartinh {
255241e5f5bSmartinh 	struct obj_ptr		*optr;
256241e5f5bSmartinh 
257241e5f5bSmartinh 	if (olist == NULL)
258241e5f5bSmartinh 		return;
259241e5f5bSmartinh 
260241e5f5bSmartinh 	while ((optr = SLIST_FIRST(olist)) != NULL) {
261241e5f5bSmartinh 		SLIST_REMOVE_HEAD(olist, next);
262241e5f5bSmartinh 		free(optr);
263241e5f5bSmartinh 	}
264241e5f5bSmartinh 
265241e5f5bSmartinh 	free(olist);
266241e5f5bSmartinh }
267241e5f5bSmartinh 
268561be2e9Smartinh /* Check if sup is a superior object class to obj.
269561be2e9Smartinh  */
270561be2e9Smartinh static int
is_super(struct object * sup,struct object * obj)271561be2e9Smartinh is_super(struct object *sup, struct object *obj)
272561be2e9Smartinh {
273561be2e9Smartinh 	struct obj_ptr	*optr;
274561be2e9Smartinh 
275561be2e9Smartinh 	if (sup == NULL || obj->sup == NULL)
2765d98f3b0Smartinh 		return 0;
277561be2e9Smartinh 
278561be2e9Smartinh 	SLIST_FOREACH(optr, obj->sup, next)
279561be2e9Smartinh 		if (optr->object == sup || is_super(sup, optr->object))
280561be2e9Smartinh 			return 1;
281561be2e9Smartinh 
282561be2e9Smartinh 	return 0;
2835d465952Smartinh }
2845d465952Smartinh 
2855d465952Smartinh int
validate_entry(const char * dn,struct ber_element * entry,int relax)2865d465952Smartinh validate_entry(const char *dn, struct ber_element *entry, int relax)
2875d465952Smartinh {
288561be2e9Smartinh 	int			 rc, extensible = 0;
2895d465952Smartinh 	char			*s;
2905d465952Smartinh 	struct ber_element	*objclass, *a, *vals;
2915d465952Smartinh 	struct object		*obj, *structural_obj = NULL;
2925d465952Smartinh 	struct attr_type	*at;
293241e5f5bSmartinh 	struct obj_list		*olist = NULL;
294d5900b0fSmartinh 	struct obj_ptr		*optr, *optr2;
2955d465952Smartinh 
2965d465952Smartinh 	if (relax)
2975d465952Smartinh 		goto rdn;
2985d465952Smartinh 
2995d465952Smartinh 	/* There must be an objectClass attribute.
3005d465952Smartinh 	 */
3015d465952Smartinh 	objclass = ldap_get_attribute(entry, "objectClass");
3025d465952Smartinh 	if (objclass == NULL) {
3035d465952Smartinh 		log_debug("missing objectClass attribute");
3045d465952Smartinh 		return LDAP_OBJECT_CLASS_VIOLATION;
3055d465952Smartinh 	}
3065d465952Smartinh 
307561be2e9Smartinh 	if ((olist = calloc(1, sizeof(*olist))) == NULL)
308561be2e9Smartinh 		return LDAP_OTHER;
309561be2e9Smartinh 	SLIST_INIT(olist);
310561be2e9Smartinh 
3115d465952Smartinh 	/* Check objectClass(es) against schema.
3125d465952Smartinh 	 */
3135d465952Smartinh 	objclass = objclass->be_next;		/* skip attribute description */
3145d465952Smartinh 	for (a = objclass->be_sub; a != NULL; a = a->be_next) {
315696b5899Stb 		if (ober_get_string(a, &s) != 0) {
316*684a6a8cSclaudio 			log_debug("invalid ObjectClass encoding");
317241e5f5bSmartinh 			rc = LDAP_INVALID_SYNTAX;
318241e5f5bSmartinh 			goto done;
319241e5f5bSmartinh 		}
320561be2e9Smartinh 
321a2a43363Smartinh 		if ((obj = lookup_object(conf->schema, s)) == NULL) {
3225d465952Smartinh 			log_debug("objectClass %s not defined in schema", s);
323241e5f5bSmartinh 			rc = LDAP_NAMING_VIOLATION;
324241e5f5bSmartinh 			goto done;
3255d465952Smartinh 		}
3265d465952Smartinh 
327561be2e9Smartinh 		if (obj->kind == KIND_STRUCTURAL) {
328561be2e9Smartinh 			if (structural_obj != NULL) {
329561be2e9Smartinh 				if (is_super(structural_obj, obj))
330561be2e9Smartinh 					structural_obj = obj;
331561be2e9Smartinh 				else if (!is_super(obj, structural_obj)) {
332561be2e9Smartinh 					log_debug("multiple structural"
333561be2e9Smartinh 					    " object classes");
334241e5f5bSmartinh 					rc = LDAP_OBJECT_CLASS_VIOLATION;
335241e5f5bSmartinh 					goto done;
336561be2e9Smartinh 				}
337561be2e9Smartinh 			} else
338561be2e9Smartinh 				structural_obj = obj;
3395d465952Smartinh 		}
3405d465952Smartinh 
341561be2e9Smartinh 		olist_push(olist, obj);
342561be2e9Smartinh 
343561be2e9Smartinh                 /* RFC4512, section 4.3:
344561be2e9Smartinh 		 * "The 'extensibleObject' auxiliary object class allows
345561be2e9Smartinh 		 * entries that belong to it to hold any user attribute."
346561be2e9Smartinh                  */
347561be2e9Smartinh                 if (strcmp(obj->oid, "1.3.6.1.4.1.1466.101.120.111") == 0)
348561be2e9Smartinh                         extensible = 1;
349561be2e9Smartinh         }
350561be2e9Smartinh 
351561be2e9Smartinh 	/* Must have exactly one structural object class.
3525d465952Smartinh 	 */
3535d465952Smartinh 	if (structural_obj == NULL) {
3545d465952Smartinh 		log_debug("no structural object class defined");
355241e5f5bSmartinh 		rc = LDAP_OBJECT_CLASS_VIOLATION;
356241e5f5bSmartinh 		goto done;
3575d465952Smartinh 	}
3585d465952Smartinh 
359d5900b0fSmartinh 	/* "An entry cannot belong to an abstract object class
360d5900b0fSmartinh 	 *  unless it belongs to a structural or auxiliary class that
361d5900b0fSmartinh 	 *  inherits from that abstract class."
362d5900b0fSmartinh 	 */
363d5900b0fSmartinh         SLIST_FOREACH(optr, olist, next) {
364d5900b0fSmartinh 		if (optr->object->kind != KIND_ABSTRACT)
365d5900b0fSmartinh 			continue;
366d5900b0fSmartinh 
367d5900b0fSmartinh 		/* Check the structural object class. */
368d5900b0fSmartinh 		if (is_super(optr->object, structural_obj))
369d5900b0fSmartinh 			continue;
370d5900b0fSmartinh 
371d5900b0fSmartinh 		/* Check all auxiliary object classes. */
372d5900b0fSmartinh 		SLIST_FOREACH(optr2, olist, next) {
373d5900b0fSmartinh 			if (optr2->object->kind != KIND_AUXILIARY)
374d5900b0fSmartinh 				continue;
375d5900b0fSmartinh 			if (is_super(optr->object, optr2->object))
376d5900b0fSmartinh 				break;
377d5900b0fSmartinh 		}
378d5900b0fSmartinh 
379d5900b0fSmartinh 		if (optr2 == NULL) {
380d5900b0fSmartinh 			/* No subclassed object class found. */
381d5900b0fSmartinh 			log_debug("abstract class '%s' not subclassed",
382d5900b0fSmartinh 			    OBJ_NAME(optr->object));
383241e5f5bSmartinh 			rc = LDAP_OBJECT_CLASS_VIOLATION;
384241e5f5bSmartinh 			goto done;
385d5900b0fSmartinh 		}
386d5900b0fSmartinh 	}
387d5900b0fSmartinh 
388561be2e9Smartinh 	/* Check all required attributes.
389561be2e9Smartinh 	 */
390561be2e9Smartinh 	SLIST_FOREACH(optr, olist, next) {
391241e5f5bSmartinh 		rc = validate_required_attributes(entry, optr->object);
392241e5f5bSmartinh 		if (rc != LDAP_SUCCESS)
393241e5f5bSmartinh 			goto done;
394561be2e9Smartinh 	}
395561be2e9Smartinh 
3965d465952Smartinh 	/* Check all attributes against schema.
3975d465952Smartinh 	 */
3985d465952Smartinh 	for (a = entry->be_sub; a != NULL; a = a->be_next) {
399696b5899Stb 		if (ober_scanf_elements(a, "{se{", &s, &vals) != 0) {
400*684a6a8cSclaudio 			log_debug("invalid attribute encoding");
401241e5f5bSmartinh 			rc = LDAP_INVALID_SYNTAX;
402241e5f5bSmartinh 			goto done;
403241e5f5bSmartinh 		}
404a2a43363Smartinh 		if ((at = lookup_attribute(conf->schema, s)) == NULL) {
4055d465952Smartinh 			log_debug("attribute %s not defined in schema", s);
406241e5f5bSmartinh 			rc = LDAP_NAMING_VIOLATION;
407241e5f5bSmartinh 			goto done;
4085d465952Smartinh 		}
4095d465952Smartinh 		if ((rc = validate_attribute(at, vals)) != LDAP_SUCCESS)
410241e5f5bSmartinh 			goto done;
411561be2e9Smartinh 		if (!extensible && at->usage == USAGE_USER_APP &&
412561be2e9Smartinh 		    (rc = validate_allowed_attribute(at, olist)) != LDAP_SUCCESS) {
413561be2e9Smartinh 			log_debug("%s not allowed by any object class",
414561be2e9Smartinh 			    ATTR_NAME(at));
415241e5f5bSmartinh 			goto done;
416561be2e9Smartinh 		}
4175d465952Smartinh 	}
4185d465952Smartinh 
4195d465952Smartinh rdn:
420241e5f5bSmartinh 	rc = validate_dn(dn, entry);
4215d465952Smartinh 
422241e5f5bSmartinh done:
423241e5f5bSmartinh 	olist_free(olist);
424241e5f5bSmartinh 	return rc;
4255d465952Smartinh }
4265d465952Smartinh 
427