libwbclient: Protect wbcCtxUnixIdsToSids against integer-wrap
[samba.git] / nsswitch / libwbclient / wbc_idmap.c
1 /*
2    Unix SMB/CIFS implementation.
3
4    Winbind client API
5
6    Copyright (C) Gerald (Jerry) Carter 2007
7
8    This library is free software; you can redistribute it and/or
9    modify it under the terms of the GNU Lesser General Public
10    License as published by the Free Software Foundation; either
11    version 3 of the License, or (at your option) any later version.
12
13    This library is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    Library General Public License for more details.
17
18    You should have received a copy of the GNU Lesser General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21
22 /* Required Headers */
23
24 #include "replace.h"
25 #include "libwbclient.h"
26 #include "../winbind_client.h"
27
28 /* Convert a Windows SID to a Unix uid, allocating an uid if needed */
29 wbcErr wbcCtxSidToUid(struct wbcContext *ctx, const struct wbcDomainSid *sid,
30                       uid_t *puid)
31 {
32         struct wbcUnixId xid;
33         wbcErr wbc_status;
34
35         if (!sid || !puid) {
36                 wbc_status = WBC_ERR_INVALID_PARAM;
37                 BAIL_ON_WBC_ERROR(wbc_status);
38         }
39
40         wbc_status = wbcCtxSidsToUnixIds(ctx, sid, 1, &xid);
41         if (!WBC_ERROR_IS_OK(wbc_status)) {
42                 goto done;
43         }
44
45         if ((xid.type == WBC_ID_TYPE_UID) || (xid.type == WBC_ID_TYPE_BOTH)) {
46                 *puid = xid.id.uid;
47                 wbc_status = WBC_ERR_SUCCESS;
48         } else {
49                 wbc_status = WBC_ERR_DOMAIN_NOT_FOUND;
50         }
51
52  done:
53         return wbc_status;
54 }
55
56 wbcErr wbcSidToUid(const struct wbcDomainSid *sid, uid_t *puid)
57 {
58         return wbcCtxSidToUid(NULL, sid, puid);
59 }
60
61 /* Convert a Windows SID to a Unix uid if there already is a mapping */
62 wbcErr wbcQuerySidToUid(const struct wbcDomainSid *sid,
63                         uid_t *puid)
64 {
65         return WBC_ERR_NOT_IMPLEMENTED;
66 }
67
68 /* Convert a Unix uid to a Windows SID, allocating a SID if needed */
69 wbcErr wbcCtxUidToSid(struct wbcContext *ctx, uid_t uid,
70                       struct wbcDomainSid *psid)
71 {
72         struct wbcUnixId xid;
73         struct wbcDomainSid sid;
74         struct wbcDomainSid null_sid = { 0 };
75         wbcErr wbc_status;
76
77         if (!psid) {
78                 wbc_status = WBC_ERR_INVALID_PARAM;
79                 BAIL_ON_WBC_ERROR(wbc_status);
80         }
81
82         xid = (struct wbcUnixId) { .type = WBC_ID_TYPE_UID, .id.uid = uid };
83
84         wbc_status = wbcCtxUnixIdsToSids(ctx, &xid, 1, &sid);
85         if (!WBC_ERROR_IS_OK(wbc_status)) {
86                 goto done;
87         }
88
89         if (memcmp(&sid, &null_sid, sizeof(sid)) != 0) {
90                 *psid = sid;
91         } else {
92                 wbc_status = WBC_ERR_DOMAIN_NOT_FOUND;
93         }
94
95 done:
96         return wbc_status;
97 }
98
99 wbcErr wbcUidToSid(uid_t uid, struct wbcDomainSid *sid)
100 {
101         return wbcCtxUidToSid(NULL, uid, sid);
102 }
103
104 /* Convert a Unix uid to a Windows SID if there already is a mapping */
105 wbcErr wbcQueryUidToSid(uid_t uid,
106                         struct wbcDomainSid *sid)
107 {
108         return WBC_ERR_NOT_IMPLEMENTED;
109 }
110
111 /** @brief Convert a Windows SID to a Unix gid, allocating a gid if needed
112  *
113  * @param *sid        Pointer to the domain SID to be resolved
114  * @param *pgid       Pointer to the resolved gid_t value
115  *
116  * @return #wbcErr
117  *
118  **/
119
120 wbcErr wbcCtxSidToGid(struct wbcContext *ctx, const struct wbcDomainSid *sid,
121                       gid_t *pgid)
122 {
123         struct wbcUnixId xid;
124         wbcErr wbc_status;
125
126         if (!sid || !pgid) {
127                 wbc_status = WBC_ERR_INVALID_PARAM;
128                 BAIL_ON_WBC_ERROR(wbc_status);
129         }
130
131         wbc_status = wbcCtxSidsToUnixIds(ctx, sid, 1, &xid);
132         if (!WBC_ERROR_IS_OK(wbc_status)) {
133                 goto done;
134         }
135
136         if ((xid.type == WBC_ID_TYPE_GID) || (xid.type == WBC_ID_TYPE_BOTH)) {
137                 *pgid = xid.id.gid;
138                 wbc_status = WBC_ERR_SUCCESS;
139         } else {
140                 wbc_status = WBC_ERR_DOMAIN_NOT_FOUND;
141         }
142
143  done:
144         return wbc_status;
145 }
146
147 wbcErr wbcSidToGid(const struct wbcDomainSid *sid, gid_t *pgid)
148 {
149         return wbcCtxSidToGid(NULL, sid, pgid);
150 }
151
152 /* Convert a Windows SID to a Unix gid if there already is a mapping */
153
154 wbcErr wbcQuerySidToGid(const struct wbcDomainSid *sid,
155                         gid_t *pgid)
156 {
157         return WBC_ERR_NOT_IMPLEMENTED;
158 }
159
160
161 /* Convert a Unix gid to a Windows SID, allocating a SID if needed */
162 wbcErr wbcCtxGidToSid(struct wbcContext *ctx, gid_t gid,
163                       struct wbcDomainSid *psid)
164 {
165         struct wbcUnixId xid;
166         struct wbcDomainSid sid;
167         struct wbcDomainSid null_sid = { 0 };
168         wbcErr wbc_status;
169
170         if (!psid) {
171                 wbc_status = WBC_ERR_INVALID_PARAM;
172                 BAIL_ON_WBC_ERROR(wbc_status);
173         }
174
175         xid = (struct wbcUnixId) { .type = WBC_ID_TYPE_GID, .id.gid = gid };
176
177         wbc_status = wbcCtxUnixIdsToSids(ctx, &xid, 1, &sid);
178         if (!WBC_ERROR_IS_OK(wbc_status)) {
179                 goto done;
180         }
181
182         if (memcmp(&sid, &null_sid, sizeof(sid)) != 0) {
183                 *psid = sid;
184         } else {
185                 wbc_status = WBC_ERR_DOMAIN_NOT_FOUND;
186         }
187
188 done:
189         return wbc_status;
190 }
191
192 wbcErr wbcGidToSid(gid_t gid, struct wbcDomainSid *sid)
193 {
194         return wbcCtxGidToSid(NULL, gid, sid);
195 }
196
197 /* Convert a Unix gid to a Windows SID if there already is a mapping */
198 wbcErr wbcQueryGidToSid(gid_t gid,
199                         struct wbcDomainSid *sid)
200 {
201         return WBC_ERR_NOT_IMPLEMENTED;
202 }
203
204 /* Obtain a new uid from Winbind */
205 wbcErr wbcCtxAllocateUid(struct wbcContext *ctx, uid_t *puid)
206 {
207         struct winbindd_request request;
208         struct winbindd_response response;
209         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
210
211         if (!puid)
212                 return WBC_ERR_INVALID_PARAM;
213
214         /* Initialise request */
215
216         ZERO_STRUCT(request);
217         ZERO_STRUCT(response);
218
219         /* Make request */
220
221         wbc_status = wbcRequestResponsePriv(ctx, WINBINDD_ALLOCATE_UID,
222                                             &request, &response);
223         BAIL_ON_WBC_ERROR(wbc_status);
224
225         /* Copy out result */
226         *puid = response.data.uid;
227
228         wbc_status = WBC_ERR_SUCCESS;
229
230  done:
231         return wbc_status;
232 }
233
234 wbcErr wbcAllocateUid(uid_t *puid)
235 {
236         return wbcCtxAllocateUid(NULL, puid);
237 }
238
239 /* Obtain a new gid from Winbind */
240 wbcErr wbcCtxAllocateGid(struct wbcContext *ctx, gid_t *pgid)
241 {
242         struct winbindd_request request;
243         struct winbindd_response response;
244         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
245
246         if (!pgid)
247                 return WBC_ERR_INVALID_PARAM;
248
249         /* Initialise request */
250
251         ZERO_STRUCT(request);
252         ZERO_STRUCT(response);
253
254         /* Make request */
255
256         wbc_status = wbcRequestResponsePriv(ctx, WINBINDD_ALLOCATE_GID,
257                                             &request, &response);
258         BAIL_ON_WBC_ERROR(wbc_status);
259
260         /* Copy out result */
261         *pgid = response.data.gid;
262
263         wbc_status = WBC_ERR_SUCCESS;
264
265  done:
266         return wbc_status;
267 }
268
269 wbcErr wbcAllocateGid(gid_t *pgid)
270 {
271         return wbcCtxAllocateGid(NULL, pgid);
272 }
273
274 /* we can't include smb.h here... */
275 #define _ID_TYPE_UID 1
276 #define _ID_TYPE_GID 2
277
278 /* Set an user id mapping - not implemented any more */
279 wbcErr wbcSetUidMapping(uid_t uid, const struct wbcDomainSid *sid)
280 {
281         return WBC_ERR_NOT_IMPLEMENTED;
282 }
283
284 /* Set a group id mapping - not implemented any more */
285 wbcErr wbcSetGidMapping(gid_t gid, const struct wbcDomainSid *sid)
286 {
287         return WBC_ERR_NOT_IMPLEMENTED;
288 }
289
290 /* Remove a user id mapping - not implemented any more */
291 wbcErr wbcRemoveUidMapping(uid_t uid, const struct wbcDomainSid *sid)
292 {
293         return WBC_ERR_NOT_IMPLEMENTED;
294 }
295
296 /* Remove a group id mapping - not implemented any more */
297 wbcErr wbcRemoveGidMapping(gid_t gid, const struct wbcDomainSid *sid)
298 {
299         return WBC_ERR_NOT_IMPLEMENTED;
300 }
301
302 /* Set the highwater mark for allocated uids - not implemented any more */
303 wbcErr wbcSetUidHwm(uid_t uid_hwm)
304 {
305         return WBC_ERR_NOT_IMPLEMENTED;
306 }
307
308 /* Set the highwater mark for allocated gids - not implemented any more */
309 wbcErr wbcSetGidHwm(gid_t gid_hwm)
310 {
311         return WBC_ERR_NOT_IMPLEMENTED;
312 }
313
314 /* Convert a list of SIDs */
315 wbcErr wbcCtxSidsToUnixIds(struct wbcContext *ctx,
316                            const struct wbcDomainSid *sids,
317                            uint32_t num_sids, struct wbcUnixId *ids)
318 {
319         struct winbindd_request request;
320         struct winbindd_response response;
321         wbcErr wbc_status = WBC_ERR_UNKNOWN_FAILURE;
322         int buflen, extra_len;
323         uint32_t i;
324         char *sidlist, *p, *extra_data;
325
326         buflen = num_sids * (WBC_SID_STRING_BUFLEN + 1) + 1;
327
328         sidlist = (char *)malloc(buflen);
329         if (sidlist == NULL) {
330                 return WBC_ERR_NO_MEMORY;
331         }
332
333         p = sidlist;
334
335         for (i=0; i<num_sids; i++) {
336                 int remaining;
337                 int len;
338
339                 remaining = buflen - (p - sidlist);
340
341                 len = wbcSidToStringBuf(&sids[i], p, remaining);
342                 if (len > remaining) {
343                         free(sidlist);
344                         return WBC_ERR_UNKNOWN_FAILURE;
345                 }
346
347                 p += len;
348                 *p++ = '\n';
349         }
350         *p++ = '\0';
351
352         ZERO_STRUCT(request);
353         ZERO_STRUCT(response);
354
355         request.extra_data.data = sidlist;
356         request.extra_len = p - sidlist;
357
358         wbc_status = wbcRequestResponse(ctx, WINBINDD_SIDS_TO_XIDS,
359                                         &request, &response);
360         free(sidlist);
361         if (!WBC_ERROR_IS_OK(wbc_status)) {
362                 return wbc_status;
363         }
364
365         extra_len = response.length - sizeof(struct winbindd_response);
366         extra_data = (char *)response.extra_data.data;
367
368         if ((extra_len <= 0) || (extra_data[extra_len-1] != '\0')) {
369                 goto wbc_err_invalid;
370         }
371
372         p = extra_data;
373
374         for (i=0; i<num_sids; i++) {
375                 struct wbcUnixId *id = &ids[i];
376                 char *q;
377
378                 switch (p[0]) {
379                 case 'U':
380                         id->type = WBC_ID_TYPE_UID;
381                         id->id.uid = strtoul(p+1, &q, 10);
382                         break;
383                 case 'G':
384                         id->type = WBC_ID_TYPE_GID;
385                         id->id.gid = strtoul(p+1, &q, 10);
386                         break;
387                 case 'B':
388                         id->type = WBC_ID_TYPE_BOTH;
389                         id->id.uid = strtoul(p+1, &q, 10);
390                         break;
391                 default:
392                         id->type = WBC_ID_TYPE_NOT_SPECIFIED;
393                         q = strchr(p, '\n');
394                         break;
395                 };
396                 if (q == NULL || q[0] != '\n') {
397                         goto wbc_err_invalid;
398                 }
399                 p = q+1;
400         }
401         wbc_status = WBC_ERR_SUCCESS;
402         goto done;
403
404 wbc_err_invalid:
405         wbc_status = WBC_ERR_INVALID_RESPONSE;
406 done:
407         winbindd_free_response(&response);
408         return wbc_status;
409 }
410
411 wbcErr wbcSidsToUnixIds(const struct wbcDomainSid *sids, uint32_t num_sids,
412                         struct wbcUnixId *ids)
413 {
414         return wbcCtxSidsToUnixIds(NULL, sids, num_sids, ids);
415 }
416
417 wbcErr wbcCtxUnixIdsToSids(struct wbcContext *ctx,
418                            const struct wbcUnixId *ids, uint32_t num_ids,
419                            struct wbcDomainSid *sids)
420 {
421         struct winbindd_request request;
422         struct winbindd_response response;
423         wbcErr wbc_status;
424         char *buf;
425         char *s;
426         const size_t sidlen = (1 /* U/G */ + 10 /* 2^32 */ + 1 /* \n */);
427         size_t ofs, buflen;
428         uint32_t i;
429
430         if (num_ids > SIZE_MAX / sidlen) {
431                 return WBC_ERR_NO_MEMORY; /* overflow */
432         }
433         buflen = num_ids * sidlen;
434
435         buflen += 1;            /* trailing \0 */
436         if (buflen < 1) {
437                 return WBC_ERR_NO_MEMORY; /* overflow */
438         }
439
440         buf = malloc(buflen);
441         if (buf == NULL) {
442                 return WBC_ERR_NO_MEMORY;
443         }
444
445         ofs = 0;
446
447         for (i=0; i<num_ids; i++) {
448                 const struct wbcUnixId *id = &ids[i];
449                 int len;
450
451                 switch (id->type) {
452                 case WBC_ID_TYPE_UID:
453                         len = snprintf(buf+ofs, buflen-ofs, "U%"PRIu32"\n",
454                                        (uint32_t)id->id.uid);
455                         break;
456                 case WBC_ID_TYPE_GID:
457                         len = snprintf(buf+ofs, buflen-ofs, "G%"PRIu32"\n",
458                                        (uint32_t)id->id.gid);
459                         break;
460                 default:
461                         free(buf);
462                         return WBC_ERR_INVALID_PARAM;
463                 }
464
465                 if (len + ofs >= buflen) { /* >= for the terminating '\0' */
466                         free(buf);
467                         return WBC_ERR_UNKNOWN_FAILURE;
468                 }
469                 ofs += len;
470         }
471
472         request = (struct winbindd_request) {
473                 .extra_data.data = buf, .extra_len = ofs+1
474         };
475         response = (struct winbindd_response) {0};
476
477         wbc_status = wbcRequestResponse(ctx, WINBINDD_XIDS_TO_SIDS,
478                                         &request, &response);
479         free(buf);
480         if (!WBC_ERROR_IS_OK(wbc_status)) {
481                 return wbc_status;
482         }
483
484         s = response.extra_data.data;
485         for (i=0; i<num_ids; i++) {
486                 char *n = strchr(s, '\n');
487
488                 if (n == NULL) {
489                         goto fail;
490                 }
491                 *n = '\0';
492
493                 wbc_status = wbcStringToSid(s, &sids[i]);
494                 if (!WBC_ERROR_IS_OK(wbc_status)) {
495                         sids[i] = (struct wbcDomainSid) {0};
496                 }
497                 s = n+1;
498         }
499
500         wbc_status = WBC_ERR_SUCCESS;
501 fail:
502         winbindd_free_response(&response);
503         return wbc_status;
504 }
505
506 wbcErr wbcUnixIdsToSids(const struct wbcUnixId *ids, uint32_t num_ids,
507                         struct wbcDomainSid *sids)
508 {
509         return wbcCtxUnixIdsToSids(NULL, ids, num_ids, sids);
510 }