cifs.upcall: the exit code should be 0 when print version
[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
44 #include "cifsacl.h"
45 #include "idmap_plugin.h"
46
47 static void *plugin_handle;
48
49 static const char *prog = "cifs.idmap";
50
51 static const struct option long_options[] = {
52         {"help", 0, NULL, 'h'},
53         {"timeout", 1, NULL, 't'},
54         {"version", 0, NULL, 'v'},
55         {NULL, 0, NULL, 0}
56 };
57
58 static void usage(void)
59 {
60         fprintf(stderr, "Usage: %s [-h] [-v] [-t timeout] key_serial\n", prog);
61 }
62
63 char *strget(const char *str, const char *substr)
64 {
65         int sublen;
66         char *substrptr;
67
68         /* find the prefix */
69         substrptr = strstr(str, substr);
70         if (!substrptr)
71                 return substrptr;
72
73         /* skip over it */
74         sublen = strlen(substr);
75         substrptr += sublen;
76
77         /* if there's nothing after the prefix, return NULL */
78         if (*substrptr == '\0')
79                 return NULL;
80
81         return substrptr;
82 }
83
84 /*
85  * Convert a string representation of unsigned int into a numeric one. Also
86  * check for incomplete string conversion and overflow.
87  */
88 static int
89 str_to_uint(const char *src, unsigned int *dst)
90 {
91         unsigned long tmp;
92         char *end;
93
94         errno = 0;
95         tmp = strtoul(src, &end, 0);
96
97         if (*end != '\0')
98                 return EINVAL;
99         if (tmp > UINT_MAX)
100                 return EOVERFLOW;
101
102         *dst = (unsigned int)tmp;
103         return 0;
104 }
105
106 static int
107 cifs_idmap(const key_serial_t key, const char *key_descr)
108 {
109         int rc = 1;
110         char *sidstr = NULL;
111         struct cifs_sid sid;
112         struct cifs_uxid cuxid;
113
114         /*
115          * Use winbind to convert received string to a SID and lookup
116          * name and map that SID to an uid.  If either of these
117          * function calls return with an error, return an error the
118          * upcall caller.  Otherwise instanticate a key using that uid.
119          *
120          * The same applies to SID and gid mapping.
121          */
122         sidstr = strget(key_descr, "os:");
123         if (sidstr) {
124                 rc = str_to_sid(plugin_handle, sidstr, &sid);
125                 if (rc) {
126                         syslog(LOG_DEBUG, "Unable to convert owner string %s "
127                                 "to SID: %s", key_descr, plugin_errmsg);
128                         goto cifs_idmap_ret;
129                 }
130
131                 rc = sids_to_ids(plugin_handle, &sid, 1, &cuxid);
132                 if (rc || (cuxid.type != CIFS_UXID_TYPE_UID &&
133                            cuxid.type != CIFS_UXID_TYPE_BOTH)) {
134                         syslog(LOG_DEBUG, "Unable to convert %s to "
135                                 "UID: %s", key_descr, plugin_errmsg);
136                         rc = rc ? rc : -EINVAL;
137                         goto cifs_idmap_ret;
138                 }
139                 rc = keyctl_instantiate(key, &cuxid.id.uid, sizeof(uid_t), 0);
140                 if (rc)
141                         syslog(LOG_ERR, "%s: key inst: %s", __func__,
142                                         strerror(errno));
143
144                 goto cifs_idmap_ret;
145         }
146
147         sidstr = strget(key_descr, "gs:");
148         if (sidstr) {
149                 rc = str_to_sid(plugin_handle, sidstr, &sid);
150                 if (rc) {
151                         syslog(LOG_DEBUG, "Unable to convert group string %s "
152                                 "to SID: %s", key_descr, plugin_errmsg);
153                         goto cifs_idmap_ret;
154                 }
155
156                 rc = sids_to_ids(plugin_handle, &sid, 1, &cuxid);
157                 if (rc || (cuxid.type != CIFS_UXID_TYPE_GID &&
158                            cuxid.type != CIFS_UXID_TYPE_BOTH)) {
159                         syslog(LOG_DEBUG, "Unable to convert %s to "
160                                 "GID: %s", key_descr, plugin_errmsg);
161                         rc = rc ? rc : -EINVAL;
162                         goto cifs_idmap_ret;
163                 }
164                 rc = keyctl_instantiate(key, &cuxid.id.gid, sizeof(gid_t), 0);
165                 if (rc)
166                         syslog(LOG_ERR, "%s: key inst: %s", __func__,
167                                         strerror(errno));
168
169                 goto cifs_idmap_ret;
170         }
171
172         sidstr = strget(key_descr, "oi:");
173         if (sidstr) {
174                 rc = str_to_uint(sidstr, (unsigned int *)&cuxid.id.uid);
175                 if (rc) {
176                         syslog(LOG_ERR, "Unable to convert %s to uid: %s",
177                                 sidstr, strerror(rc));
178                         goto cifs_idmap_ret;
179                 }
180                 cuxid.type = CIFS_UXID_TYPE_UID;
181
182                 syslog(LOG_DEBUG, "SID: %s, uid: %u", sidstr, cuxid.id.uid);
183                 rc = ids_to_sids(plugin_handle, &cuxid, 1, &sid);
184                 if (rc || sid.revision == 0) {
185                         syslog(LOG_DEBUG, "uid %u to SID error: %s",
186                                 cuxid.id.uid, plugin_errmsg);
187                         goto cifs_idmap_ret;
188                 }
189
190                 rc = keyctl_instantiate(key, &sid, sizeof(struct cifs_sid), 0);
191                 if (rc)
192                         syslog(LOG_ERR, "%s: key inst: %s", __func__,
193                                 strerror(errno));
194
195                 goto cifs_idmap_ret;
196         }
197
198         sidstr = strget(key_descr, "gi:");
199         if (sidstr) {
200                 rc = str_to_uint(sidstr, (unsigned int *)&cuxid.id.gid);
201                 if (rc) {
202                         syslog(LOG_ERR, "Unable to convert %s to gid: %s",
203                                 sidstr, strerror(rc));
204                         goto cifs_idmap_ret;
205                 }
206                 cuxid.type = CIFS_UXID_TYPE_GID;
207
208                 syslog(LOG_DEBUG, "SID: %s, gid: %u", sidstr, cuxid.id.gid);
209                 rc = ids_to_sids(plugin_handle, &cuxid, 1, &sid);
210                 if (rc || sid.revision == 0) {
211                         syslog(LOG_DEBUG, "gid %u to SID error: %s",
212                                 cuxid.id.gid, plugin_errmsg);
213                         goto cifs_idmap_ret;
214                 }
215
216                 rc = keyctl_instantiate(key, &sid, sizeof(struct cifs_sid), 0);
217                 if (rc)
218                         syslog(LOG_ERR, "%s: key inst: %s", __func__,
219                                 strerror(errno));
220
221                 goto cifs_idmap_ret;
222         }
223
224         syslog(LOG_DEBUG, "Invalid key: %s", key_descr);
225
226 cifs_idmap_ret:
227         return rc;
228 }
229
230 int main(const int argc, char *const argv[])
231 {
232         int c;
233         long rc;
234         key_serial_t key = 0;
235         char *buf;
236         unsigned int timeout = 600; /* default idmap cache timeout */
237
238         openlog(prog, 0, LOG_DAEMON);
239
240         while ((c = getopt_long(argc, argv, "ht:v",
241                                         long_options, NULL)) != -1) {
242                 switch (c) {
243                 case 'h':
244                         rc = 0;
245                         usage();
246                         goto out;
247                 case 't':
248                         rc = str_to_uint(optarg, &timeout);
249                         if (rc) {
250                                 syslog(LOG_ERR, "bad timeout value %s: %s",
251                                         optarg, strerror(rc));
252                                 goto out;
253                         }
254                         break;
255                 case 'v':
256                         rc = 0;
257                         printf("version: %s\n", VERSION);
258                         goto out;
259                 default:
260                         rc = EINVAL;
261                         syslog(LOG_ERR, "unknown option: %c", c);
262                         goto out;
263                 }
264         }
265
266         rc = 1;
267         /* is there a key? */
268         if (argc <= optind) {
269                 usage();
270                 goto out;
271         }
272
273         /* get key and keyring values */
274         errno = 0;
275         key = strtol(argv[optind], NULL, 10);
276         if (errno != 0) {
277                 key = 0;
278                 syslog(LOG_ERR, "Invalid key format: %s", strerror(errno));
279                 goto out;
280         }
281
282         if (init_plugin(&plugin_handle)) {
283                 plugin_handle = NULL;
284                 syslog(LOG_ERR, "Unable to initialize ID mapping plugin: %s",
285                         plugin_errmsg);
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_exit_plugin;
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                 goto out_exit_plugin;
302         }
303
304         syslog(LOG_DEBUG, "key description: %s", buf);
305
306         rc = cifs_idmap(key, buf);
307 out_exit_plugin:
308         exit_plugin(plugin_handle);
309 out:
310         return rc;
311 }