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