]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - sbin/idmapd/idmapd.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / sbin / idmapd / idmapd.c
1 /* $FreeBSD$ */
2 /* $Id: idmapd.c,v 1.5 2003/11/05 14:58:58 rees Exp $ */
3
4 /*
5  * copyright (c) 2003
6  * the regents of the university of michigan
7  * all rights reserved
8  * 
9  * permission is granted to use, copy, create derivative works and redistribute
10  * this software and such derivative works for any purpose, so long as the name
11  * of the university of michigan is not used in any advertising or publicity
12  * pertaining to the use or distribution of this software without specific,
13  * written prior authorization.  if the above copyright notice or any other
14  * identification of the university of michigan is included in any copy of any
15  * portion of this software, then the disclaimer below must also be included.
16  * 
17  * this software is provided as is, without representation from the university
18  * of michigan as to its fitness for any purpose, and without warranty by the
19  * university of michigan of any kind, either express or implied, including
20  * without limitation the implied warranties of merchantability and fitness for
21  * a particular purpose. the regents of the university of michigan shall not be
22  * liable for any damages, including special, indirect, incidental, or
23  * consequential damages, with respect to any claim arising out of or in
24  * connection with the use of the software, even if it has been or is hereafter
25  * advised of the possibility of such damages.
26  */
27
28 /* XXX ignores the domain of received names. */
29
30 #include <sys/types.h>
31 #include <sys/ioctl.h>
32 #include <sys/syscall.h>
33 #include <sys/errno.h>
34 #include <sys/stat.h>
35 #include <sys/time.h>
36 #include <sys/queue.h>
37
38 #include <nfs4client/nfs4_dev.h>
39 #include <nfs4client/nfs4_idmap.h>
40
41 #include <stdio.h>
42 #include <fcntl.h>
43 #include <assert.h>
44 #include <string.h>
45 #include <stdlib.h>
46 #include <unistd.h>
47 #include <pwd.h>
48 #include <grp.h>
49
50 #define DEV_PATH "/dev/nfs4"
51
52 #define DOMAIN  "@FreeBSD.org"
53 #define BADUSER "nobody"
54 #define BADGROUP "nogroup"
55 #define BADUID  65534
56 #define BADGID  65533
57
58 struct idmap_e {
59         struct nfs4dev_msg msg;
60         TAILQ_ENTRY(idmap_e) next;
61 };
62
63 int fd, verbose;
64 char *domain = DOMAIN;
65
66 TAILQ_HEAD(, idmap_e) upcall_q;
67
68 #define add_idmap_e(E) do {     \
69         assert(E != NULL);      \
70         TAILQ_INSERT_TAIL(&upcall_q, E, next); \
71 } while(0)
72
73 #define remove_idmap_e(E) do {  \
74         assert(E != NULL && !TAILQ_EMPTY(&upcall_q));   \
75         E = TAILQ_FIRST(&upcall_q);     \
76         TAILQ_REMOVE(&upcall_q, E, next); \
77 } while(0)
78
79 #define get_idmap_e(E) do {     \
80         if ((E = (struct idmap_e *) malloc(sizeof(struct idmap_e))) == NULL) {\
81                 fprintf(stderr, "get_idmap_e(): error in malloc\n");\
82         } } while(0)
83
84 #define put_idmap_e(E) free(E)
85
86 /* from marius */
87 int
88 validateascii(char *string, u_int32_t len)
89 {
90         int i;
91
92         for (i = 0; i < len; i++) {
93                 if (string[i] == '\0')
94                         break;
95                 if (string[i] & 0x80)
96                         return (-1);
97         }
98
99         if (string[i] != '\0')
100                 return (-1);
101         return (i + 1);
102 }
103
104 char *
105 idmap_prune_domain(struct idmap_msg * m)
106 {
107         size_t i;
108         size_t len;
109         char * ret = NULL;
110
111         if (m == NULL)
112                 return NULL;
113
114         len = m->id_namelen;
115
116         if (validateascii(m->id_name, len) < 0) {
117                 fprintf(stderr, "msg has invalid ascii\n");
118                 return NULL;
119         }
120
121         for (i=0; i < len && m->id_name[i] != '@' ; i++);
122
123         ret = (char *)malloc(i+1);
124         if (ret == NULL)
125           return NULL;
126
127         bcopy(m->id_name, ret, i);
128         ret[i] = '\0';
129
130         return ret;
131 }
132
133 int
134 idmap_add_domain(struct idmap_msg * m, char * name)
135 {
136         size_t len, nlen;
137
138         if (m == NULL || name == NULL)
139                 return -1;
140
141         len = strlen(name);
142
143         nlen = len + strlen(domain);
144
145         if (nlen > IDMAP_MAXNAMELEN)
146                 return -1;
147
148         bcopy(name, &m->id_name[0], len);
149         bcopy(domain, &m->id_name[len], strlen(domain));
150
151         m->id_name[nlen] = '\0';
152         m->id_namelen = nlen;
153
154         return 0;
155 }
156
157 int
158 idmap_name(struct idmap_msg * m, char *name)
159 {
160         if (m == NULL || name == NULL || m->id_namelen != 0)
161                 return -1;
162
163         if (idmap_add_domain(m, name))
164                 return -1;
165
166         return 0;
167 }
168
169 int
170 idmap_id(struct idmap_msg * m, ident_t id)
171 {
172         if (m == NULL || m->id_namelen == 0) {
173                 fprintf(stderr, "idmap_id: bad msg\n");
174                 return -1;
175         }
176
177         switch(m->id_type) {
178         case IDMAP_TYPE_UID:
179                 m->id_id.uid = id.uid;
180                 break;
181         case IDMAP_TYPE_GID:
182                 m->id_id.gid = id.gid;
183                 break;
184         default:
185                 return -1;
186                 break;
187         };
188
189         return 0;
190 }
191
192 int
193 idmap_service(struct idmap_e * e)
194 {
195         struct idmap_msg * m;
196         struct passwd * pwd;
197         struct group * grp;
198         ident_t id;
199         char * name;
200
201         if (e == NULL) {
202                 fprintf(stderr, "bad entry\n");
203                 return -1;
204         }
205
206         if (e->msg.msg_vers != NFS4DEV_VERSION) {
207                 fprintf(stderr, "kernel/userland version mismatch! %d/%d\n",
208                     e->msg.msg_vers, NFS4DEV_VERSION);
209                 return -1;
210         }
211
212         if (e->msg.msg_type != NFS4DEV_TYPE_IDMAP) {
213                 fprintf(stderr, "bad type!\n");
214                 return -1;
215         }
216
217         if (e->msg.msg_len != sizeof(struct idmap_msg)) {
218                 fprintf(stderr, "bad message length: %zu/%zu\n", e->msg.msg_len,
219                         sizeof(struct idmap_msg));
220                 return -1;
221         }
222
223         if (verbose)
224                 printf("servicing msg xid: %x\n", e->msg.msg_xid);
225
226
227         m = (struct idmap_msg *)e->msg.msg_data;
228
229         if (m->id_namelen != 0 && m->id_namelen != strlen(m->id_name)) {
230                 fprintf(stderr, "bad name length in idmap_msg\n");
231                 return -1;
232         }
233
234         switch (m->id_type) {
235         case IDMAP_TYPE_UID:
236                 if (m->id_namelen == 0) {
237                         /* id to name */
238                         pwd = getpwuid(m->id_id.uid);
239
240                         if (pwd == NULL) {
241                                 fprintf(stderr, "unknown uid %d!\n",
242                                         (uint32_t)m->id_id.uid);
243                                 name = BADUSER;
244                         } else
245                                 name = pwd->pw_name;
246
247                         if (idmap_name(m, name))
248                                 return -1;
249
250                 } else {
251                         /* name to id */
252                         name = idmap_prune_domain(m);
253                         if (name == NULL)
254                                 return -1;
255
256                         pwd = getpwnam(name);
257
258                         if (pwd == NULL) {
259                                 fprintf(stderr, "unknown username %s!\n", name);
260
261                                 id.uid = (uid_t)BADUID;
262                         } else
263                                 id.uid = pwd->pw_uid;
264
265                         free(name);
266
267                         if (idmap_id(m, id))
268                                 return -1;
269                 }
270                 break;
271         case IDMAP_TYPE_GID:
272                 if (m->id_namelen == 0) {
273                         /* id to name */
274                         grp = getgrgid(m->id_id.gid);
275
276                         if (grp == NULL) {
277                                 fprintf(stderr, "unknown gid %d!\n",
278                                         (uint32_t)m->id_id.gid);
279                                 name = BADGROUP;
280                         } else
281                                 name = grp->gr_name;
282
283                         if (idmap_name(m, name))
284                                 return -1;
285                 } else {
286                         /* name to id */
287                         name = idmap_prune_domain(m);
288                         if (name == NULL)
289                                 return -1;
290
291                         grp = getgrnam(name);
292
293                         if (grp == NULL) {
294                                 fprintf(stderr, "unknown groupname %s!\n", name);
295
296                                 id.gid = (gid_t)BADGID;
297                         } else
298                                 id.gid = grp->gr_gid;
299
300                         free(name);
301
302                         if (idmap_id(m, id))
303                                 return -1;
304                 }
305                 break;
306         default:
307                 fprintf(stderr, "bad idmap type: %d\n", m->id_type);
308                 return -1;
309                 break;
310         }
311
312         return 0;
313 }
314
315 int
316 main(int argc, char ** argv)
317 {
318         int error = 0;
319         struct idmap_e * entry;
320         fd_set read_fds, write_fds;
321         int maxfd;
322         int ret, ch;
323
324         while ((ch = getopt(argc, argv, "d:v")) != -1) {
325                 switch (ch) {
326                 case 'd':
327                         domain = optarg;
328                         break;
329                 case 'v':
330                         verbose = 1;
331                         break;
332                 default:
333                         fprintf(stderr, "usage: %s [-v] [-d domain]\n", argv[0]);
334                         exit(1);
335                         break;
336                 }
337         }
338
339
340         TAILQ_INIT(&upcall_q);
341
342         fd = open(DEV_PATH, O_RDWR, S_IRUSR | S_IWUSR);
343
344         if (fd < 0) {
345                 perror(DEV_PATH);
346                 exit(1);
347         }
348
349         if (!verbose)
350                 daemon(0,0);
351
352         maxfd = fd;
353         for (;;) {
354                 struct timeval timo = {1, 0};
355                 do {
356                         FD_ZERO(&read_fds);
357                         FD_ZERO(&write_fds);
358
359                         FD_SET(fd, &read_fds);
360                         FD_SET(fd, &write_fds);
361
362                         ret = select(maxfd+1, &read_fds, &write_fds, NULL, &timo);
363                 } while (ret < 0 && errno == EINTR);
364
365                 if (ret <= 0) {
366                         if (ret != 0)
367                                 perror("select");
368                         continue;
369                 }
370
371
372                 if (FD_ISSET(fd, &read_fds)) {
373                         for (;;) {
374                                 get_idmap_e(entry);
375
376                                 error = ioctl(fd, NFS4DEVIOCGET, &entry->msg);
377
378                                 if (error == -1) {
379                                         if (errno != EAGAIN)
380                                                 perror("get ioctl:");
381                                         put_idmap_e(entry);
382                                         break;
383                                 }
384
385                                 switch (entry->msg.msg_type ) {
386                                 case NFS4DEV_TYPE_IDMAP:
387                                         if (idmap_service(entry))
388                                                 entry->msg.msg_error = EIO;
389                                 break;
390                                 default:
391                                         fprintf(stderr, "unknown nfs4dev_msg type\n");
392                                         entry->msg.msg_error = EIO;
393                                 break;
394                                 }
395
396                                 add_idmap_e(entry);
397                         }
398                 }
399
400                 if (FD_ISSET(fd, &write_fds)) {
401                         while (!TAILQ_EMPTY(&upcall_q)) {
402                                 remove_idmap_e(entry);
403
404                                 error = ioctl(fd, NFS4DEVIOCPUT, &entry->msg);
405
406                                 if (error == -1) {
407                                         if (errno != EAGAIN)
408                                                 perror("put ioctl");
409                                         break;
410                                 }
411                                 put_idmap_e(entry);
412                         }
413                 }
414         }
415
416         /* never reached */
417         exit(1);
418 }