getgroupmembership.c revision 1.4.8.2 1 1.4.8.2 martin /* $NetBSD: getgroupmembership.c,v 1.4.8.2 2008/04/28 20:23:00 martin Exp $ */
2 1.4.8.2 martin
3 1.4.8.2 martin /*-
4 1.4.8.2 martin * Copyright (c) 2004-2005 The NetBSD Foundation, Inc.
5 1.4.8.2 martin * All rights reserved.
6 1.4.8.2 martin *
7 1.4.8.2 martin * This code is derived from software contributed to The NetBSD Foundation
8 1.4.8.2 martin * by Luke Mewburn.
9 1.4.8.2 martin *
10 1.4.8.2 martin * Redistribution and use in source and binary forms, with or without
11 1.4.8.2 martin * modification, are permitted provided that the following conditions
12 1.4.8.2 martin * are met:
13 1.4.8.2 martin * 1. Redistributions of source code must retain the above copyright
14 1.4.8.2 martin * notice, this list of conditions and the following disclaimer.
15 1.4.8.2 martin * 2. Redistributions in binary form must reproduce the above copyright
16 1.4.8.2 martin * notice, this list of conditions and the following disclaimer in the
17 1.4.8.2 martin * documentation and/or other materials provided with the distribution.
18 1.4.8.2 martin *
19 1.4.8.2 martin * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 1.4.8.2 martin * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 1.4.8.2 martin * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 1.4.8.2 martin * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 1.4.8.2 martin * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 1.4.8.2 martin * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 1.4.8.2 martin * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 1.4.8.2 martin * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 1.4.8.2 martin * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 1.4.8.2 martin * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 1.4.8.2 martin * POSSIBILITY OF SUCH DAMAGE.
30 1.4.8.2 martin */
31 1.4.8.2 martin
32 1.4.8.2 martin #include <sys/cdefs.h>
33 1.4.8.2 martin #if defined(LIBC_SCCS) && !defined(lint)
34 1.4.8.2 martin __RCSID("$NetBSD: getgroupmembership.c,v 1.4.8.2 2008/04/28 20:23:00 martin Exp $");
35 1.4.8.2 martin #endif /* LIBC_SCCS and not lint */
36 1.4.8.2 martin
37 1.4.8.2 martin /*
38 1.4.8.2 martin * calculate group access list
39 1.4.8.2 martin */
40 1.4.8.2 martin
41 1.4.8.2 martin #include "namespace.h"
42 1.4.8.2 martin #include "reentrant.h"
43 1.4.8.2 martin
44 1.4.8.2 martin #include <sys/param.h>
45 1.4.8.2 martin
46 1.4.8.2 martin #include <assert.h>
47 1.4.8.2 martin #include <errno.h>
48 1.4.8.2 martin #include <grp.h>
49 1.4.8.2 martin #include <limits.h>
50 1.4.8.2 martin #include <nsswitch.h>
51 1.4.8.2 martin #include <stdarg.h>
52 1.4.8.2 martin #include <stdio.h>
53 1.4.8.2 martin #include <stdlib.h>
54 1.4.8.2 martin #include <string.h>
55 1.4.8.2 martin #include <unistd.h>
56 1.4.8.2 martin
57 1.4.8.2 martin #ifdef HESIOD
58 1.4.8.2 martin #include <hesiod.h>
59 1.4.8.2 martin #endif
60 1.4.8.2 martin
61 1.4.8.2 martin #include "gr_private.h"
62 1.4.8.2 martin
63 1.4.8.2 martin #ifdef __weak_alias
64 1.4.8.2 martin __weak_alias(getgroupmembership,_getgroupmembership)
65 1.4.8.2 martin #endif
66 1.4.8.2 martin
67 1.4.8.2 martin /*
68 1.4.8.2 martin * __gr_addgid
69 1.4.8.2 martin * Add gid to the groups array (of maxgrp size) at the position
70 1.4.8.2 martin * indicated by *groupc, unless it already exists or *groupc is
71 1.4.8.2 martin * past &groups[maxgrp].
72 1.4.8.2 martin * Returns 1 upon success (including duplicate suppression), 0 otherwise.
73 1.4.8.2 martin */
74 1.4.8.2 martin static int
75 1.4.8.2 martin __gr_addgid(gid_t gid, gid_t *groups, int maxgrp, int *groupc)
76 1.4.8.2 martin {
77 1.4.8.2 martin int ret, dupc;
78 1.4.8.2 martin
79 1.4.8.2 martin _DIAGASSERT(groupc != NULL);
80 1.4.8.2 martin _DIAGASSERT(groups != NULL);
81 1.4.8.2 martin
82 1.4.8.2 martin /* skip duplicates */
83 1.4.8.2 martin for (dupc = 0; dupc < MIN(maxgrp, *groupc); dupc++) {
84 1.4.8.2 martin if (groups[dupc] == gid)
85 1.4.8.2 martin return 1;
86 1.4.8.2 martin }
87 1.4.8.2 martin
88 1.4.8.2 martin ret = 1;
89 1.4.8.2 martin if (*groupc < maxgrp) /* add this gid */
90 1.4.8.2 martin groups[*groupc] = gid;
91 1.4.8.2 martin else
92 1.4.8.2 martin ret = 0;
93 1.4.8.2 martin (*groupc)++;
94 1.4.8.2 martin return ret;
95 1.4.8.2 martin }
96 1.4.8.2 martin
97 1.4.8.2 martin
98 1.4.8.2 martin /*ARGSUSED*/
99 1.4.8.2 martin static int
100 1.4.8.2 martin _files_getgroupmembership(void *retval, void *cb_data, va_list ap)
101 1.4.8.2 martin {
102 1.4.8.2 martin int *result = va_arg(ap, int *);
103 1.4.8.2 martin const char *uname = va_arg(ap, const char *);
104 1.4.8.2 martin gid_t agroup = va_arg(ap, gid_t);
105 1.4.8.2 martin gid_t *groups = va_arg(ap, gid_t *);
106 1.4.8.2 martin int maxgrp = va_arg(ap, int);
107 1.4.8.2 martin int *groupc = va_arg(ap, int *);
108 1.4.8.2 martin
109 1.4.8.2 martin struct __grstate_files state;
110 1.4.8.2 martin struct group grp;
111 1.4.8.2 martin char grpbuf[_GETGR_R_SIZE_MAX];
112 1.4.8.2 martin int rv, i;
113 1.4.8.2 martin
114 1.4.8.2 martin _DIAGASSERT(result != NULL);
115 1.4.8.2 martin _DIAGASSERT(uname != NULL);
116 1.4.8.2 martin /* groups may be NULL if just sizing when invoked with maxgrp = 0 */
117 1.4.8.2 martin _DIAGASSERT(groupc != NULL);
118 1.4.8.2 martin
119 1.4.8.2 martin /* install primary group */
120 1.4.8.2 martin (void) __gr_addgid(agroup, groups, maxgrp, groupc);
121 1.4.8.2 martin
122 1.4.8.2 martin memset(&state, 0, sizeof(state));
123 1.4.8.2 martin while (__grscan_files(&rv, &grp, grpbuf, sizeof(grpbuf), &state,
124 1.4.8.2 martin 0, NULL, 0) == NS_SUCCESS) {
125 1.4.8.2 martin /* scan members */
126 1.4.8.2 martin for (i = 0; grp.gr_mem[i]; i++) {
127 1.4.8.2 martin if (strcmp(grp.gr_mem[i], uname) != 0)
128 1.4.8.2 martin continue;
129 1.4.8.2 martin if (! __gr_addgid(grp.gr_gid, groups, maxgrp, groupc))
130 1.4.8.2 martin *result = -1;
131 1.4.8.2 martin break;
132 1.4.8.2 martin }
133 1.4.8.2 martin }
134 1.4.8.2 martin __grend_files(&state);
135 1.4.8.2 martin return NS_NOTFOUND;
136 1.4.8.2 martin }
137 1.4.8.2 martin
138 1.4.8.2 martin
139 1.4.8.2 martin #ifdef HESIOD
140 1.4.8.2 martin
141 1.4.8.2 martin /*ARGSUSED*/
142 1.4.8.2 martin static int
143 1.4.8.2 martin _dns_getgroupmembership(void *retval, void *cb_data, va_list ap)
144 1.4.8.2 martin {
145 1.4.8.2 martin int *result = va_arg(ap, int *);
146 1.4.8.2 martin const char *uname = va_arg(ap, const char *);
147 1.4.8.2 martin gid_t agroup = va_arg(ap, gid_t);
148 1.4.8.2 martin gid_t *groups = va_arg(ap, gid_t *);
149 1.4.8.2 martin int maxgrp = va_arg(ap, int);
150 1.4.8.2 martin int *groupc = va_arg(ap, int *);
151 1.4.8.2 martin
152 1.4.8.2 martin struct __grstate_dns state;
153 1.4.8.2 martin struct group grp;
154 1.4.8.2 martin char grpbuf[_GETGR_R_SIZE_MAX];
155 1.4.8.2 martin unsigned long id;
156 1.4.8.2 martin void *context;
157 1.4.8.2 martin char **hp, *cp, *ep;
158 1.4.8.2 martin int rv, i;
159 1.4.8.2 martin
160 1.4.8.2 martin _DIAGASSERT(result != NULL);
161 1.4.8.2 martin _DIAGASSERT(uname != NULL);
162 1.4.8.2 martin /* groups may be NULL if just sizing when invoked with maxgrp = 0 */
163 1.4.8.2 martin _DIAGASSERT(groupc != NULL);
164 1.4.8.2 martin
165 1.4.8.2 martin /* install primary group */
166 1.4.8.2 martin (void) __gr_addgid(agroup, groups, maxgrp, groupc);
167 1.4.8.2 martin
168 1.4.8.2 martin hp = NULL;
169 1.4.8.2 martin rv = NS_NOTFOUND;
170 1.4.8.2 martin
171 1.4.8.2 martin if (hesiod_init(&context) == -1) /* setup hesiod */
172 1.4.8.2 martin return NS_UNAVAIL;
173 1.4.8.2 martin
174 1.4.8.2 martin hp = hesiod_resolve(context, uname, "grplist"); /* find grplist */
175 1.4.8.2 martin if (hp == NULL) {
176 1.4.8.2 martin if (errno != ENOENT) { /* wasn't "not found"*/
177 1.4.8.2 martin rv = NS_UNAVAIL;
178 1.4.8.2 martin goto dnsgroupmembers_out;
179 1.4.8.2 martin }
180 1.4.8.2 martin /* grplist not found, fallback to _dns_grscan */
181 1.4.8.2 martin memset(&state, 0, sizeof(state));
182 1.4.8.2 martin while (__grscan_dns(&rv, &grp, grpbuf, sizeof(grpbuf), &state,
183 1.4.8.2 martin 0, NULL, 0) == NS_SUCCESS) {
184 1.4.8.2 martin /* scan members */
185 1.4.8.2 martin for (i = 0; grp.gr_mem[i]; i++) {
186 1.4.8.2 martin if (strcmp(grp.gr_mem[i], uname) != 0)
187 1.4.8.2 martin continue;
188 1.4.8.2 martin if (! __gr_addgid(grp.gr_gid, groups, maxgrp,
189 1.4.8.2 martin groupc))
190 1.4.8.2 martin *result = -1;
191 1.4.8.2 martin break;
192 1.4.8.2 martin }
193 1.4.8.2 martin }
194 1.4.8.2 martin __grend_dns(&state);
195 1.4.8.2 martin rv = NS_NOTFOUND;
196 1.4.8.2 martin goto dnsgroupmembers_out;
197 1.4.8.2 martin }
198 1.4.8.2 martin
199 1.4.8.2 martin if ((ep = strchr(hp[0], '\n')) != NULL)
200 1.4.8.2 martin *ep = '\0'; /* clear trailing \n */
201 1.4.8.2 martin
202 1.4.8.2 martin for (cp = hp[0]; *cp != '\0'; ) { /* parse grplist */
203 1.4.8.2 martin if ((cp = strchr(cp, ':')) == NULL) /* skip grpname */
204 1.4.8.2 martin break;
205 1.4.8.2 martin cp++;
206 1.4.8.2 martin id = strtoul(cp, &ep, 10); /* parse gid */
207 1.4.8.2 martin if (id > GID_MAX || (*ep != ':' && *ep != '\0')) {
208 1.4.8.2 martin rv = NS_UNAVAIL;
209 1.4.8.2 martin goto dnsgroupmembers_out;
210 1.4.8.2 martin }
211 1.4.8.2 martin cp = ep;
212 1.4.8.2 martin if (*cp == ':')
213 1.4.8.2 martin cp++;
214 1.4.8.2 martin
215 1.4.8.2 martin /* add gid */
216 1.4.8.2 martin if (! __gr_addgid((gid_t)id, groups, maxgrp, groupc))
217 1.4.8.2 martin *result = -1;
218 1.4.8.2 martin }
219 1.4.8.2 martin
220 1.4.8.2 martin rv = NS_NOTFOUND;
221 1.4.8.2 martin
222 1.4.8.2 martin dnsgroupmembers_out:
223 1.4.8.2 martin if (hp)
224 1.4.8.2 martin hesiod_free_list(context, hp);
225 1.4.8.2 martin hesiod_end(context);
226 1.4.8.2 martin return rv;
227 1.4.8.2 martin }
228 1.4.8.2 martin
229 1.4.8.2 martin #endif /* HESIOD */
230 1.4.8.2 martin
231 1.4.8.2 martin
232 1.4.8.2 martin #ifdef YP
233 1.4.8.2 martin
234 1.4.8.2 martin /*ARGSUSED*/
235 1.4.8.2 martin static int
236 1.4.8.2 martin _nis_getgroupmembership(void *retval, void *cb_data, va_list ap)
237 1.4.8.2 martin {
238 1.4.8.2 martin int *result = va_arg(ap, int *);
239 1.4.8.2 martin const char *uname = va_arg(ap, const char *);
240 1.4.8.2 martin gid_t agroup = va_arg(ap, gid_t);
241 1.4.8.2 martin gid_t *groups = va_arg(ap, gid_t *);
242 1.4.8.2 martin int maxgrp = va_arg(ap, int);
243 1.4.8.2 martin int *groupc = va_arg(ap, int *);
244 1.4.8.2 martin
245 1.4.8.2 martin struct __grstate_nis state;
246 1.4.8.2 martin struct group grp;
247 1.4.8.2 martin char grpbuf[_GETGR_R_SIZE_MAX];
248 1.4.8.2 martin int rv, i;
249 1.4.8.2 martin
250 1.4.8.2 martin _DIAGASSERT(result != NULL);
251 1.4.8.2 martin _DIAGASSERT(uname != NULL);
252 1.4.8.2 martin /* groups may be NULL if just sizing when invoked with maxgrp = 0 */
253 1.4.8.2 martin _DIAGASSERT(groupc != NULL);
254 1.4.8.2 martin
255 1.4.8.2 martin /* install primary group */
256 1.4.8.2 martin (void) __gr_addgid(agroup, groups, maxgrp, groupc);
257 1.4.8.2 martin
258 1.4.8.2 martin memset(&state, 0, sizeof(state));
259 1.4.8.2 martin while (__grscan_nis(&rv, &grp, grpbuf, sizeof(grpbuf), &state,
260 1.4.8.2 martin 0, NULL, 0) == NS_SUCCESS) {
261 1.4.8.2 martin /* scan members */
262 1.4.8.2 martin for (i = 0; grp.gr_mem[i]; i++) {
263 1.4.8.2 martin if (strcmp(grp.gr_mem[i], uname) != 0)
264 1.4.8.2 martin continue;
265 1.4.8.2 martin if (! __gr_addgid(grp.gr_gid, groups, maxgrp, groupc))
266 1.4.8.2 martin *result = -1;
267 1.4.8.2 martin break;
268 1.4.8.2 martin }
269 1.4.8.2 martin }
270 1.4.8.2 martin __grend_nis(&state);
271 1.4.8.2 martin
272 1.4.8.2 martin return NS_NOTFOUND;
273 1.4.8.2 martin }
274 1.4.8.2 martin
275 1.4.8.2 martin #endif /* YP */
276 1.4.8.2 martin
277 1.4.8.2 martin
278 1.4.8.2 martin #ifdef _GROUP_COMPAT
279 1.4.8.2 martin
280 1.4.8.2 martin struct __compatggm {
281 1.4.8.2 martin const char *uname; /* user to search for */
282 1.4.8.2 martin gid_t *groups;
283 1.4.8.2 martin gid_t agroup;
284 1.4.8.2 martin int maxgrp;
285 1.4.8.2 martin int *groupc;
286 1.4.8.2 martin };
287 1.4.8.2 martin
288 1.4.8.2 martin static int
289 1.4.8.2 martin _compat_ggm_search(void *cookie, struct group **groupres)
290 1.4.8.2 martin {
291 1.4.8.2 martin struct __compatggm *cp;
292 1.4.8.2 martin int rerror, crv;
293 1.4.8.2 martin
294 1.4.8.2 martin static const ns_dtab dtab[] = {
295 1.4.8.2 martin NS_FILES_CB(__grbad_compat, "files")
296 1.4.8.2 martin NS_DNS_CB(_dns_getgroupmembership, NULL)
297 1.4.8.2 martin NS_NIS_CB(_nis_getgroupmembership, NULL)
298 1.4.8.2 martin NS_COMPAT_CB(__grbad_compat, "compat")
299 1.4.8.2 martin NS_NULL_CB
300 1.4.8.2 martin };
301 1.4.8.2 martin
302 1.4.8.2 martin *groupres = NULL; /* we don't care about this */
303 1.4.8.2 martin cp = (struct __compatggm *)cookie;
304 1.4.8.2 martin
305 1.4.8.2 martin crv = nsdispatch(NULL, dtab,
306 1.4.8.2 martin NSDB_GROUP_COMPAT, "getgroupmembership",
307 1.4.8.2 martin __nsdefaultnis,
308 1.4.8.2 martin &rerror, cp->uname, cp->agroup, cp->groups, cp->maxgrp, cp->groupc);
309 1.4.8.2 martin
310 1.4.8.2 martin if (crv == NS_SUCCESS)
311 1.4.8.2 martin crv = NS_NOTFOUND; /* indicate "no more +: entries" */
312 1.4.8.2 martin
313 1.4.8.2 martin return crv;
314 1.4.8.2 martin }
315 1.4.8.2 martin
316 1.4.8.2 martin /* ARGSUSED */
317 1.4.8.2 martin static int
318 1.4.8.2 martin _compat_getgroupmembership(void *retval, void *cb_data, va_list ap)
319 1.4.8.2 martin {
320 1.4.8.2 martin int *result = va_arg(ap, int *);
321 1.4.8.2 martin const char *uname = va_arg(ap, const char *);
322 1.4.8.2 martin gid_t agroup = va_arg(ap, gid_t);
323 1.4.8.2 martin gid_t *groups = va_arg(ap, gid_t *);
324 1.4.8.2 martin int maxgrp = va_arg(ap, int);
325 1.4.8.2 martin int *groupc = va_arg(ap, int *);
326 1.4.8.2 martin
327 1.4.8.2 martin struct __grstate_compat state;
328 1.4.8.2 martin struct __compatggm ggmstate;
329 1.4.8.2 martin struct group grp;
330 1.4.8.2 martin char grpbuf[_GETGR_R_SIZE_MAX];
331 1.4.8.2 martin int rv, i;
332 1.4.8.2 martin
333 1.4.8.2 martin _DIAGASSERT(result != NULL);
334 1.4.8.2 martin _DIAGASSERT(uname != NULL);
335 1.4.8.2 martin /* groups may be NULL if just sizing when invoked with maxgrp = 0 */
336 1.4.8.2 martin _DIAGASSERT(groupc != NULL);
337 1.4.8.2 martin
338 1.4.8.2 martin /* install primary group */
339 1.4.8.2 martin (void) __gr_addgid(agroup, groups, maxgrp, groupc);
340 1.4.8.2 martin
341 1.4.8.2 martin memset(&state, 0, sizeof(state));
342 1.4.8.2 martin memset(&ggmstate, 0, sizeof(ggmstate));
343 1.4.8.2 martin ggmstate.uname = uname;
344 1.4.8.2 martin ggmstate.groups = groups;
345 1.4.8.2 martin ggmstate.agroup = agroup;
346 1.4.8.2 martin ggmstate.maxgrp = maxgrp;
347 1.4.8.2 martin ggmstate.groupc = groupc;
348 1.4.8.2 martin
349 1.4.8.2 martin while (__grscan_compat(&rv, &grp, grpbuf, sizeof(grpbuf), &state,
350 1.4.8.2 martin 0, NULL, 0, _compat_ggm_search, &ggmstate)
351 1.4.8.2 martin == NS_SUCCESS) {
352 1.4.8.2 martin /* scan members */
353 1.4.8.2 martin for (i = 0; grp.gr_mem[i]; i++) {
354 1.4.8.2 martin if (strcmp(grp.gr_mem[i], uname) != 0)
355 1.4.8.2 martin continue;
356 1.4.8.2 martin if (! __gr_addgid(grp.gr_gid, groups, maxgrp, groupc))
357 1.4.8.2 martin *result = -1;
358 1.4.8.2 martin break;
359 1.4.8.2 martin }
360 1.4.8.2 martin }
361 1.4.8.2 martin
362 1.4.8.2 martin __grend_compat(&state);
363 1.4.8.2 martin return NS_NOTFOUND;
364 1.4.8.2 martin }
365 1.4.8.2 martin
366 1.4.8.2 martin #endif /* _GROUP_COMPAT */
367 1.4.8.2 martin
368 1.4.8.2 martin
369 1.4.8.2 martin int
370 1.4.8.2 martin getgroupmembership(const char *uname, gid_t agroup,
371 1.4.8.2 martin gid_t *groups, int maxgrp, int *groupc)
372 1.4.8.2 martin {
373 1.4.8.2 martin int rerror;
374 1.4.8.2 martin
375 1.4.8.2 martin static const ns_dtab dtab[] = {
376 1.4.8.2 martin NS_FILES_CB(_files_getgroupmembership, NULL)
377 1.4.8.2 martin NS_DNS_CB(_dns_getgroupmembership, NULL)
378 1.4.8.2 martin NS_NIS_CB(_nis_getgroupmembership, NULL)
379 1.4.8.2 martin NS_COMPAT_CB(_compat_getgroupmembership, NULL)
380 1.4.8.2 martin NS_NULL_CB
381 1.4.8.2 martin };
382 1.4.8.2 martin
383 1.4.8.2 martin _DIAGASSERT(uname != NULL);
384 1.4.8.2 martin /* groups may be NULL if just sizing when invoked with maxgrp = 0 */
385 1.4.8.2 martin _DIAGASSERT(groupc != NULL);
386 1.4.8.2 martin
387 1.4.8.2 martin *groupc = 0;
388 1.4.8.2 martin
389 1.4.8.2 martin mutex_lock(&__grmutex);
390 1.4.8.2 martin /*
391 1.4.8.2 martin * Call each backend.
392 1.4.8.2 martin * For compatibility with getgrent(3) semantics,
393 1.4.8.2 martin * a backend should return NS_NOTFOUND even upon
394 1.4.8.2 martin * completion, to allow result merging to occur.
395 1.4.8.2 martin */
396 1.4.8.2 martin (void) nsdispatch(NULL, dtab, NSDB_GROUP, "getgroupmembership",
397 1.4.8.2 martin __nsdefaultcompat,
398 1.4.8.2 martin &rerror, uname, agroup, groups, maxgrp, groupc);
399 1.4.8.2 martin mutex_unlock(&__grmutex);
400 1.4.8.2 martin
401 1.4.8.2 martin if (*groupc > maxgrp) /* too many groups found */
402 1.4.8.2 martin return -1;
403 1.4.8.2 martin else
404 1.4.8.2 martin return 0;
405 1.4.8.2 martin }
406