chiark / gitweb /
Display synthetic message serial number in a more readable format than (uint32_t) -1
[elogind.git] / src / libsystemd-bus / bus-creds.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2013 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <stdlib.h>
23
24 #include "util.h"
25 #include "cgroup-util.h"
26 #include "fileio.h"
27 #include "audit.h"
28 #include "bus-message.h"
29 #include "bus-util.h"
30 #include "time-util.h"
31 #include "strv.h"
32 #include "bus-creds.h"
33
34 enum {
35         CAP_OFFSET_INHERITABLE = 0,
36         CAP_OFFSET_PERMITTED = 1,
37         CAP_OFFSET_EFFECTIVE = 2,
38         CAP_OFFSET_BOUNDING = 3
39 };
40
41 void bus_creds_done(sd_bus_creds *c) {
42         assert(c);
43
44         /* For internal bus cred structures that are allocated by
45          * something else */
46
47         free(c->session);
48         free(c->unit);
49         free(c->user_unit);
50         free(c->slice);
51
52         strv_free(c->cmdline_array);
53         strv_free(c->well_known_names_array);
54 }
55
56 _public_ sd_bus_creds *sd_bus_creds_ref(sd_bus_creds *c) {
57         assert_return(c, NULL);
58
59         if (c->allocated) {
60                 assert(c->n_ref > 0);
61                 c->n_ref++;
62         } else {
63                 sd_bus_message *m;
64
65                 /* If this is an embedded creds structure, then
66                  * forward ref counting to the message */
67                 m = container_of(c, sd_bus_message, creds);
68                 sd_bus_message_ref(m);
69         }
70
71         return c;
72 }
73
74 _public_ sd_bus_creds *sd_bus_creds_unref(sd_bus_creds *c) {
75         assert_return(c, NULL);
76
77         if (c->allocated) {
78                 assert(c->n_ref > 0);
79                 c->n_ref--;
80
81                 if (c->n_ref == 0) {
82                         bus_creds_done(c);
83
84                         free(c->comm);
85                         free(c->tid_comm);
86                         free(c->exe);
87                         free(c->cmdline);
88                         free(c->cgroup);
89                         free(c->capability);
90                         free(c->label);
91                         free(c->unique_name);
92                         free(c->well_known_names);
93                         free(c);
94                 }
95         } else {
96                 sd_bus_message *m;
97
98                 m = container_of(c, sd_bus_message, creds);
99                 sd_bus_message_unref(m);
100         }
101
102
103         return NULL;
104 }
105
106 _public_ uint64_t sd_bus_creds_get_mask(sd_bus_creds *c) {
107         assert_return(c, 0);
108
109         return c->mask;
110 }
111
112 sd_bus_creds* bus_creds_new(void) {
113         sd_bus_creds *c;
114
115         c = new0(sd_bus_creds, 1);
116         if (!c)
117                 return NULL;
118
119         c->allocated = true;
120         c->n_ref = 1;
121         return c;
122 }
123
124 _public_ int sd_bus_creds_new_from_pid(pid_t pid, uint64_t mask, sd_bus_creds **ret) {
125         sd_bus_creds *c;
126         int r;
127
128         assert_return(pid >= 0, -EINVAL);
129         assert_return(mask <= _SD_BUS_CREDS_MAX, -ENOTSUP);
130         assert_return(ret, -EINVAL);
131
132         if (pid == 0)
133                 pid = getpid();
134
135         c = bus_creds_new();
136         if (!c)
137                 return -ENOMEM;
138
139         r = bus_creds_add_more(c, mask, pid, 0);
140         if (r < 0) {
141                 free(c);
142                 return r;
143         }
144
145         /* Check if the process existed at all, in case we haven't
146          * figured that out already */
147         if (kill(pid, 0) < 0 && errno == ESRCH) {
148                 sd_bus_creds_unref(c);
149                 return -ESRCH;
150         }
151
152         *ret = c;
153         return 0;
154 }
155
156 _public_ int sd_bus_creds_get_uid(sd_bus_creds *c, uid_t *uid) {
157         assert_return(c, -EINVAL);
158         assert_return(uid, -EINVAL);
159         assert_return(c->mask & SD_BUS_CREDS_UID, -ENODATA);
160
161         *uid = c->uid;
162         return 0;
163 }
164
165 _public_ int sd_bus_creds_get_gid(sd_bus_creds *c, gid_t *gid) {
166         assert_return(c, -EINVAL);
167         assert_return(gid, -EINVAL);
168         assert_return(c->mask & SD_BUS_CREDS_UID, -ENODATA);
169
170         *gid = c->gid;
171         return 0;
172 }
173
174 _public_ int sd_bus_creds_get_pid(sd_bus_creds *c, pid_t *pid) {
175         assert_return(c, -EINVAL);
176         assert_return(pid, -EINVAL);
177         assert_return(c->mask & SD_BUS_CREDS_PID, -ENODATA);
178
179         assert(c->pid > 0);
180         *pid = c->pid;
181         return 0;
182 }
183
184 _public_ int sd_bus_creds_get_tid(sd_bus_creds *c, pid_t *tid) {
185         assert_return(c, -EINVAL);
186         assert_return(tid, -EINVAL);
187         assert_return(c->mask & SD_BUS_CREDS_TID, -ENODATA);
188
189         assert(c->tid > 0);
190         *tid = c->tid;
191         return 0;
192 }
193
194 _public_ int sd_bus_creds_get_pid_starttime(sd_bus_creds *c, uint64_t *usec) {
195         assert_return(c, -EINVAL);
196         assert_return(usec, -EINVAL);
197         assert_return(c->mask & SD_BUS_CREDS_PID_STARTTIME, -ENODATA);
198
199         assert(c->pid_starttime > 0);
200         *usec = c->pid_starttime;
201         return 0;
202 }
203
204 _public_ int sd_bus_creds_get_selinux_context(sd_bus_creds *c, const char **ret) {
205         assert_return(c, -EINVAL);
206         assert_return(c->mask & SD_BUS_CREDS_SELINUX_CONTEXT, -ENODATA);
207
208         assert(c->label);
209         *ret = c->label;
210         return 0;
211 }
212
213 _public_ int sd_bus_creds_get_comm(sd_bus_creds *c, const char **ret) {
214         assert_return(c, -EINVAL);
215         assert_return(ret, -EINVAL);
216         assert_return(c->mask & SD_BUS_CREDS_COMM, -ENODATA);
217
218         assert(c->comm);
219         *ret = c->comm;
220         return 0;
221 }
222
223 _public_ int sd_bus_creds_get_tid_comm(sd_bus_creds *c, const char **ret) {
224         assert_return(c, -EINVAL);
225         assert_return(ret, -EINVAL);
226         assert_return(c->mask & SD_BUS_CREDS_TID_COMM, -ENODATA);
227
228         assert(c->tid_comm);
229         *ret = c->tid_comm;
230         return 0;
231 }
232
233 _public_ int sd_bus_creds_get_exe(sd_bus_creds *c, const char **ret) {
234         assert_return(c, -EINVAL);
235         assert_return(ret, -EINVAL);
236         assert_return(c->mask & SD_BUS_CREDS_EXE, -ENODATA);
237
238         assert(c->exe);
239         *ret = c->exe;
240         return 0;
241 }
242
243 _public_ int sd_bus_creds_get_cgroup(sd_bus_creds *c, const char **ret) {
244         assert_return(c, -EINVAL);
245         assert_return(ret, -EINVAL);
246         assert_return(c->mask & SD_BUS_CREDS_CGROUP, -ENODATA);
247
248         assert(c->cgroup);
249         *ret = c->cgroup;
250         return 0;
251 }
252
253 _public_ int sd_bus_creds_get_unit(sd_bus_creds *c, const char **ret) {
254         int r;
255
256         assert_return(c, -EINVAL);
257         assert_return(ret, -EINVAL);
258         assert_return(c->mask & SD_BUS_CREDS_UNIT, -ENODATA);
259
260         assert(c->cgroup);
261
262         if (!c->unit) {
263                 r = cg_path_get_unit(c->cgroup, (char**) &c->unit);
264                 if (r < 0)
265                         return r;
266         }
267
268         *ret = c->unit;
269         return 0;
270 }
271
272 _public_ int sd_bus_creds_get_user_unit(sd_bus_creds *c, const char **ret) {
273         int r;
274
275         assert_return(c, -EINVAL);
276         assert_return(ret, -EINVAL);
277         assert_return(c->mask & SD_BUS_CREDS_USER_UNIT, -ENODATA);
278
279         assert(c->cgroup);
280
281         if (!c->user_unit) {
282                 r = cg_path_get_user_unit(c->cgroup, (char**) &c->user_unit);
283                 if (r < 0)
284                         return r;
285         }
286
287         *ret = c->user_unit;
288         return 0;
289 }
290
291 _public_ int sd_bus_creds_get_slice(sd_bus_creds *c, const char **ret) {
292         int r;
293
294         assert_return(c, -EINVAL);
295         assert_return(ret, -EINVAL);
296         assert_return(c->mask & SD_BUS_CREDS_SLICE, -ENODATA);
297
298         assert(c->cgroup);
299
300         if (!c->slice) {
301                 r = cg_path_get_slice(c->cgroup, (char**) &c->slice);
302                 if (r < 0)
303                         return r;
304         }
305
306         *ret = c->slice;
307         return 0;
308 }
309
310 _public_ int sd_bus_creds_get_session(sd_bus_creds *c, const char **ret) {
311         int r;
312
313         assert_return(c, -EINVAL);
314         assert_return(ret, -EINVAL);
315         assert_return(c->mask & SD_BUS_CREDS_SESSION, -ENODATA);
316
317         assert(c->cgroup);
318
319         if (!c->session) {
320                 r = cg_path_get_session(c->cgroup, (char**) &c->session);
321                 if (r < 0)
322                         return r;
323         }
324
325         *ret = c->session;
326         return 0;
327 }
328
329 _public_ int sd_bus_creds_get_owner_uid(sd_bus_creds *c, uid_t *uid) {
330         assert_return(c, -EINVAL);
331         assert_return(uid, -EINVAL);
332         assert_return(c->mask & SD_BUS_CREDS_OWNER_UID, -ENODATA);
333
334         assert(c->cgroup);
335
336         return cg_path_get_owner_uid(c->cgroup, uid);
337 }
338
339 _public_ int sd_bus_creds_get_cmdline(sd_bus_creds *c, char ***cmdline) {
340         assert_return(c, -EINVAL);
341         assert_return(c->cmdline, -ESRCH);
342         assert_return(c->mask & SD_BUS_CREDS_CMDLINE, -ENODATA);
343
344         assert(c->cmdline);
345
346         if (!c->cmdline_array) {
347                 c->cmdline_array = strv_parse_nulstr(c->cmdline, c->cmdline_size);
348                 if (!c->cmdline_array)
349                         return -ENOMEM;
350         }
351
352         *cmdline = c->cmdline_array;
353         return 0;
354 }
355
356 _public_ int sd_bus_creds_get_audit_session_id(sd_bus_creds *c, uint32_t *sessionid) {
357         assert_return(c, -EINVAL);
358         assert_return(sessionid, -EINVAL);
359         assert_return(c->mask & SD_BUS_CREDS_AUDIT_SESSION_ID, -ENODATA);
360
361         *sessionid = c->audit_session_id;
362         return 0;
363 }
364
365 _public_ int sd_bus_creds_get_audit_login_uid(sd_bus_creds *c, uid_t *uid) {
366         assert_return(c, -EINVAL);
367         assert_return(uid, -EINVAL);
368         assert_return(c->mask & SD_BUS_CREDS_AUDIT_LOGIN_UID, -ENODATA);
369
370         *uid = c->audit_login_uid;
371         return 0;
372 }
373
374 _public_ int sd_bus_creds_get_unique_name(sd_bus_creds *c, const char **unique_name) {
375         assert_return(c, -EINVAL);
376         assert_return(unique_name, -EINVAL);
377         assert_return(c->mask & SD_BUS_CREDS_UNIQUE_NAME, -ENODATA);
378
379         *unique_name = c->unique_name;
380         return 0;
381 }
382
383 _public_ int sd_bus_creds_get_well_known_names(sd_bus_creds *c, char ***well_known_names) {
384         assert_return(c, -EINVAL);
385         assert_return(well_known_names, -EINVAL);
386         assert_return(c->mask & SD_BUS_CREDS_WELL_KNOWN_NAMES, -ENODATA);
387
388         assert(c->well_known_names);
389
390         if (!c->well_known_names_array) {
391                 c->well_known_names_array = strv_parse_nulstr(c->well_known_names, c->well_known_names_size);
392                 if (!c->well_known_names_array)
393                         return -ENOMEM;
394         }
395
396         *well_known_names = c->well_known_names_array;
397         return 0;
398 }
399
400 static int has_cap(sd_bus_creds *c, unsigned offset, int capability) {
401         size_t sz;
402
403         assert(c);
404         assert(c->capability);
405
406         sz = c->capability_size / 4;
407         if ((size_t) capability >= sz*8)
408                 return 0;
409
410         return !!(c->capability[offset * sz + (capability / 8)] & (1 << (capability % 8)));
411 }
412
413 _public_ int sd_bus_creds_has_effective_cap(sd_bus_creds *c, int capability) {
414         assert_return(c, -EINVAL);
415         assert_return(capability >= 0, -EINVAL);
416         assert_return(c->mask & SD_BUS_CREDS_EFFECTIVE_CAPS, -ENODATA);
417
418         return has_cap(c, CAP_OFFSET_EFFECTIVE, capability);
419 }
420
421 _public_ int sd_bus_creds_has_permitted_cap(sd_bus_creds *c, int capability) {
422         assert_return(c, -EINVAL);
423         assert_return(capability >= 0, -EINVAL);
424         assert_return(c->mask & SD_BUS_CREDS_PERMITTED_CAPS, -ENODATA);
425
426         return has_cap(c, CAP_OFFSET_PERMITTED, capability);
427 }
428
429 _public_ int sd_bus_creds_has_inheritable_cap(sd_bus_creds *c, int capability) {
430         assert_return(c, -EINVAL);
431         assert_return(capability >= 0, -EINVAL);
432         assert_return(c->mask & SD_BUS_CREDS_INHERITABLE_CAPS, -ENODATA);
433
434         return has_cap(c, CAP_OFFSET_INHERITABLE, capability);
435 }
436
437 _public_ int sd_bus_creds_has_bounding_cap(sd_bus_creds *c, int capability) {
438         assert_return(c, -EINVAL);
439         assert_return(capability >= 0, -EINVAL);
440         assert_return(c->mask & SD_BUS_CREDS_BOUNDING_CAPS, -ENODATA);
441
442         return has_cap(c, CAP_OFFSET_BOUNDING, capability);
443 }
444
445 static int parse_caps(sd_bus_creds *c, unsigned offset, const char *p) {
446         size_t sz;
447         unsigned i;
448
449         assert(c);
450         assert(p);
451
452         p += strspn(p, WHITESPACE);
453
454         sz = strlen(p);
455         if (sz % 2 != 0)
456                 return -EINVAL;
457
458         sz /= 2;
459         if (!c->capability) {
460                 c->capability = new0(uint8_t, sz * 4);
461                 if (!c->capability)
462                         return -ENOMEM;
463
464                 c->capability_size = sz * 4;
465         }
466
467         for (i = 0; i < sz; i ++) {
468                 int x, y;
469
470                 x = unhexchar(p[i*2]);
471                 y = unhexchar(p[i*2+1]);
472
473                 if (x < 0 || y < 0)
474                         return -EINVAL;
475
476                 c->capability[offset * sz + (sz - i - 1)] = (uint8_t) x << 4 | (uint8_t) y;
477         }
478
479         return 0;
480 }
481
482 int bus_creds_add_more(sd_bus_creds *c, uint64_t mask, pid_t pid, pid_t tid) {
483         uint64_t missing;
484         int r;
485
486         assert(c);
487         assert(c->allocated);
488
489         missing = mask & ~c->mask;
490         if (missing == 0)
491                 return 0;
492
493         /* Try to retrieve PID from creds if it wasn't passed to us */
494         if (pid <= 0 && (c->mask & SD_BUS_CREDS_PID))
495                 pid = c->pid;
496
497         if (tid <= 0 && (c->mask & SD_BUS_CREDS_TID))
498                 tid = c->pid;
499
500         /* Without pid we cannot do much... */
501         if (pid <= 0)
502                 return 0;
503
504         if (missing & (SD_BUS_CREDS_UID | SD_BUS_CREDS_GID |
505                        SD_BUS_CREDS_EFFECTIVE_CAPS | SD_BUS_CREDS_INHERITABLE_CAPS |
506                        SD_BUS_CREDS_PERMITTED_CAPS | SD_BUS_CREDS_BOUNDING_CAPS)) {
507
508                 _cleanup_fclose_ FILE *f = NULL;
509                 char line[LINE_MAX];
510                 const char *p;
511
512                 p = procfs_file_alloca(pid, "status");
513
514                 f = fopen(p, "re");
515                 if (!f)
516                         return errno == ENOENT ? -ESRCH : -errno;
517
518                 FOREACH_LINE(line, f, return -errno) {
519                         truncate_nl(line);
520
521                         if (missing & SD_BUS_CREDS_UID) {
522                                 p = startswith(line, "Uid:");
523                                 if (p) {
524                                         unsigned long uid;
525
526                                         p += strspn(p, WHITESPACE);
527                                         if (sscanf(p, "%lu", &uid) != 1)
528                                                 return -EIO;
529
530                                         c->uid = (uid_t) uid;
531                                         c->mask |= SD_BUS_CREDS_UID;
532                                         continue;
533                                 }
534                         }
535
536                         if (missing & SD_BUS_CREDS_GID) {
537                                 p = startswith(line, "Gid:");
538                                 if (p) {
539                                         unsigned long gid;
540
541                                         p += strspn(p, WHITESPACE);
542                                         if (sscanf(p, "%lu", &gid) != 1)
543                                                 return -EIO;
544
545                                         c->gid = (uid_t) gid;
546                                         c->mask |= SD_BUS_CREDS_GID;
547                                         continue;
548                                 }
549                         }
550
551                         if (missing & SD_BUS_CREDS_EFFECTIVE_CAPS) {
552                                 p = startswith(line, "CapEff:");
553                                 if (p) {
554                                         r = parse_caps(c, CAP_OFFSET_EFFECTIVE, p);
555                                         if (r < 0)
556                                                 return r;
557
558                                         c->mask |= SD_BUS_CREDS_EFFECTIVE_CAPS;
559                                         continue;
560                                 }
561                         }
562
563                         if (missing & SD_BUS_CREDS_PERMITTED_CAPS) {
564                                 p = startswith(line, "CapPrm:");
565                                 if (p) {
566                                         r = parse_caps(c, CAP_OFFSET_PERMITTED, p);
567                                         if (r < 0)
568                                                 return r;
569
570                                         c->mask |= SD_BUS_CREDS_PERMITTED_CAPS;
571                                         continue;
572                                 }
573                         }
574
575                         if (missing & SD_BUS_CREDS_INHERITABLE_CAPS) {
576                                 p = startswith(line, "CapInh:");
577                                 if (p) {
578                                         r = parse_caps(c, CAP_OFFSET_INHERITABLE, p);
579                                         if (r < 0)
580                                                 return r;
581
582                                         c->mask |= SD_BUS_CREDS_INHERITABLE_CAPS;
583                                         continue;
584                                 }
585                         }
586
587                         if (missing & SD_BUS_CREDS_BOUNDING_CAPS) {
588                                 p = startswith(line, "CapBnd:");
589                                 if (p) {
590                                         r = parse_caps(c, CAP_OFFSET_BOUNDING, p);
591                                         if (r < 0)
592                                                 return r;
593
594                                         c->mask |= SD_BUS_CREDS_BOUNDING_CAPS;
595                                         continue;
596                                 }
597                         }
598                 }
599         }
600
601         if (missing & (SD_BUS_CREDS_PID_STARTTIME)) {
602                 unsigned long long st;
603
604                 r = get_starttime_of_pid(pid, &st);
605                 if (r < 0)
606                         return r;
607
608                 c->pid_starttime = ((usec_t) st * USEC_PER_SEC) / (usec_t) sysconf(_SC_CLK_TCK);
609                 c->mask |= SD_BUS_CREDS_PID_STARTTIME;
610         }
611
612         if (missing & SD_BUS_CREDS_SELINUX_CONTEXT) {
613                 const char *p;
614
615                 p = procfs_file_alloca(pid, "attr/current");
616                 r = read_one_line_file(p, &c->label);
617                 if (r < 0 && r != -ENOENT && r != -EINVAL)
618                         return r;
619                 else if (r >= 0)
620                         c->mask |= SD_BUS_CREDS_SELINUX_CONTEXT;
621         }
622
623         if (missing & SD_BUS_CREDS_COMM) {
624                 r = get_process_comm(pid, &c->comm);
625                 if (r < 0)
626                         return r;
627
628                 c->mask |= SD_BUS_CREDS_COMM;
629         }
630
631         if (missing & SD_BUS_CREDS_EXE) {
632                 r = get_process_exe(pid, &c->exe);
633                 if (r < 0)
634                         return r;
635
636                 c->mask |= SD_BUS_CREDS_EXE;
637         }
638
639         if (missing & SD_BUS_CREDS_CMDLINE) {
640                 const char *p;
641
642                 p = procfs_file_alloca(pid, "cmdline");
643                 r = read_full_file(p, &c->cmdline, &c->cmdline_size);
644                 if (r < 0)
645                         return r;
646
647                 if (c->cmdline_size == 0) {
648                         free(c->cmdline);
649                         c->cmdline = NULL;
650                 } else
651                         c->mask |= SD_BUS_CREDS_CMDLINE;
652         }
653
654         if (tid > 0 && (missing & SD_BUS_CREDS_TID_COMM)) {
655                 _cleanup_free_ char *p = NULL;
656
657                 if (asprintf(&p, "/proc/%lu/task/%lu/comm", (unsigned long) pid, (unsigned long) tid) < 0)
658                         return -ENOMEM;
659
660                 r = read_one_line_file(p, &c->tid_comm);
661                 if (r < 0)
662                         return r == -ENOENT ? -ESRCH : r;
663
664                 c->mask |= SD_BUS_CREDS_TID_COMM;
665         }
666
667         if (missing & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID)) {
668
669                 r = cg_pid_get_path(NULL, pid, &c->cgroup);
670                 if (r < 0)
671                         return r;
672
673                 c->mask |= missing & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_OWNER_UID);
674         }
675
676         if (missing & SD_BUS_CREDS_AUDIT_SESSION_ID) {
677                 r = audit_session_from_pid(pid, &c->audit_session_id);
678                 if (r < 0 && r != -ENOTSUP && r != -ENXIO && r != -ENOENT)
679                         return r;
680                 else if (r >= 0)
681                         c->mask |= SD_BUS_CREDS_AUDIT_SESSION_ID;
682         }
683
684         if (missing & SD_BUS_CREDS_AUDIT_LOGIN_UID) {
685                 r = audit_loginuid_from_pid(pid, &c->audit_login_uid);
686                 if (r < 0 && r != -ENOTSUP && r != -ENXIO && r != -ENOENT)
687                         return r;
688                 else if (r >= 0)
689                         c->mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID;
690         }
691
692         return 0;
693 }
694
695 int bus_creds_extend_by_pid(sd_bus_creds *c, uint64_t mask, sd_bus_creds **ret) {
696         _cleanup_bus_creds_unref_ sd_bus_creds *n = NULL;
697         int r;
698
699         assert(c);
700         assert(ret);
701
702         if ((mask & ~c->mask) == 0) {
703                 /* There's already all data we need. */
704
705                 *ret = sd_bus_creds_ref(c);
706                 return 0;
707         }
708
709         n = bus_creds_new();
710         if (!n)
711                 return -ENOMEM;
712
713         /* Copy the original data over */
714
715         if (c->mask & mask & SD_BUS_CREDS_UID) {
716                 n->uid = c->uid;
717                 n->mask |= SD_BUS_CREDS_UID;
718         }
719
720         if (c->mask & mask & SD_BUS_CREDS_GID) {
721                 n->gid = c->gid;
722                 n->mask |= SD_BUS_CREDS_GID;
723         }
724
725         if (c->mask & mask & SD_BUS_CREDS_PID) {
726                 n->pid = c->pid;
727                 n->mask |= SD_BUS_CREDS_PID;
728         }
729
730         if (c->mask & mask & SD_BUS_CREDS_TID) {
731                 n->tid = c->tid;
732                 n->mask |= SD_BUS_CREDS_TID;
733         }
734
735         if (c->mask & mask & SD_BUS_CREDS_PID_STARTTIME) {
736                 n->pid_starttime = c->pid_starttime;
737                 n->mask |= SD_BUS_CREDS_PID_STARTTIME;
738         }
739
740         if (c->mask & mask & SD_BUS_CREDS_COMM) {
741                 n->comm = strdup(c->comm);
742                 if (!n->comm)
743                         return -ENOMEM;
744
745                 n->mask |= SD_BUS_CREDS_COMM;
746         }
747
748         if (c->mask & mask & SD_BUS_CREDS_TID_COMM) {
749                 n->tid_comm = strdup(c->tid_comm);
750                 if (!n->tid_comm)
751                         return -ENOMEM;
752
753                 n->mask |= SD_BUS_CREDS_TID_COMM;
754         }
755
756         if (c->mask & mask & SD_BUS_CREDS_EXE) {
757                 n->exe = strdup(c->exe);
758                 if (!n->exe)
759                         return -ENOMEM;
760
761                 n->mask |= SD_BUS_CREDS_EXE;
762         }
763
764         if (c->mask & mask & SD_BUS_CREDS_CMDLINE) {
765                 n->cmdline = memdup(c->cmdline, c->cmdline_size);
766                 if (!n->cmdline)
767                         return -ENOMEM;
768
769                 n->cmdline_size = c->cmdline_size;
770                 n->mask |= SD_BUS_CREDS_CMDLINE;
771         }
772
773         if (c->mask & mask & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_OWNER_UID)) {
774                 n->cgroup = strdup(c->cgroup);
775                 if (!n->cgroup)
776                         return -ENOMEM;
777
778                 n->mask |= mask & (SD_BUS_CREDS_CGROUP|SD_BUS_CREDS_SESSION|SD_BUS_CREDS_UNIT|SD_BUS_CREDS_USER_UNIT|SD_BUS_CREDS_SLICE|SD_BUS_CREDS_OWNER_UID);
779         }
780
781         if (c->mask & mask & (SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS)) {
782                 n->capability = memdup(c->capability, c->capability_size);
783                 if (!n->capability)
784                         return -ENOMEM;
785
786                 n->capability_size = c->capability_size;
787                 n->mask |= c->mask & mask & (SD_BUS_CREDS_EFFECTIVE_CAPS|SD_BUS_CREDS_PERMITTED_CAPS|SD_BUS_CREDS_INHERITABLE_CAPS|SD_BUS_CREDS_BOUNDING_CAPS);
788         }
789
790         if (c->mask & mask & SD_BUS_CREDS_AUDIT_SESSION_ID) {
791                 n->audit_session_id = c->audit_session_id;
792                 n->mask |= SD_BUS_CREDS_AUDIT_SESSION_ID;
793         }
794
795         if (c->mask & mask & SD_BUS_CREDS_AUDIT_LOGIN_UID) {
796                 n->audit_login_uid = c->audit_login_uid;
797                 n->mask |= SD_BUS_CREDS_AUDIT_LOGIN_UID;
798         }
799
800         if (c->mask & mask & SD_BUS_CREDS_UNIQUE_NAME) {
801                 n->unique_name = strdup(c->unique_name);
802                 if (!n->unique_name)
803                         return -ENOMEM;
804         }
805
806         if (c->mask & mask & SD_BUS_CREDS_WELL_KNOWN_NAMES) {
807                 n->well_known_names = memdup(c->well_known_names, c->well_known_names_size);
808                 if (!n->well_known_names)
809                         return -ENOMEM;
810
811                 n->well_known_names_size = c->well_known_names_size;
812         }
813
814         /* Get more data */
815
816         r = bus_creds_add_more(n, mask,
817                                c->mask & SD_BUS_CREDS_PID ? c->pid : 0,
818                                c->mask & SD_BUS_CREDS_TID ? c->tid : 0);
819         if (r < 0)
820                 return r;
821
822         *ret = n;
823         n = NULL;
824         return 0;
825 }