]> git.decadent.org.uk Git - nfs-utils.git/blob - utils/idmapd/idmapd.c
d2ecbebafdcb2dd08c79e60baaf6a3f2798007a6
[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 'p':
199                         strlcpy(pipefsdir, optarg, sizeof(pipefsdir));
200                         break;
201                 case 'd':
202                 case 'U':
203                 case 'G':
204                         errx(1, "the -d, -U, and -G options have been removed;"
205                                 " please use the configuration file instead.");
206                 case 'C':
207                         serverstart = 0;
208                         break;
209                 case 'S':
210                         clientstart = 0;
211                         break;
212                 default:
213                         break;
214                 }
215
216         if (!serverstart && !clientstart)
217                 errx(1, "it is illegal to specify both -C and -S");
218
219         strncat(pipefsdir, "/nfs", sizeof(pipefsdir));
220
221         if (domain[0] == '\0') {
222                 struct hostent *he;
223                 char hname[64], *c;
224
225                 if (gethostname(hname, sizeof(hname)) == -1)
226                         errx(1, "Error getting hostname");
227
228                 if ((he = gethostbyname(hname)) == NULL)
229                         errx(1, "Error resolving hostname: %s", hname);
230
231                 if ((c = strchr(he->h_name, '.')) == NULL || *++c == '\0')
232                         errx(1, "Error resolving domain, "
233                             "please use the -d switch");
234
235                 strlcpy(domain, c, sizeof(domain));
236         }
237
238         if ((pw = getpwnam(nobodyuser)) == NULL)
239                 errx(1, "Could not find user \"%s\"", nobodyuser);
240         nobodyuid = pw->pw_uid;
241
242         if ((gr = getgrnam(nobodygroup)) == NULL)
243                 errx(1, "Could not find group \"%s\"", nobodygroup);
244         nobodygid = gr->gr_gid;
245
246         if (strlen(domain) == 0)
247                 errx(1, "Invalid domain; please specify with -d switch");
248
249         if (verbose > 2)
250                 warnx("Using domain \"%s\"", domain);
251
252         if (!fg)
253                 daemon(0, 0);
254
255         event_init();
256
257         if (serverstart)
258                 nfsdret = nfsdopen(NFSD_DIR);
259
260         if (clientstart) {
261                 struct timeval now = {
262                         .tv_sec = 0,
263                         .tv_usec = 0,
264                 };
265
266                 if ((fd = open(pipefsdir, O_RDONLY)) == -1)
267                         err(1, "open(%s)", pipefsdir);
268
269                 if (fcntl(fd, F_SETSIG, SIGUSR1) == -1)
270                         err(1, "fcntl(%s)", pipefsdir);
271                 if (fcntl(fd, F_NOTIFY,
272                         DN_CREATE | DN_DELETE | DN_MODIFY | DN_MULTISHOT) == -1)
273                         err(1, "fcntl(%s)", pipefsdir);
274
275                 TAILQ_INIT(&icq);
276
277                 /* These events are persistent */
278                 signal_set(&rootdirev, SIGUSR1, dirscancb, &icq);
279                 signal_add(&rootdirev, NULL);
280                 signal_set(&clntdirev, SIGUSR2, clntscancb, &icq);
281                 signal_add(&clntdirev, NULL);
282
283                 /* Fetch current state */
284                 /* (Delay till start of event_dispatch to avoid possibly losing
285                  * a SIGUSR1 between here and the call to event_dispatch().) */
286                 evtimer_set(&initialize, dirscancb, &icq);
287                 evtimer_add(&initialize, &now);
288         }
289
290         if (nfsdret != 0 && fd == 0)
291                 errx(1, "Neither NFS client nor NFSd found");
292
293         if (event_dispatch() < 0)
294                 errx(1, "event_dispatch: returns errno %d (%s)", errno, strerror(errno));
295         /* NOTREACHED */
296         return 1;
297 }
298
299 static void
300 dirscancb(int fd, short which, void *data)
301 {
302         int nent, i;
303         struct dirent **ents;
304         struct idmap_client *ic;
305         char path[PATH_MAX];
306         struct idmap_clientq *icq = data;
307
308         nent = scandir(pipefsdir, &ents, NULL, alphasort);
309         if (nent == -1) {
310                 warn("scandir(%s)", pipefsdir);
311                 return;
312         }
313
314         for (i = 0;  i < nent; i++) {
315                 if (ents[i]->d_reclen > 4 &&
316                     strncmp(ents[i]->d_name, "clnt", 4) == 0) {
317                         TAILQ_FOREACH(ic, icq, ic_next)
318                             if (strcmp(ents[i]->d_name + 4, ic->ic_clid) == 0)
319                                     break;
320                         if (ic != NULL)
321                                 goto next;
322
323                         if ((ic = calloc(1, sizeof(*ic))) == NULL)
324                                 return;
325                         strlcpy(ic->ic_clid, ents[i]->d_name + 4,
326                             sizeof(ic->ic_clid));
327                         path[0] = '\0';
328                         snprintf(path, sizeof(path), "%s/%s",
329                             pipefsdir, ents[i]->d_name);
330
331                         if ((ic->ic_dirfd = open(path, O_RDONLY, 0)) == -1) {
332                                 warn("open(%s)", path);
333                                 free(ic);
334                                 return;
335                         }
336
337                         strlcat(path, "/idmap", sizeof(path));
338                         strlcpy(ic->ic_path, path, sizeof(ic->ic_path));
339
340                         if (verbose > 0)
341                                 warnx("New client: %s", ic->ic_clid);
342
343                         if (nfsopen(ic) == -1) {
344                                 close(ic->ic_dirfd);
345                                 free(ic);
346                                 return;
347                         }
348
349                         ic->ic_id = "Client";
350
351                         TAILQ_INSERT_TAIL(icq, ic, ic_next);
352
353                 next:
354                         ic->ic_scanned = 1;
355                 }
356         }
357
358         TAILQ_FOREACH(ic, icq, ic_next) {
359                 if (!ic->ic_scanned) {
360                         event_del(&ic->ic_event);
361                         close(ic->ic_fd);
362                         close(ic->ic_dirfd);
363                         TAILQ_REMOVE(icq, ic, ic_next);
364                         if (verbose > 0) {
365                                 warnx("Stale client: %s", ic->ic_clid);
366                                 warnx("\t-> closed %s", ic->ic_path);
367                         }
368                         free(ic);
369                 } else
370                         ic->ic_scanned = 0;
371         }
372         return;
373 }
374
375 static void
376 clntscancb(int fd, short which, void *data)
377 {
378         struct idmap_clientq *icq = data;
379         struct idmap_client *ic;
380
381         TAILQ_FOREACH(ic, icq, ic_next)
382                 if (ic->ic_fd == -1 && nfsopen(ic) == -1) {
383                         close(ic->ic_dirfd);
384                         TAILQ_REMOVE(icq, ic, ic_next);
385                         free(ic);
386                 }
387 }
388
389 static void
390 nfsdcb(int fd, short which, void *data)
391 {
392         struct idmap_client *ic = data;
393         struct idmap_msg im;
394         u_char buf[IDMAP_MAXMSGSZ + 1];
395         size_t len, bsiz;
396         char *bp, typebuf[IDMAP_MAXMSGSZ],
397                 buf1[IDMAP_MAXMSGSZ], authbuf[IDMAP_MAXMSGSZ], *p;
398
399         if (which != EV_READ)
400                 goto out;
401
402         if ((len = read(ic->ic_fd, buf, sizeof(buf))) == -1) {
403                 warnx("nfsdcb: read(%s) failed: errno %d (%s)",
404                         ic->ic_path, errno, strerror(errno));
405                 goto out;
406         }
407
408         /* Get rid of newline and terminate buffer*/
409         buf[len - 1] = '\0';
410         bp = buf;
411
412         memset(&im, 0, sizeof(im));
413
414         /* Authentication name -- ignored for now*/
415         if (getfield(&bp, authbuf, sizeof(authbuf)) == -1) {
416                 warnx("nfsdcb: bad authentication name in upcall\n");
417                 return;
418         }
419         if (getfield(&bp, typebuf, sizeof(typebuf)) == -1) {
420                 warnx("nfsdcb: bad type in upcall\n");
421                 return;
422         }
423         if (verbose > 0)
424                 warnx("nfsdcb: authbuf=%s authtype=%s", authbuf, typebuf);
425
426         im.im_type = strcmp(typebuf, "user") == 0 ?
427                 IDMAP_TYPE_USER : IDMAP_TYPE_GROUP;
428
429         switch (ic->ic_which) {
430         case IC_NAMEID:
431                 im.im_conv = IDMAP_CONV_NAMETOID;
432                 if (getfield(&bp, im.im_name, sizeof(im.im_name)) == -1) {
433                         warnx("nfsdcb: bad name in upcall\n");
434                         return;
435                 }
436                 break;
437         case IC_IDNAME:
438                 im.im_conv = IDMAP_CONV_IDTONAME;
439                 if (getfield(&bp, buf1, sizeof(buf1)) == -1) {
440                         warnx("nfsdcb: bad id in upcall\n");
441                         return;
442                 }
443                 if ((im.im_id = strtoul(buf1, (char **)NULL, 10)) == ULONG_MAX &&
444                     errno == ERANGE) {
445                         warnx("nfsdcb: id '%s' too big!\n", buf1);
446                         return;
447                 }
448
449                 break;
450         default:
451                 warnx("Unknown which type %d", ic->ic_which);
452                 return;
453         }
454
455         imconv(ic, &im);
456
457         buf[0] = '\0';
458         bp = buf;
459         bsiz = sizeof(buf);
460
461         /* Authentication name */
462         addfield(&bp, &bsiz, authbuf);
463
464         switch (ic->ic_which) {
465         case IC_NAMEID:
466                 /* Type */
467                 p = im.im_type == IDMAP_TYPE_USER ? "user" : "group";
468                 addfield(&bp, &bsiz, p);
469                 /* Name */
470                 addfield(&bp, &bsiz, im.im_name);
471 #define NFSD_EXPIRY 300 /* seconds */
472                 /* expiry */
473                 snprintf(buf1, sizeof(buf1), "%lu", time(NULL) + NFSD_EXPIRY);
474                 addfield(&bp, &bsiz, buf1);
475                 /* ID */
476                 snprintf(buf1, sizeof(buf1), "%u", im.im_id);
477                 addfield(&bp, &bsiz, buf1);
478
479                 //if (bsiz == sizeof(buf)) /* XXX */
480
481                 bp[-1] = '\n';
482
483                 break;
484         case IC_IDNAME:
485                 /* Type */
486                 p = im.im_type == IDMAP_TYPE_USER ? "user" : "group";
487                 addfield(&bp, &bsiz, p);
488                 /* ID */
489                 snprintf(buf1, sizeof(buf1), "%u", im.im_id);
490                 addfield(&bp, &bsiz, buf1);
491                 /* expiry */
492                 snprintf(buf1, sizeof(buf1), "%lu", time(NULL) + NFSD_EXPIRY);
493                 addfield(&bp, &bsiz, buf1);
494                 /* Name */
495                 addfield(&bp, &bsiz, im.im_name);
496
497                 bp[-1] = '\n';
498
499                 break;
500         default:
501                 warnx("Unknown which type %d", ic->ic_which);
502                 return;
503         }
504
505         bsiz = sizeof(buf) - bsiz;
506
507         if (atomicio(write, ic->ic_fd, buf, bsiz) != bsiz)
508                 warnx("nfsdcb: write(%s) failed: errno %d (%s)",
509                         ic->ic_path, errno, strerror(errno));
510
511 out:
512         event_add(&ic->ic_event, NULL);
513 }
514
515 static void
516 imconv(struct idmap_client *ic, struct idmap_msg *im)
517 {
518         switch (im->im_conv) {
519         case IDMAP_CONV_IDTONAME:
520                 idtonameres(im);
521                 if (verbose > 1)
522                         warnx("%s %s: (%s) id \"%d\" -> name \"%s\"",
523                             ic->ic_id, ic->ic_clid,
524                             im->im_type == IDMAP_TYPE_USER ? "user" : "group",
525                             im->im_id, im->im_name);
526                 break;
527         case IDMAP_CONV_NAMETOID:
528                 if (validateascii(im->im_name, sizeof(im->im_name)) == -1) {
529                         im->im_status |= IDMAP_STATUS_INVALIDMSG;
530                         return;
531                 }
532                 nametoidres(im);
533                 if (verbose > 1)
534                         warnx("%s %s: (%s) name \"%s\" -> id \"%d\"",
535                             ic->ic_id, ic->ic_clid,
536                             im->im_type == IDMAP_TYPE_USER ? "user" : "group",
537                             im->im_name, im->im_id);
538                 break;
539         default:
540                 warnx("Invalid conversion type (%d) in message", im->im_conv);
541                 im->im_status |= IDMAP_STATUS_INVALIDMSG;
542                 break;
543         }
544 }
545
546 static void
547 nfscb(int fd, short which, void *data)
548 {
549         struct idmap_client *ic = data;
550         struct idmap_msg im;
551
552         if (which != EV_READ)
553                 goto out;
554
555         if (atomicio(read, ic->ic_fd, &im, sizeof(im)) != sizeof(im)) {
556                 if (verbose > 0)
557                         warn("read(%s)", ic->ic_path);
558                 if (errno == EPIPE)
559                         return;
560                 goto out;
561         }
562
563         imconv(ic, &im);
564
565         if (atomicio(write, ic->ic_fd, &im, sizeof(im)) != sizeof(im))
566                 warn("write(%s)", ic->ic_path);
567 out:
568         event_add(&ic->ic_event, NULL);
569 }
570
571 static int
572 nfsdopen(char *path)
573 {
574         return ((nfsdopenone(&nfsd_ic[0], IC_NAMEID, path) == 0 &&
575                     nfsdopenone(&nfsd_ic[1], IC_IDNAME, path) == 0) ? 0 : -1);
576 }
577
578 static int
579 nfsdopenone(struct idmap_client *ic, short which, char *path)
580 {
581         char *whichstr;
582
583         whichstr = which == IC_IDNAME ? "idtoname" : "nametoid";
584         snprintf(ic->ic_path, sizeof(ic->ic_path),
585                 "%s/nfs4.%s/channel", path, whichstr);
586         if ((ic->ic_fd = open(ic->ic_path, O_RDWR, 0)) == -1) {
587                 if (verbose > 0)
588                         warnx("Opening %s failed: errno %d (%s)",
589                                 ic->ic_path, errno, strerror(errno));
590                 return (-1);
591         }
592
593         event_set(&ic->ic_event, ic->ic_fd, EV_READ, nfsdcb, ic);
594         event_add(&ic->ic_event, NULL);
595
596         ic->ic_which = which;
597         ic->ic_id = "Server";
598         strlcpy(ic->ic_clid, domain, sizeof(ic->ic_clid));
599
600         if (verbose > 0)
601                 warnx("Opened %s", ic->ic_path);
602
603         return (0);
604 }
605
606 static int
607 nfsopen(struct idmap_client *ic)
608 {
609         if ((ic->ic_fd = open(ic->ic_path, O_RDWR, 0)) == -1) {
610                 switch (errno) {
611                 case ENOENT:
612                         fcntl(ic->ic_dirfd, F_SETSIG, SIGUSR2);
613                         fcntl(ic->ic_dirfd, F_NOTIFY,
614                             DN_CREATE | DN_DELETE | DN_MULTISHOT);
615                         break;
616                 default:
617                         warn("open(%s)", ic->ic_path);
618                         return (-1);
619                 }
620         } else {
621                 event_set(&ic->ic_event, ic->ic_fd, EV_READ, nfscb, ic);
622                 event_add(&ic->ic_event, NULL);
623                 fcntl(ic->ic_dirfd, F_SETSIG, 0);
624                 fcntl(ic->ic_dirfd, F_NOTIFY, 0);
625                 if (verbose > 0)
626                         warnx("Opened %s", ic->ic_path);
627         }
628
629         return (0);
630 }
631
632 static int write_name(char *dest, char *localname, char *domain, size_t len)
633 {
634         if (strlen(localname) + 1 + strlen(domain) + 1 > len) {
635                 return -ENOMEM; /* XXX: Is there an -ETOOLONG? */
636         }
637         strcpy(dest, localname);
638         strcat(dest, "@");
639         strcat(dest, domain);
640         return 0;
641 }
642
643 static void
644 idtonameres(struct idmap_msg *im)
645 {
646         int ret = 0;
647
648         switch (im->im_type) {
649         case IDMAP_TYPE_USER:
650                 ret = nfs4_uid_to_name(im->im_id, domain, im->im_name,
651                                 sizeof(im->im_name));
652                 if (ret)
653                         write_name(im->im_name, nobodyuser, domain,
654                                         sizeof(im->im_name));
655                 break;
656         case IDMAP_TYPE_GROUP:
657                 ret = nfs4_gid_to_name(im->im_id, domain, im->im_name,
658                                 sizeof(im->im_name));
659                 if (ret)
660                         write_name(im->im_name, nobodygroup, domain,
661                                         sizeof(im->im_name));
662                 break;
663         }
664         /* XXX Hack? would rather return failure instead of writing nobody
665          * as above, but kernel seems not to deal well with that as of
666          * 2.6.8-rc3. */
667         im->im_status = IDMAP_STATUS_SUCCESS;
668 }
669
670 static void
671 nametoidres(struct idmap_msg *im)
672 {
673         int ret = 0;
674
675         switch (im->im_type) {
676         case IDMAP_TYPE_USER:
677                 ret = nfs4_name_to_uid(im->im_name, &im->im_id);
678                 if (ret)
679                         im->im_id = nobodyuid;
680                 break;
681         case IDMAP_TYPE_GROUP:
682                 ret = nfs4_name_to_gid(im->im_name, &im->im_id);
683                 if (ret)
684                         im->im_id = nobodygid;
685                 break;
686         }
687         /* XXX Hack? would rather return failure instead of writing nobody
688          * as above, but kernel seems not to deal well with that as of
689          * 2.6.8-rc3. */
690         im->im_status = IDMAP_STATUS_SUCCESS;
691 }
692
693 static int
694 validateascii(char *string, u_int32_t len)
695 {
696         int i;
697
698         for (i = 0; i < len; i++) {
699                 if (string[i] == '\0')
700                         break;
701
702                 if (string[i] & 0x80)
703                         return (-1);
704         }
705
706         if (string[i] != '\0')
707                 return (-1);
708
709         return (i + 1);
710 }
711
712 static int
713 addfield(char **bpp, ssize_t *bsizp, char *fld)
714 {
715         char ch, *bp = *bpp;
716         ssize_t bsiz = *bsizp;
717
718         while ((ch = *fld++) != '\0' && bsiz > 0) {
719                 switch(ch) {
720                 case ' ':
721                 case '\t':
722                 case '\n':
723                 case '\\':
724                         if (bsiz >= 4) {
725                                 bp += snprintf(bp, bsiz, "\\%03o", ch);
726                                 bsiz -= 4;
727                         }
728                         break;
729                 default:
730                         *bp++ = ch;
731                         bsiz--;
732                         break;
733                 }
734         }
735
736         if (bsiz < 1 || ch != '\0')
737                 return (-1);
738
739         *bp++ = ' ';
740         bsiz--;
741
742         *bpp = bp;
743         *bsizp = bsiz;
744
745         return (0);
746 }
747
748 static int
749 getfield(char **bpp, char *fld, size_t fldsz)
750 {
751         char *bp;
752         u_int val, n;
753
754         while ((bp = strsep(bpp, " ")) != NULL && bp[0] == '\0')
755                 ;
756
757         if (bp == NULL || bp[0] == '\0' || bp[0] == '\n')
758                 return (-1);
759
760         while (*bp != '\0' && fldsz > 1) {
761                 if (*bp == '\\') {
762                         if ((n = sscanf(bp, "\\%03o", &val)) != 1)
763                                 return (-1);
764                         if (val > (char)-1)
765                                 return (-1);
766                         *fld++ = (char)val;
767                         bp += 4;
768                 } else {
769                         *fld++ = *bp;
770                         bp++;
771                 }
772                 fldsz--;
773         }
774
775         if (*bp != '\0')
776                 return (-1);
777         *fld = '\0';
778
779         return (0);
780 }