]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/idmapd/idmapd.c
Add the idmapd files...
[nfs-utils.git] / utils / idmapd / idmapd.c
1 /*
2  *  idmapd.c
3  *
4  *  Userland daemon for idmap.
5  *
6  *  Copyright (c) 2002 The Regents of the University of Michigan.
7  *  All rights reserved.
8  *
9  *  Marius Aamodt Eriksen <marius@umich.edu>
10  *
11  *  Redistribution and use in source and binary forms, with or without
12  *  modification, are permitted provided that the following conditions
13  *  are met:
14  *
15  *  1. Redistributions of source code must retain the above copyright
16  *     notice, this list of conditions and the following disclaimer.
17  *  2. Redistributions in binary form must reproduce the above copyright
18  *     notice, this list of conditions and the following disclaimer in the
19  *     documentation and/or other materials provided with the distribution.
20  *  3. Neither the name of the University nor the names of its
21  *     contributors may be used to endorse or promote products derived
22  *     from this software without specific prior written permission.
23  *
24  *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
25  *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
26  *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27  *  DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  *  FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
31  *  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
32  *  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
33  *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
34  *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35  */
36
37 #include <sys/types.h>
38 #include <sys/time.h>
39 #include <sys/poll.h>
40 #include <sys/socket.h>
41 #include <sys/stat.h>
42 #include <time.h>
43
44 #include "nfs_idmap.h"
45
46 #include <err.h>
47 #include <errno.h>
48 #include <event.h>
49 #include <fcntl.h>
50 #include <dirent.h>
51 #include <unistd.h>
52 #include <netdb.h>
53 #include <signal.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <pwd.h>
58 #include <grp.h>
59 #include <limits.h>
60 #include <ctype.h>
61 #include <nfsidmap.h>
62
63 #ifdef HAVE_CONFIG_H
64 #include "config.h"
65 #endif /* HAVE_CONFIG_H */
66
67 #include "cfg.h"
68 #include "queue.h"
69 #include "nfslib.h"
70
71 #ifndef PIPEFS_DIR
72 #define PIPEFS_DIR  "/var/lib/nfs/rpc_pipefs/"
73 #endif
74
75 #ifndef NFSD_DIR
76 #define NFSD_DIR  "/proc/net/rpc"
77 #endif
78
79 #ifndef NFS4NOBODY_USER
80 #define NFS4NOBODY_USER "nobody"
81 #endif
82
83 #ifndef NFS4NOBODY_GROUP
84 #define NFS4NOBODY_GROUP "nobody"
85 #endif
86
87 /* From Niels */
88 #define CONF_SAVE(w, f) do {                    \
89         char *p = f;                            \
90         if (p != NULL)                          \
91                 (w) = p;                        \
92 } while (0)
93
94 #define IC_IDNAME 1
95 #define IC_NAMEID 2
96 struct idmap_client {
97         int                        ic_fd;
98         int                        ic_dirfd;
99         char                       ic_clid[30];
100         char                       ic_path[PATH_MAX];
101         int                        ic_scanned;
102         struct event               ic_event;
103         char                      *ic_id;
104         short                      ic_which;
105         TAILQ_ENTRY(idmap_client)  ic_next;
106 };
107
108 TAILQ_HEAD(idmap_clientq, idmap_client);
109
110 static void dirscancb(int, short, void *);
111 static void clntscancb(int, short, void *);
112 static int  nfsopen(struct idmap_client *);
113 static void nfscb(int, short, void *);
114 static void nfsdcb(int, short, void *);
115 static int  validateascii(char *, u_int32_t);
116 static int  addfield(char **, ssize_t *, char *);
117 static int  getfield(char **, char *, size_t);
118
119 static void imconv(struct idmap_client *, struct idmap_msg *);
120 static void idtonameres(struct idmap_msg *);
121 static void nametoidres(struct idmap_msg *);
122
123 static int nfsdopen(char *);
124 static int nfsdopenone(struct idmap_client *, short, char *);
125
126 size_t  strlcat(char *, const char *, size_t);
127 size_t  strlcpy(char *, const char *, size_t);
128 ssize_t atomicio(ssize_t (*)(), int, void *, size_t);
129 int     daemon(int, int);
130
131 static int verbose = 0;
132 static char domain[512];
133 static char pipefsdir[PATH_MAX];
134 static char *nobodyuser, *nobodygroup;
135 static uid_t nobodyuid;
136 static gid_t nobodygid;
137 static struct idmap_client nfsd_ic[2];
138
139 /* Used by cfg.c */
140 char *conf_path;
141
142 int
143 main(int argc, char **argv)
144 {
145         int fd = 0, opt, fg = 0, nfsdret = -1;
146         struct idmap_clientq icq;
147         struct event rootdirev, clntdirev;
148         struct event initialize;
149         struct passwd *pw;
150         struct group *gr;
151         struct stat sb;
152         char *xpipefsdir = NULL;
153         char *xdomain = NULL;
154         int serverstart = 1, clientstart = 1;
155
156         conf_path = _PATH_IDMAPDCONF;
157         nobodyuser = NFS4NOBODY_USER;
158         nobodygroup = NFS4NOBODY_GROUP;
159         strlcpy(pipefsdir, PIPEFS_DIR, sizeof(pipefsdir));
160
161 #define GETOPTSTR "vfd:p:U:G:c:CS"
162         opterr=0; /* Turn off error messages */
163         while ((opt = getopt(argc, argv, GETOPTSTR)) != -1) {
164                 if (opt == 'c')
165                         conf_path = optarg;
166                 if (opt == '?') {
167                         if (strchr(GETOPTSTR, optopt))
168                                 errx(1, "'-%c' option requires an argument.", optopt);
169                         else
170                                 errx(1, "'-%c' is an invalid argument.", optopt);
171                 }
172         }
173         optind = 1;
174
175         if (stat(conf_path, &sb) == -1 && (errno == ENOENT || errno == EACCES)) {
176                 warn("Skipping configuration file \"%s\"", conf_path);
177         } else {
178                 conf_init();
179                 verbose = conf_get_num("General", "Verbosity", 0);
180                 CONF_SAVE(xpipefsdir, conf_get_str("General", "Pipefs-Directory"));
181                 CONF_SAVE(xdomain, conf_get_str("General", "Domain"));
182                 if (xpipefsdir != NULL)
183                         strlcpy(pipefsdir, xpipefsdir, sizeof(pipefsdir));
184                 if (xdomain != NULL)
185                         strlcpy(domain, xdomain, sizeof(domain));
186                 CONF_SAVE(nobodyuser, conf_get_str("Mapping", "Nobody-User"));
187                 CONF_SAVE(nobodygroup, conf_get_str("Mapping", "Nobody-Group"));
188         }
189
190         while ((opt = getopt(argc, argv, GETOPTSTR)) != -1)
191                 switch (opt) {
192                 case 'v':
193                         verbose++;
194                         break;
195                 case 'f':
196                         fg = 1;
197                         break;
198                 case 'd':
199                         strlcpy(domain, optarg, sizeof(domain));
200                         break;
201                 case 'p':
202                         strlcpy(pipefsdir, optarg, sizeof(pipefsdir));
203                         break;
204                 case 'U':
205                         nobodyuser = optarg;
206                         break;
207                 case 'G':
208                         nobodygroup = optarg;
209                         break;
210                 case 'C':
211                         serverstart = 0;
212                         break;
213                 case 'S':
214                         clientstart = 0;
215                         break;
216                 default:
217                         break;
218                 }
219
220         if (!serverstart && !clientstart)
221                 errx(1, "it is illegal to specify both -C and -S");
222
223         strncat(pipefsdir, "/nfs", sizeof(pipefsdir));
224
225         if (domain[0] == '\0') {
226                 struct hostent *he;
227                 char hname[64], *c;
228
229                 if (gethostname(hname, sizeof(hname)) == -1)
230                         errx(1, "Error getting hostname");
231
232                 if ((he = gethostbyname(hname)) == NULL)
233                         errx(1, "Error resolving hostname: %s", hname);
234
235                 if ((c = strchr(he->h_name, '.')) == NULL || *++c == '\0')
236                         errx(1, "Error resolving domain, "
237                             "please use the -d switch");
238
239                 strlcpy(domain, c, sizeof(domain));
240         }
241
242         if ((pw = getpwnam(nobodyuser)) == NULL)
243                 errx(1, "Could not find user \"%s\"", nobodyuser);
244         nobodyuid = pw->pw_uid;
245
246         if ((gr = getgrnam(nobodygroup)) == NULL)
247                 errx(1, "Could not find group \"%s\"", nobodygroup);
248         nobodygid = gr->gr_gid;
249
250         if (strlen(domain) == 0)
251                 errx(1, "Invalid domain; please specify with -d switch");
252
253         if (verbose > 2)
254                 warnx("Using domain \"%s\"", domain);
255
256         if (!fg)
257                 daemon(0, 0);
258
259         event_init();
260
261         if (serverstart)
262                 nfsdret = nfsdopen(NFSD_DIR);
263
264         if (clientstart) {
265                 struct timeval now = {
266                         .tv_sec = 0,
267                         .tv_usec = 0,
268                 };
269
270                 if ((fd = open(pipefsdir, O_RDONLY)) == -1)
271                         err(1, "open(%s)", pipefsdir);
272
273                 if (fcntl(fd, F_SETSIG, SIGUSR1) == -1)
274                         err(1, "fcntl(%s)", pipefsdir);
275                 if (fcntl(fd, F_NOTIFY,
276                         DN_CREATE | DN_DELETE | DN_MODIFY | DN_MULTISHOT) == -1)
277                         err(1, "fcntl(%s)", pipefsdir);
278
279                 TAILQ_INIT(&icq);
280
281                 /* These events are persistent */
282                 signal_set(&rootdirev, SIGUSR1, dirscancb, &icq);
283                 signal_add(&rootdirev, NULL);
284                 signal_set(&clntdirev, SIGUSR2, clntscancb, &icq);
285                 signal_add(&clntdirev, NULL);
286
287                 /* Fetch current state */
288                 /* (Delay till start of event_dispatch to avoid possibly losing
289                  * a SIGUSR1 between here and the call to event_dispatch().) */
290                 evtimer_set(&initialize, dirscancb, &icq);
291                 evtimer_add(&initialize, &now);
292         }
293
294         if (nfsdret != 0 && fd == 0)
295                 errx(1, "Neither NFS client nor NFSd found");
296
297         event_dispatch();
298         /* NOTREACHED */
299         return 1;
300 }
301
302 static void
303 dirscancb(int fd, short which, void *data)
304 {
305         int nent, i;
306         struct dirent **ents;
307         struct idmap_client *ic;
308         char path[PATH_MAX];
309         struct idmap_clientq *icq = data;
310
311         nent = scandir(pipefsdir, &ents, NULL, alphasort);
312         if (nent == -1) {
313                 warn("scandir(%s)", pipefsdir);
314                 return;
315         }
316
317         for (i = 0;  i < nent; i++) {
318                 if (ents[i]->d_reclen > 4 &&
319                     strncmp(ents[i]->d_name, "clnt", 4) == 0) {
320                         TAILQ_FOREACH(ic, icq, ic_next)
321                             if (strcmp(ents[i]->d_name + 4, ic->ic_clid) == 0)
322                                     break;
323                         if (ic != NULL)
324                                 goto next;
325
326                         if ((ic = calloc(1, sizeof(*ic))) == NULL)
327                                 return;
328                         strlcpy(ic->ic_clid, ents[i]->d_name + 4,
329                             sizeof(ic->ic_clid));
330                         path[0] = '\0';
331                         snprintf(path, sizeof(path), "%s/%s",
332                             pipefsdir, ents[i]->d_name);
333
334                         if ((ic->ic_dirfd = open(path, O_RDONLY, 0)) == -1) {
335                                 warn("open(%s)", path);
336                                 free(ic);
337                                 return;
338                         }
339
340                         strlcat(path, "/idmap", sizeof(path));
341                         strlcpy(ic->ic_path, path, sizeof(ic->ic_path));
342
343                         if (verbose > 0)
344                                 warnx("New client: %s", ic->ic_clid);
345
346                         if (nfsopen(ic) == -1) {
347                                 close(ic->ic_dirfd);
348                                 free(ic);
349                                 return;
350                         }
351
352                         ic->ic_id = "Client";
353
354                         TAILQ_INSERT_TAIL(icq, ic, ic_next);
355
356                 next:
357                         ic->ic_scanned = 1;
358                 }
359         }
360
361         TAILQ_FOREACH(ic, icq, ic_next) {
362                 if (!ic->ic_scanned) {
363                         event_del(&ic->ic_event);
364                         close(ic->ic_fd);
365                         close(ic->ic_dirfd);
366                         TAILQ_REMOVE(icq, ic, ic_next);
367                         if (verbose > 0) {
368                                 warnx("Stale client: %s", ic->ic_clid);
369                                 warnx("\t-> closed %s", ic->ic_path);
370                         }
371                         free(ic);
372                 } else
373                         ic->ic_scanned = 0;
374         }
375         return;
376 }
377
378 static void
379 clntscancb(int fd, short which, void *data)
380 {
381         struct idmap_clientq *icq = data;
382         struct idmap_client *ic;
383
384         TAILQ_FOREACH(ic, icq, ic_next)
385                 if (ic->ic_fd == -1 && nfsopen(ic) == -1) {
386                         close(ic->ic_dirfd);
387                         TAILQ_REMOVE(icq, ic, ic_next);
388                         free(ic);
389                 }
390 }
391
392 static void
393 nfsdcb(int fd, short which, void *data)
394 {
395         struct idmap_client *ic = data;
396         struct idmap_msg im;
397         u_char buf[IDMAP_MAXMSGSZ + 1];
398         size_t len, bsiz;
399         char *bp, typebuf[IDMAP_MAXMSGSZ],
400                 buf1[IDMAP_MAXMSGSZ], authbuf[IDMAP_MAXMSGSZ], *p;
401
402         if (which != EV_READ)
403                 goto out;
404
405         if ((len = read(ic->ic_fd, buf, sizeof(buf))) == -1) {
406                 if (verbose > 0)
407                         warn("read(%s)", ic->ic_path);
408                 goto out;
409         }
410
411         /* Get rid of newline and terminate buffer*/
412         buf[len - 1] = '\0';
413         bp = buf;
414
415         memset(&im, 0, sizeof(im));
416
417         /* Authentication name -- ignored for now*/
418         if (getfield(&bp, authbuf, sizeof(authbuf)) == -1)
419                 return;
420
421         if (getfield(&bp, typebuf, sizeof(typebuf)) == -1)
422                 return;
423
424         im.im_type = strcmp(typebuf, "user") == 0 ?
425                 IDMAP_TYPE_USER : IDMAP_TYPE_GROUP;
426
427         switch (ic->ic_which) {
428         case IC_NAMEID:
429                 im.im_conv = IDMAP_CONV_NAMETOID;
430                 if (getfield(&bp, im.im_name, sizeof(im.im_name)) == -1)
431                         return;
432                 break;
433         case IC_IDNAME:
434                 im.im_conv = IDMAP_CONV_IDTONAME;
435                 if (getfield(&bp, buf1, sizeof(buf1)) == -1)
436                         return;
437                 if ((im.im_id = strtoul(buf1, (char **)NULL, 10)) == ULONG_MAX &&
438                     errno == ERANGE)
439                         return;
440
441                 break;
442         default:
443                 warnx("Unknown which type %d", ic->ic_which);
444                 return;
445         }
446
447         imconv(ic, &im);
448
449         buf[0] = '\0';
450         bp = buf;
451         bsiz = sizeof(buf);
452
453         /* Authentication name */
454         addfield(&bp, &bsiz, authbuf);
455
456         switch (ic->ic_which) {
457         case IC_NAMEID:
458                 /* Type */
459                 p = im.im_type == IDMAP_TYPE_USER ? "user" : "group";
460                 addfield(&bp, &bsiz, p);
461                 /* Name */
462                 addfield(&bp, &bsiz, im.im_name);
463 #define NFSD_EXPIRY 300 /* seconds */
464                 /* expiry */
465                 snprintf(buf1, sizeof(buf1), "%lu", time(NULL) + NFSD_EXPIRY);
466                 addfield(&bp, &bsiz, buf1);
467                 /* ID */
468                 snprintf(buf1, sizeof(buf1), "%u", im.im_id);
469                 addfield(&bp, &bsiz, buf1);
470
471                 //if (bsiz == sizeof(buf)) /* XXX */
472
473                 bp[-1] = '\n';
474
475                 break;
476         case IC_IDNAME:
477                 /* Type */
478                 p = im.im_type == IDMAP_TYPE_USER ? "user" : "group";
479                 addfield(&bp, &bsiz, p);
480                 /* ID */
481                 snprintf(buf1, sizeof(buf1), "%u", im.im_id);
482                 addfield(&bp, &bsiz, buf1);
483                 /* expiry */
484                 snprintf(buf1, sizeof(buf1), "%lu", time(NULL) + NFSD_EXPIRY);
485                 addfield(&bp, &bsiz, buf1);
486                 /* Name */
487                 addfield(&bp, &bsiz, im.im_name);
488
489                 bp[-1] = '\n';
490
491                 break;
492         default:
493                 warnx("Unknown which type %d", ic->ic_which);
494                 return;
495         }
496
497         bsiz = sizeof(buf) - bsiz;
498
499         if (atomicio(write, ic->ic_fd, buf, bsiz) != bsiz && verbose > 0)
500                 warn("write(%s)", ic->ic_path);
501
502 out:
503         event_add(&ic->ic_event, NULL);
504 }
505
506 static void
507 imconv(struct idmap_client *ic, struct idmap_msg *im)
508 {
509         switch (im->im_conv) {
510         case IDMAP_CONV_IDTONAME:
511                 idtonameres(im);
512                 if (verbose > 1)
513                         warnx("%s %s: (%s) id \"%d\" -> name \"%s\"",
514                             ic->ic_id, ic->ic_clid,
515                             im->im_type == IDMAP_TYPE_USER ? "user" : "group",
516                             im->im_id, im->im_name);
517                 break;
518         case IDMAP_CONV_NAMETOID:
519                 if (validateascii(im->im_name, sizeof(im->im_name)) == -1) {
520                         im->im_status |= IDMAP_STATUS_INVALIDMSG;
521                         return;
522                 }
523                 nametoidres(im);
524                 if (verbose > 1)
525                         warnx("%s %s: (%s) name \"%s\" -> id \"%d\"",
526                             ic->ic_id, ic->ic_clid,
527                             im->im_type == IDMAP_TYPE_USER ? "user" : "group",
528                             im->im_name, im->im_id);
529                 break;
530         default:
531                 warnx("Invalid conversion type (%d) in message", im->im_conv);
532                 im->im_status |= IDMAP_STATUS_INVALIDMSG;
533                 break;
534         }
535 }
536
537 static void
538 nfscb(int fd, short which, void *data)
539 {
540         struct idmap_client *ic = data;
541         struct idmap_msg im;
542
543         if (which != EV_READ)
544                 goto out;
545
546         if (atomicio(read, ic->ic_fd, &im, sizeof(im)) != sizeof(im)) {
547                 if (verbose > 0)
548                         warn("read(%s)", ic->ic_path);
549                 if (errno == EPIPE)
550                         return;
551                 goto out;
552         }
553
554         imconv(ic, &im);
555
556         if (atomicio(write, ic->ic_fd, &im, sizeof(im)) != sizeof(im))
557                 warn("write(%s)", ic->ic_path);
558 out:
559         event_add(&ic->ic_event, NULL);
560 }
561
562 static int
563 nfsdopen(char *path)
564 {
565         return ((nfsdopenone(&nfsd_ic[0], IC_NAMEID, path) == 0 &&
566                     nfsdopenone(&nfsd_ic[1], IC_IDNAME, path) == 0) ? 0 : -1);
567 }
568
569 static int
570 nfsdopenone(struct idmap_client *ic, short which, char *path)
571 {
572         char *whichstr;
573
574         whichstr = which == IC_IDNAME ? "idtoname" : "nametoid";
575         snprintf(ic->ic_path, sizeof(ic->ic_path),
576                 "%s/nfs4.%s/channel", path, whichstr);
577         if ((ic->ic_fd = open(ic->ic_path, O_RDWR, 0)) == -1) {
578                 warn("%s", ic->ic_path);
579                 return (-1);
580         }
581
582         event_set(&ic->ic_event, ic->ic_fd, EV_READ, nfsdcb, ic);
583         event_add(&ic->ic_event, NULL);
584
585         ic->ic_which = which;
586         ic->ic_id = "Server";
587         strlcpy(ic->ic_clid, domain, sizeof(ic->ic_clid));
588
589         if (verbose > 0)
590                 warnx("Opened %s", ic->ic_path);
591
592         return (0);
593 }
594
595 static int
596 nfsopen(struct idmap_client *ic)
597 {
598         if ((ic->ic_fd = open(ic->ic_path, O_RDWR, 0)) == -1) {
599                 switch (errno) {
600                 case ENOENT:
601                         fcntl(ic->ic_dirfd, F_SETSIG, SIGUSR2);
602                         fcntl(ic->ic_dirfd, F_NOTIFY,
603                             DN_CREATE | DN_DELETE | DN_MULTISHOT);
604                         break;
605                 default:
606                         warn("open(%s)", ic->ic_path);
607                         return (-1);
608                 }
609         } else {
610                 event_set(&ic->ic_event, ic->ic_fd, EV_READ, nfscb, ic);
611                 event_add(&ic->ic_event, NULL);
612                 fcntl(ic->ic_dirfd, F_SETSIG, 0);
613                 fcntl(ic->ic_dirfd, F_NOTIFY, 0);
614                 if (verbose > 0)
615                         warnx("Opened %s", ic->ic_path);
616         }
617
618         return (0);
619 }
620
621 static int write_name(char *dest, char *localname, char *domain, size_t len)
622 {
623         if (strlen(localname) + 1 + strlen(domain) + 1 > len) {
624                 return -ENOMEM; /* XXX: Is there an -ETOOLONG? */
625         }
626         strcpy(dest, localname);
627         strcat(dest, "@");
628         strcat(dest, domain);
629         return 0;
630 }
631
632 static void
633 idtonameres(struct idmap_msg *im)
634 {
635         int ret = 0;
636
637         switch (im->im_type) {
638         case IDMAP_TYPE_USER:
639                 ret = nfs4_uid_to_name(im->im_id, domain, im->im_name,
640                                 sizeof(im->im_name));
641                 if (ret)
642                         write_name(im->im_name, nobodyuser, domain,
643                                         sizeof(im->im_name));
644                 break;
645         case IDMAP_TYPE_GROUP:
646                 ret = nfs4_gid_to_name(im->im_id, domain, im->im_name,
647                                 sizeof(im->im_name));
648                 if (ret)
649                         write_name(im->im_name, nobodygroup, domain,
650                                         sizeof(im->im_name));
651                 break;
652         }
653         /* XXX Hack? would rather return failure instead of writing nobody
654          * as above, but kernel seems not to deal well with that as of
655          * 2.6.8-rc3. */
656         im->im_status = IDMAP_STATUS_SUCCESS;
657 }
658
659 static void
660 nametoidres(struct idmap_msg *im)
661 {
662         int ret = 0;
663
664         switch (im->im_type) {
665         case IDMAP_TYPE_USER:
666                 ret = nfs4_name_to_uid(im->im_name, &im->im_id);
667                 if (ret)
668                         im->im_id = nobodyuid;
669                 break;
670         case IDMAP_TYPE_GROUP:
671                 ret = nfs4_name_to_gid(im->im_name, &im->im_id);
672                 if (ret)
673                         im->im_id = nobodygid;
674                 break;
675         }
676         /* XXX Hack? would rather return failure instead of writing nobody
677          * as above, but kernel seems not to deal well with that as of
678          * 2.6.8-rc3. */
679         im->im_status = IDMAP_STATUS_SUCCESS;
680 }
681
682 static int
683 validateascii(char *string, u_int32_t len)
684 {
685         int i;
686
687         for (i = 0; i < len; i++) {
688                 if (string[i] == '\0')
689                         break;
690
691                 if (string[i] & 0x80)
692                         return (-1);
693         }
694
695         if (string[i] != '\0')
696                 return (-1);
697
698         return (i + 1);
699 }
700
701 static int
702 addfield(char **bpp, ssize_t *bsizp, char *fld)
703 {
704         char ch, *bp = *bpp;
705         ssize_t bsiz = *bsizp;
706
707         while ((ch = *fld++) != '\0' && bsiz > 0) {
708                 switch(ch) {
709                 case ' ':
710                 case '\t':
711                 case '\n':
712                 case '\\':
713                         if (bsiz >= 4) {
714                                 bp += snprintf(bp, bsiz, "\\%03o", ch);
715                                 bsiz -= 4;
716                         }
717                         break;
718                 default:
719                         *bp++ = ch;
720                         bsiz--;
721                         break;
722                 }
723         }
724
725         if (bsiz < 1 || ch != '\0')
726                 return (-1);
727
728         *bp++ = ' ';
729         bsiz--;
730
731         *bpp = bp;
732         *bsizp = bsiz;
733
734         return (0);
735 }
736
737 static int
738 getfield(char **bpp, char *fld, size_t fldsz)
739 {
740         char *bp;
741         u_int val, n;
742
743         while ((bp = strsep(bpp, " ")) != NULL && bp[0] == '\0')
744                 ;
745
746         if (bp == NULL || bp[0] == '\0' || bp[0] == '\n')
747                 return (-1);
748
749         while (*bp != '\0' && fldsz > 1) {
750                 if (*bp == '\\') {
751                         if ((n = sscanf(bp, "\\%03o", &val)) != 1)
752                                 return (-1);
753                         if (val > (char)-1)
754                                 return (-1);
755                         *fld++ = (char)val;
756                         bp += 4;
757                 } else {
758                         *fld++ = *bp;
759                         bp++;
760                 }
761                 fldsz--;
762         }
763
764         if (*bp != '\0')
765                 return (-1);
766         *fld = '\0';
767
768         return (0);
769 }