setcifsacl: fix up endianness conversions
[jlayton/cifs-utils.git] / cifs.idmap.c
1 /*
2 * CIFS idmap helper.
3 * Copyright (C) Shirish Pargaonkar (shirishp@us.ibm.com) 2011
4 *
5 * Used by /sbin/request-key.conf for handling
6 * cifs upcall for SID to uig/gid and uid/gid to SID mapping.
7 * You should have keyutils installed and add
8 * this lines to /etc/request-key.conf file:
9
10     create cifs.idmap * * /usr/local/sbin/cifs.idmap %k
11
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 */
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif /* HAVE_CONFIG_H */
28
29 #include <string.h>
30 #include <getopt.h>
31 #include <syslog.h>
32 #include <dirent.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <unistd.h>
36 #include <keyutils.h>
37 #include <stdint.h>
38 #include <stdbool.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <errno.h>
42 #include <limits.h>
43 #include <wbclient.h>
44
45 #include "cifsacl.h"
46
47 static const char *prog = "cifs.idmap";
48
49 static const struct option long_options[] = {
50         {"help", 0, NULL, 'h'},
51         {"timeout", 1, NULL, 't'},
52         {"version", 0, NULL, 'v'},
53         {NULL, 0, NULL, 0}
54 };
55
56 static void usage(void)
57 {
58         fprintf(stderr, "Usage: %s [-h] [-v] [-t timeout] key_serial\n", prog);
59 }
60
61 char *strget(const char *str, const char *substr)
62 {
63         int len, sublen, retlen;
64         char *substrptr;
65
66         /* find the prefix */
67         substrptr = strstr(str, substr);
68         if (!substrptr)
69                 return substrptr;
70
71         /* skip over it */
72         sublen = strlen(substr);
73         substrptr += sublen;
74
75         /* if there's nothing after the prefix, return NULL */
76         if (*substrptr == '\0')
77                 return NULL;
78
79         return substrptr;
80 }
81
82 /*
83  * Convert a string representation of unsigned int into a numeric one. Also
84  * check for incomplete string conversion and overflow.
85  */
86 static int
87 str_to_uint(const char *src, unsigned int *dst)
88 {
89         unsigned long tmp;
90         char *end;
91
92         errno = 0;
93         tmp = strtoul(src, &end, 0);
94
95         if (*end != '\0')
96                 return EINVAL;
97         if (tmp > UINT_MAX)
98                 return EOVERFLOW;
99
100         *dst = (unsigned int)tmp;
101         return 0;
102 }
103
104 /*
105  * Winbind keeps wbcDomainSid fields in host-endian. So, we must convert it
106  * to little endian since the kernel will expect that.
107  */
108 static void
109 convert_sid_endianness(struct cifs_sid *sid)
110 {
111         int i;
112
113         for (i = 0; i < sid->num_subauth; i++)
114                 sid->sub_auth[i] = htole32(sid->sub_auth[i]);
115 }
116
117 static int
118 cifs_idmap(const key_serial_t key, const char *key_descr)
119 {
120         uid_t uid = 0;
121         gid_t gid = 0;;
122         wbcErr rc = 1;
123         char *sidstr = NULL;
124         struct wbcDomainSid sid;
125
126         /*
127          * Use winbind to convert received string to a SID and lookup
128          * name and map that SID to an uid.  If either of these
129          * function calls return with an error, return an error the
130          * upcall caller.  Otherwise instanticate a key using that uid.
131          *
132          * The same applies to SID and gid mapping.
133          */
134         sidstr = strget(key_descr, "os:");
135         if (sidstr) {
136                 rc = wbcStringToSid(sidstr, &sid);
137                 if (rc)
138                         syslog(LOG_DEBUG, "Invalid owner string: %s, rc: %d",
139                                 key_descr, rc);
140                 else {
141                         rc = wbcSidToUid(&sid, &uid);
142                         if (rc)
143                                 syslog(LOG_DEBUG, "SID %s to uid wbc error: %d",
144                                                 key_descr, rc);
145                 }
146                 if (!rc) { /* SID has been mapped to an uid */
147                         rc = keyctl_instantiate(key, &uid, sizeof(uid_t), 0);
148                         if (rc)
149                                 syslog(LOG_ERR, "%s: key inst: %s",
150                                         __func__, strerror(errno));
151                 }
152
153                 goto cifs_idmap_ret;
154         }
155
156         sidstr = strget(key_descr, "gs:");
157         if (sidstr) {
158                 rc = wbcStringToSid(sidstr, &sid);
159                 if (rc)
160                         syslog(LOG_DEBUG, "Invalid group string: %s, rc: %d",
161                                         key_descr, rc);
162                 else {
163                         rc = wbcSidToGid(&sid, &gid);
164                         if (rc)
165                                 syslog(LOG_DEBUG, "SID %s to gid wbc error: %d",
166                                                 key_descr, rc);
167                 }
168                 if (!rc) { /* SID has been mapped to a gid */
169                         rc = keyctl_instantiate(key, &gid, sizeof(gid_t), 0);
170                         if (rc)
171                                 syslog(LOG_ERR, "%s: key inst: %s",
172                                                 __func__, strerror(errno));
173                 }
174
175                 goto cifs_idmap_ret;
176         }
177
178         sidstr = strget(key_descr, "oi:");
179         if (sidstr) {
180                 rc = str_to_uint(sidstr, (unsigned int *)&uid);
181                 if (rc) {
182                         syslog(LOG_ERR, "Unable to convert %s to uid: %s",
183                                 sidstr, strerror(rc));
184                         goto cifs_idmap_ret;
185                 }
186
187                 syslog(LOG_DEBUG, "SID: %s, uid: %u", sidstr, uid);
188                 rc = wbcUidToSid(uid, &sid);
189                 if (rc)
190                         syslog(LOG_DEBUG, "uid %u to SID  error: %d", uid, rc);
191                 if (!rc) {
192                         /* SID has been mapped to a uid */
193                         convert_sid_endianness((struct cifs_sid *)&sid);
194                         rc = keyctl_instantiate(key, &sid,
195                                         sizeof(struct wbcDomainSid), 0);
196                         if (rc)
197                                 syslog(LOG_ERR, "%s: key inst: %s",
198                                         __func__, strerror(errno));
199                 }
200
201                 goto cifs_idmap_ret;
202         }
203
204         sidstr = strget(key_descr, "gi:");
205         if (sidstr) {
206                 rc = str_to_uint(sidstr, (unsigned int *)&gid);
207                 if (rc) {
208                         syslog(LOG_ERR, "Unable to convert %s to gid: %s",
209                                 sidstr, strerror(rc));
210                         goto cifs_idmap_ret;
211                 }
212
213                 syslog(LOG_DEBUG, "SID: %s, gid: %u", sidstr, gid);
214                 rc = wbcGidToSid(gid, &sid);
215                 if (rc)
216                         syslog(LOG_DEBUG, "gid %u to SID error: %d", gid, rc);
217                 if (!rc) {
218                         /* SID has been mapped to a gid */
219                         convert_sid_endianness((struct cifs_sid *)&sid);
220                         rc = keyctl_instantiate(key, &sid,
221                                         sizeof(struct wbcDomainSid), 0);
222                         if (rc)
223                                 syslog(LOG_ERR, "%s: key inst: %s",
224                                         __func__, strerror(errno));
225                 }
226
227                 goto cifs_idmap_ret;
228         }
229
230
231         syslog(LOG_DEBUG, "Invalid key: %s", key_descr);
232
233 cifs_idmap_ret:
234         return rc;
235 }
236
237 int main(const int argc, char *const argv[])
238 {
239         int c;
240         long rc;
241         key_serial_t key = 0;
242         char *buf;
243         unsigned int timeout = 600; /* default idmap cache timeout */
244
245         openlog(prog, 0, LOG_DAEMON);
246
247         while ((c = getopt_long(argc, argv, "ht:v",
248                                         long_options, NULL)) != -1) {
249                 switch (c) {
250                 case 'h':
251                         rc = 0;
252                         usage();
253                         goto out;
254                 case 't':
255                         rc = str_to_uint(optarg, &timeout);
256                         if (rc) {
257                                 syslog(LOG_ERR, "bad timeout value %s: %s",
258                                         optarg, strerror(rc));
259                                 goto out;
260                         }
261                         break;
262                 case 'v':
263                         rc = 0;
264                         printf("version: %s\n", VERSION);
265                         goto out;
266                 default:
267                         rc = EINVAL;
268                         syslog(LOG_ERR, "unknown option: %c", c);
269                         goto out;
270                 }
271         }
272
273         rc = 1;
274         /* is there a key? */
275         if (argc <= optind) {
276                 usage();
277                 goto out;
278         }
279
280         /* get key and keyring values */
281         errno = 0;
282         key = strtol(argv[optind], NULL, 10);
283         if (errno != 0) {
284                 key = 0;
285                 syslog(LOG_ERR, "Invalid key format: %s", strerror(errno));
286                 goto out;
287         }
288
289         /* set timeout on key */
290         rc = keyctl_set_timeout(key, timeout);
291         if (rc == -1) {
292                 syslog(LOG_ERR, "unable to set key timeout: %s",
293                         strerror(errno));
294                 goto out;
295         }
296
297         rc = keyctl_describe_alloc(key, &buf);
298         if (rc == -1) {
299                 syslog(LOG_ERR, "keyctl_describe_alloc failed: %s",
300                        strerror(errno));
301                 rc = 1;
302                 goto out;
303         }
304
305         syslog(LOG_DEBUG, "key description: %s", buf);
306
307         rc = cifs_idmap(key, buf);
308 out:
309         return rc;
310 }