]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/core/dynamic-user.c
7236dcfabd4f20faea949006982aca044bd958ef
1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2016 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
25 #include "clean-ipc.h"
26 #include "dynamic-user.h"
31 #include "parse-util.h"
32 #include "random-util.h"
33 #include "stdio-util.h"
34 #include "string-util.h"
35 #include "user-util.h"
37 /* Takes a value generated randomly or by hashing and turns it into a UID in the right range */
38 #define UID_CLAMP_INTO_RANGE(rnd) (((uid_t) (rnd) % (DYNAMIC_UID_MAX - DYNAMIC_UID_MIN + 1)) + DYNAMIC_UID_MIN)
40 static DynamicUser
* dynamic_user_free ( DynamicUser
* d
) {
45 ( void ) hashmap_remove ( d
-> manager
-> dynamic_users
, d
-> name
);
47 safe_close_pair ( d
-> storage_socket
);
51 static int dynamic_user_add ( Manager
* m
, const char * name
, int storage_socket
[ 2 ], DynamicUser
** ret
) {
52 DynamicUser
* d
= NULL
;
57 assert ( storage_socket
);
59 r
= hashmap_ensure_allocated (& m
-> dynamic_users
, & string_hash_ops
);
63 d
= malloc0 ( offsetof ( DynamicUser
, name
) + strlen ( name
) + 1 );
67 strcpy ( d
-> name
, name
);
69 d
-> storage_socket
[ 0 ] = storage_socket
[ 0 ];
70 d
-> storage_socket
[ 1 ] = storage_socket
[ 1 ];
72 r
= hashmap_put ( m
-> dynamic_users
, d
-> name
, d
);
86 static int dynamic_user_acquire ( Manager
* m
, const char * name
, DynamicUser
** ret
) {
87 _cleanup_close_pair_
int storage_socket
[ 2 ] = { - 1 , - 1 };
94 /* Return the DynamicUser structure for a specific user name. Note that this won't actually allocate a UID for
95 * it, but just prepare the data structure for it. The UID is allocated only on demand, when it's really
96 * needed, and in the child process we fork off, since allocation involves NSS checks which are not OK to do
97 * from PID 1. To allow the children and PID 1 share information about allocated UIDs we use an anonymous
98 * AF_UNIX/SOCK_DGRAM socket (called the "storage socket") that contains at most one datagram with the
99 * allocated UID number, plus an fd referencing the lock file for the UID
100 * (i.e. /run/systemd/dynamic-uid/$UID). Why involve the socket pair? So that PID 1 and all its children can
101 * share the same storage for the UID and lock fd, simply by inheriting the storage socket fds. The socket pair
102 * may exist in three different states:
104 * a) no datagram stored. This is the initial state. In this case the dynamic user was never realized.
106 * b) a datagram containing a UID stored, but no lock fd attached to it. In this case there was already a
107 * statically assigned UID by the same name, which we are reusing.
109 * c) a datagram containing a UID stored, and a lock fd is attached to it. In this case we allocated a dynamic
110 * UID and locked it in the file system, using the lock fd.
112 * As PID 1 and various children might access the socket pair simultaneously, and pop the datagram or push it
113 * back in any time, we also maintain a lock on the socket pair. Note one peculiarity regarding locking here:
114 * the UID lock on disk is protected via a BSD file lock (i.e. an fd-bound lock), so that the lock is kept in
115 * place as long as there's a reference to the fd open. The lock on the storage socket pair however is a POSIX
116 * file lock (i.e. a process-bound lock), as all users share the same fd of this (after all it is anonymous,
117 * nobody else could get any access to it except via our own fd) and we want to synchronize access between all
118 * processes that have access to it. */
120 d
= hashmap_get ( m
-> dynamic_users
, name
);
122 /* We already have a structure for the dynamic user, let's increase the ref count and reuse it */
128 if (! valid_user_group_name_or_id ( name
))
131 if ( socketpair ( AF_UNIX
, SOCK_DGRAM
| SOCK_CLOEXEC
, 0 , storage_socket
) < 0 )
134 r
= dynamic_user_add ( m
, name
, storage_socket
, & d
);
138 storage_socket
[ 0 ] = storage_socket
[ 1 ] = - 1 ;
148 static int make_uid_symlinks ( uid_t uid
, const char * name
, bool b
) {
150 char path1
[ STRLEN ( "/run/systemd/dynamic-uid/direct:" ) + DECIMAL_STR_MAX ( uid_t
) + 1 ];
154 /* Add direct additional symlinks for direct lookups of dynamic UIDs and their names by userspace code. The
155 * only reason we have this is because dbus-daemon cannot use D-Bus for resolving users and groups (since it
156 * would be its own client then). We hence keep these world-readable symlinks in place, so that the
157 * unprivileged dbus user can read the mappings when it needs them via these symlinks instead of having to go
158 * via the bus. Ideally, we'd use the lock files we keep for this anyway, but we can't since we use BSD locks
159 * on them and as those may be taken by any user with read access we can't make them world-readable. */
161 xsprintf ( path1
, "/run/systemd/dynamic-uid/direct:" UID_FMT
, uid
);
162 if ( unlink ( path1
) < 0 && errno
!= ENOENT
)
165 if ( b
&& symlink ( name
, path1
) < 0 ) {
166 k
= log_warning_errno ( errno
, "Failed to symlink \" %s \" : %m" , path1
);
171 path2
= strjoina ( "/run/systemd/dynamic-uid/direct:" , name
);
172 if ( unlink ( path2
) < 0 && errno
!= ENOENT
) {
178 if ( b
&& symlink ( path1
+ STRLEN ( "/run/systemd/dynamic-uid/direct:" ), path2
) < 0 ) {
179 k
= log_warning_errno ( errno
, "Failed to symlink \" %s \" : %m" , path2
);
187 static int pick_uid ( char ** suggested_paths
, const char * name
, uid_t
* ret_uid
) {
189 /* Find a suitable free UID. We use the following strategy to find a suitable UID:
191 * 1. Initially, we try to read the UID of a number of specified paths. If any of these UIDs works, we use
192 * them. We use in order to increase the chance of UID reuse, if StateDirectory=, CacheDirectory= or
193 * LogDirectory= are used, as reusing the UID these directories are owned by saves us from having to
194 * recursively chown() them to new users.
196 * 2. If that didn't yield a currently unused UID, we hash the user name, and try to use that. This should be
197 * pretty good, as the use ris by default derived from the unit name, and hence the same service and same
198 * user should usually get the same UID as long as our hashing doesn't clash.
200 * 3. Finally, if that didn't work, we randomly pick UIDs, until we find one that is empty.
202 * Since the dynamic UID space is relatively small we'll stop trying after 100 iterations, giving up. */
205 PHASE_SUGGESTED
, /* the first phase, reusing directory ownership UIDs */
206 PHASE_HASHED
, /* the second phase, deriving a UID from the username by hashing */
207 PHASE_RANDOM
, /* the last phase, randomly picking UIDs */
208 } phase
= PHASE_SUGGESTED
;
210 static const uint8_t hash_key
[] = {
211 0x37 , 0x53 , 0x7e , 0x31 , 0xcf , 0xce , 0x48 , 0xf5 ,
212 0x8a , 0xbb , 0x39 , 0x57 , 0x8d , 0xd9 , 0xec , 0x59
215 unsigned n_tries
= 100 , current_suggested
= 0 ;
218 ( void ) mkdir ( "/run/systemd/dynamic-uid" , 0755 );
221 char lock_path
[ STRLEN ( "/run/systemd/dynamic-uid/" ) + DECIMAL_STR_MAX ( uid_t
) + 1 ];
222 _cleanup_close_
int lock_fd
= - 1 ;
226 if (-- n_tries
<= 0 ) /* Give up retrying eventually */
231 case PHASE_SUGGESTED
: {
234 if (! suggested_paths
|| ! suggested_paths
[ current_suggested
]) {
235 /* We reached the end of the suggested paths list, let's try by hashing the name */
236 phase
= PHASE_HASHED
;
240 if ( stat ( suggested_paths
[ current_suggested
++], & st
) < 0 )
241 continue ; /* We can't read the UID of this path, but that doesn't matter, just try the next */
243 candidate
= st
. st_uid
;
248 /* A static user by this name does not exist yet. Let's find a free ID then, and use that. We
249 * start with a UID generated as hash from the user name. */
250 candidate
= UID_CLAMP_INTO_RANGE ( siphash24 ( name
, strlen ( name
), hash_key
));
252 /* If this one fails, we should proceed with random tries */
253 phase
= PHASE_RANDOM
;
258 /* Pick another random UID, and see if that works for us. */
259 random_bytes (& candidate
, sizeof ( candidate
));
260 candidate
= UID_CLAMP_INTO_RANGE ( candidate
);
264 assert_not_reached ( "unknown phase" );
267 /* Make sure whatever we picked here actually is in the right range */
268 if (! uid_is_dynamic ( candidate
))
271 xsprintf ( lock_path
, "/run/systemd/dynamic-uid/" UID_FMT
, candidate
);
276 lock_fd
= open ( lock_path
, O_CREAT
| O_RDWR
| O_NOFOLLOW
| O_CLOEXEC
| O_NOCTTY
, 0600 );
280 r
= flock ( lock_fd
, LOCK_EX
| LOCK_NB
); /* Try to get a BSD file lock on the UID lock file */
282 if ( IN_SET ( errno
, EBUSY
, EAGAIN
))
283 goto next
; /* already in use */
288 if ( fstat ( lock_fd
, & st
) < 0 )
293 /* Oh, bummer, we got the lock, but the file was unlinked between the time we opened it and
294 * got the lock. Close it, and try again. */
295 lock_fd
= safe_close ( lock_fd
);
298 /* Some superficial check whether this UID/GID might already be taken by some static user */
299 if ( getpwuid ( candidate
) ||
300 getgrgid (( gid_t
) candidate
) ||
301 search_ipc ( candidate
, ( gid_t
) candidate
) != 0 ) {
302 ( void ) unlink ( lock_path
);
306 /* Let's store the user name in the lock file, so that we can use it for looking up the username for a UID */
309 IOVEC_INIT_STRING ( name
),
310 IOVEC_INIT (( char [ 1 ]) { ' \n ' }, 1 ),
314 ( void ) unlink ( lock_path
);
318 ( void ) ftruncate ( lock_fd
, l
);
319 ( void ) make_uid_symlinks ( candidate
, name
, true ); /* also add direct lookup symlinks */
321 * ret_uid
= candidate
;
322 return TAKE_FD ( lock_fd
);
329 static int dynamic_user_pop ( DynamicUser
* d
, uid_t
* ret_uid
, int * ret_lock_fd
) {
330 uid_t uid
= UID_INVALID
;
331 struct iovec iov
= IOVEC_INIT (& uid
, sizeof ( uid
));
333 struct cmsghdr cmsghdr
;
334 uint8_t buf
[ CMSG_SPACE ( sizeof ( int ))];
337 . msg_control
= & control
,
338 . msg_controllen
= sizeof ( control
),
342 struct cmsghdr
* cmsg
;
351 /* Read the UID and lock fd that is stored in the storage AF_UNIX socket. This should be called with the lock
352 * on the socket taken. */
354 k
= recvmsg ( d
-> storage_socket
[ 0 ], & mh
, MSG_DONTWAIT
| MSG_NOSIGNAL
| MSG_CMSG_CLOEXEC
);
358 cmsg
= cmsg_find (& mh
, SOL_SOCKET
, SCM_RIGHTS
, CMSG_LEN ( sizeof ( int )));
360 lock_fd
= *( int *) CMSG_DATA ( cmsg
);
362 cmsg_close_all (& mh
); /* just in case... */
365 * ret_lock_fd
= lock_fd
;
370 static int dynamic_user_push ( DynamicUser
* d
, uid_t uid
, int lock_fd
) {
371 struct iovec iov
= IOVEC_INIT (& uid
, sizeof ( uid
));
373 struct cmsghdr cmsghdr
;
374 uint8_t buf
[ CMSG_SPACE ( sizeof ( int ))];
377 . msg_control
= & control
,
378 . msg_controllen
= sizeof ( control
),
386 /* Store the UID and lock_fd in the storage socket. This should be called with the socket pair lock taken. */
389 struct cmsghdr
* cmsg
;
391 cmsg
= CMSG_FIRSTHDR (& mh
);
392 cmsg
-> cmsg_level
= SOL_SOCKET
;
393 cmsg
-> cmsg_type
= SCM_RIGHTS
;
394 cmsg
-> cmsg_len
= CMSG_LEN ( sizeof ( int ));
395 memcpy ( CMSG_DATA ( cmsg
), & lock_fd
, sizeof ( int ));
397 mh
. msg_controllen
= CMSG_SPACE ( sizeof ( int ));
399 mh
. msg_control
= NULL
;
400 mh
. msg_controllen
= 0 ;
403 k
= sendmsg ( d
-> storage_socket
[ 1 ], & mh
, MSG_DONTWAIT
| MSG_NOSIGNAL
);
410 static void unlink_uid_lock ( int lock_fd
, uid_t uid
, const char * name
) {
411 char lock_path
[ STRLEN ( "/run/systemd/dynamic-uid/" ) + DECIMAL_STR_MAX ( uid_t
) + 1 ];
416 xsprintf ( lock_path
, "/run/systemd/dynamic-uid/" UID_FMT
, uid
);
417 ( void ) unlink ( lock_path
);
419 ( void ) make_uid_symlinks ( uid
, name
, false ); /* remove direct lookup symlinks */
422 static int lockfp ( int fd
, int * fd_lock
) {
423 if ( lockf ( fd
, F_LOCK
, 0 ) < 0 )
429 static void unlockfp ( int * fd_lock
) {
432 lockf (* fd_lock
, F_ULOCK
, 0 );
436 static int dynamic_user_realize (
438 char ** suggested_dirs
,
439 uid_t
* ret_uid
, gid_t
* ret_gid
,
442 _cleanup_ ( unlockfp
) int storage_socket0_lock
= - 1 ;
443 _cleanup_close_
int uid_lock_fd
= - 1 ;
444 _cleanup_close_
int etc_passwd_lock_fd
= - 1 ;
445 uid_t num
= UID_INVALID
; /* a uid if is_user, and a gid otherwise */
446 gid_t gid
= GID_INVALID
; /* a gid if is_user, ignored otherwise */
450 assert ( is_user
== !! ret_uid
);
453 /* Acquire a UID for the user name. This will allocate a UID for the user name if the user doesn't exist
454 * yet. If it already exists its existing UID/GID will be reused. */
456 r
= lockfp ( d
-> storage_socket
[ 0 ], & storage_socket0_lock
);
460 r
= dynamic_user_pop ( d
, & num
, & uid_lock_fd
);
468 /* OK, nothing stored yet, let's try to find something useful. While we are working on this release the
469 * lock however, so that nobody else blocks on our NSS lookups. */
470 unlockfp (& storage_socket0_lock
);
472 /* Let's see if a proper, static user or group by this name exists. Try to take the lock on
473 * /etc/passwd, if that fails with EROFS then /etc is read-only. In that case it's fine if we don't
474 * take the lock, given that users can't be added there anyway in this case. */
475 etc_passwd_lock_fd
= take_etc_passwd_lock ( NULL
);
476 if ( etc_passwd_lock_fd
< 0 && etc_passwd_lock_fd
!= - EROFS
)
477 return etc_passwd_lock_fd
;
479 /* First, let's parse this as numeric UID */
480 r
= parse_uid ( d
-> name
, & num
);
486 /* OK, this is not a numeric UID. Let's see if there's a user by this name */
487 p
= getpwnam ( d
-> name
);
492 /* if the user does not exist but the group with the same name exists, refuse operation */
493 g
= getgrnam ( d
-> name
);
498 /* Let's see if there's a group by this name */
499 g
= getgrnam ( d
-> name
);
501 num
= ( uid_t
) g
-> gr_gid
;
503 /* if the group does not exist but the user with the same name exists, refuse operation */
504 p
= getpwnam ( d
-> name
);
511 if ( num
== UID_INVALID
) {
512 /* No static UID assigned yet, excellent. Let's pick a new dynamic one, and lock it. */
514 uid_lock_fd
= pick_uid ( suggested_dirs
, d
-> name
, & num
);
519 /* So, we found a working UID/lock combination. Let's see if we actually still need it. */
520 r
= lockfp ( d
-> storage_socket
[ 0 ], & storage_socket0_lock
);
522 unlink_uid_lock ( uid_lock_fd
, num
, d
-> name
);
526 r
= dynamic_user_pop ( d
, & new_uid
, & new_uid_lock_fd
);
529 /* OK, something bad happened, let's get rid of the bits we acquired. */
530 unlink_uid_lock ( uid_lock_fd
, num
, d
-> name
);
534 /* Great! Nothing is stored here, still. Store our newly acquired data. */
536 /* Hmm, so as it appears there's now something stored in the storage socket. Throw away what we
537 * acquired, and use what's stored now. */
539 unlink_uid_lock ( uid_lock_fd
, num
, d
-> name
);
540 safe_close ( uid_lock_fd
);
543 uid_lock_fd
= new_uid_lock_fd
;
547 /* If the UID/GID was already allocated dynamically, push the data we popped out back in. If it was already
548 * allocated statically, push the UID back too, but do not push the lock fd in. If we allocated the UID
549 * dynamically right here, push that in along with the lock fd for it. */
550 r
= dynamic_user_push ( d
, num
, uid_lock_fd
);
556 * ret_gid
= gid
!= GID_INVALID
? gid
: num
;
563 int dynamic_user_current ( DynamicUser
* d
, uid_t
* ret
) {
564 _cleanup_ ( unlockfp
) int storage_socket0_lock
= - 1 ;
565 _cleanup_close_
int lock_fd
= - 1 ;
572 /* Get the currently assigned UID for the user, if there's any. This simply pops the data from the storage socket, and pushes it back in right-away. */
574 r
= lockfp ( d
-> storage_socket
[ 0 ], & storage_socket0_lock
);
578 r
= dynamic_user_pop ( d
, & uid
, & lock_fd
);
582 r
= dynamic_user_push ( d
, uid
, lock_fd
);
590 static DynamicUser
* dynamic_user_ref ( DynamicUser
* d
) {
594 assert ( d
-> n_ref
> 0 );
600 static DynamicUser
* dynamic_user_unref ( DynamicUser
* d
) {
604 /* Note that this doesn't actually release any resources itself. If a dynamic user should be fully destroyed
605 * and its UID released, use dynamic_user_destroy() instead. NB: the dynamic user table may contain entries
606 * with no references, which is commonly the case right before a daemon reload. */
608 assert ( d
-> n_ref
> 0 );
614 static int dynamic_user_close ( DynamicUser
* d
) {
615 _cleanup_ ( unlockfp
) int storage_socket0_lock
= - 1 ;
616 _cleanup_close_
int lock_fd
= - 1 ;
620 /* Release the user ID, by releasing the lock on it, and emptying the storage socket. After this the user is
621 * unrealized again, much like it was after it the DynamicUser object was first allocated. */
623 r
= lockfp ( d
-> storage_socket
[ 0 ], & storage_socket0_lock
);
627 r
= dynamic_user_pop ( d
, & uid
, & lock_fd
);
629 /* User wasn't realized yet, nothing to do. */
634 /* This dynamic user was realized and dynamically allocated. In this case, let's remove the lock file. */
635 unlink_uid_lock ( lock_fd
, uid
, d
-> name
);
639 static DynamicUser
* dynamic_user_destroy ( DynamicUser
* d
) {
643 /* Drop a reference to a DynamicUser object, and destroy the user completely if this was the last
644 * reference. This is called whenever a service is shut down and wants its dynamic UID gone. Note that
645 * dynamic_user_unref() is what is called whenever a service is simply freed, for example during a reload
646 * cycle, where the dynamic users should not be destroyed, but our datastructures should. */
648 dynamic_user_unref ( d
);
653 ( void ) dynamic_user_close ( d
);
654 return dynamic_user_free ( d
);
657 int dynamic_user_serialize ( Manager
* m
, FILE * f
, FDSet
* fds
) {
665 /* Dump the dynamic user database into the manager serialization, to deal with daemon reloads. */
667 HASHMAP_FOREACH ( d
, m
-> dynamic_users
, i
) {
670 copy0
= fdset_put_dup ( fds
, d
-> storage_socket
[ 0 ]);
674 copy1
= fdset_put_dup ( fds
, d
-> storage_socket
[ 1 ]);
678 fprintf ( f
, "dynamic-user=%s %i %i \n " , d
-> name
, copy0
, copy1
);
684 void dynamic_user_deserialize_one ( Manager
* m
, const char * value
, FDSet
* fds
) {
685 _cleanup_free_
char * name
= NULL
, * s0
= NULL
, * s1
= NULL
;
692 /* Parse the serialization again, after a daemon reload */
694 r
= extract_many_words (& value
, NULL
, 0 , & name
, & s0
, & s1
, NULL
);
695 if ( r
!= 3 || ! isempty ( value
)) {
696 log_debug ( "Unable to parse dynamic user line." );
700 if ( safe_atoi ( s0
, & fd0
) < 0 || ! fdset_contains ( fds
, fd0
)) {
701 log_debug ( "Unable to process dynamic user fd specification." );
705 if ( safe_atoi ( s1
, & fd1
) < 0 || ! fdset_contains ( fds
, fd1
)) {
706 log_debug ( "Unable to process dynamic user fd specification." );
710 r
= dynamic_user_add ( m
, name
, ( int []) { fd0
, fd1
}, NULL
);
712 log_debug_errno ( r
, "Failed to add dynamic user: %m" );
716 ( void ) fdset_remove ( fds
, fd0
);
717 ( void ) fdset_remove ( fds
, fd1
);
720 void dynamic_user_vacuum ( Manager
* m
, bool close_user
) {
726 /* Empty the dynamic user database, optionally cleaning up orphaned dynamic users, i.e. destroy and free users
727 * to which no reference exist. This is called after a daemon reload finished, in order to destroy users which
728 * might not be referenced anymore. */
730 HASHMAP_FOREACH ( d
, m
-> dynamic_users
, i
) {
735 log_debug ( "Removing orphaned dynamic user %s" , d
-> name
);
736 ( void ) dynamic_user_close ( d
);
739 dynamic_user_free ( d
);
743 int dynamic_user_lookup_uid ( Manager
* m
, uid_t uid
, char ** ret
) {
744 char lock_path
[ STRLEN ( "/run/systemd/dynamic-uid/" ) + DECIMAL_STR_MAX ( uid_t
) + 1 ];
745 _cleanup_free_
char * user
= NULL
;
752 /* A friendly way to translate a dynamic user's UID into a name. */
753 if (! uid_is_dynamic ( uid
))
756 xsprintf ( lock_path
, "/run/systemd/dynamic-uid/" UID_FMT
, uid
);
757 r
= read_one_line_file ( lock_path
, & user
);
763 /* The lock file might be stale, hence let's verify the data before we return it */
764 r
= dynamic_user_lookup_name ( m
, user
, & check_uid
);
767 if ( check_uid
!= uid
) /* lock file doesn't match our own idea */
770 * ret
= TAKE_PTR ( user
);
775 int dynamic_user_lookup_name ( Manager
* m
, const char * name
, uid_t
* ret
) {
783 /* A friendly call for translating a dynamic user's name into its UID */
785 d
= hashmap_get ( m
-> dynamic_users
, name
);
789 r
= dynamic_user_current ( d
, ret
);
790 if ( r
== - EAGAIN
) /* not realized yet? */
796 int dynamic_creds_acquire ( DynamicCreds
* creds
, Manager
* m
, const char * user
, const char * group
) {
797 bool acquired
= false ;
803 /* A DynamicUser object encapsulates an allocation of both a UID and a GID for a specific name. However, some
804 * services use different user and groups. For cases like that there's DynamicCreds containing a pair of user
805 * and group. This call allocates a pair. */
807 if (! creds
-> user
&& user
) {
808 r
= dynamic_user_acquire ( m
, user
, & creds
-> user
);
817 if ( creds
-> user
&& (! group
|| streq_ptr ( user
, group
)))
818 creds
-> group
= dynamic_user_ref ( creds
-> user
);
820 r
= dynamic_user_acquire ( m
, group
, & creds
-> group
);
823 creds
-> user
= dynamic_user_unref ( creds
-> user
);
832 int dynamic_creds_realize ( DynamicCreds
* creds
, char ** suggested_paths
, uid_t
* uid
, gid_t
* gid
) {
833 uid_t u
= UID_INVALID
;
834 gid_t g
= GID_INVALID
;
841 /* Realize both the referenced user and group */
844 r
= dynamic_user_realize ( creds
-> user
, suggested_paths
, & u
, & g
, true );
849 if ( creds
-> group
&& creds
-> group
!= creds
-> user
) {
850 r
= dynamic_user_realize ( creds
-> group
, suggested_paths
, NULL
, & g
, false );
860 void dynamic_creds_unref ( DynamicCreds
* creds
) {
863 creds
-> user
= dynamic_user_unref ( creds
-> user
);
864 creds
-> group
= dynamic_user_unref ( creds
-> group
);
867 void dynamic_creds_destroy ( DynamicCreds
* creds
) {
870 creds
-> user
= dynamic_user_destroy ( creds
-> user
);
871 creds
-> group
= dynamic_user_destroy ( creds
-> group
);