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