core: merge branch 'th/getpwuid-cleanup'

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/1771
This commit is contained in:
Thomas Haller 2023-11-14 10:35:51 +01:00
commit 8dcadafec0
No known key found for this signature in database
GPG key ID: 29C2366E4DFC5728
6 changed files with 150 additions and 40 deletions

View file

@ -8,6 +8,7 @@
#include "nm-auth-utils.h"
#include "libnm-glib-aux/nm-c-list.h"
#include "libnm-core-intern/nm-core-internal.h"
#include "nm-setting-connection.h"
#include "libnm-core-aux-intern/nm-auth-subject.h"
#include "nm-auth-manager.h"
@ -603,7 +604,6 @@ gboolean
nm_auth_is_subject_in_acl(NMConnection *connection, NMAuthSubject *subject, char **out_error_desc)
{
NMSettingConnection *s_con;
gs_free char *user = NULL;
gulong uid;
g_return_val_if_fail(connection, FALSE);
@ -621,13 +621,6 @@ nm_auth_is_subject_in_acl(NMConnection *connection, NMAuthSubject *subject, char
if (0 == uid)
return TRUE;
user = nm_utils_uid_to_name(uid);
if (!user) {
NM_SET_OUT(out_error_desc,
g_strdup_printf("Could not determine username for uid %lu", uid));
return FALSE;
}
s_con = nm_connection_get_setting_connection(connection);
if (!s_con) {
/* This can only happen when called from AddAndActivate, so we know
@ -637,7 +630,7 @@ nm_auth_is_subject_in_acl(NMConnection *connection, NMAuthSubject *subject, char
}
/* Match the username returned by the session check to a user in the ACL */
if (!nm_setting_connection_permissions_user_allowed(s_con, user)) {
if (!nm_setting_connection_permissions_user_allowed_by_uid(s_con, uid)) {
NM_SET_OUT(out_error_desc,
g_strdup_printf("uid %lu has no permission to perform this operation", uid));
return FALSE;

View file

@ -355,19 +355,10 @@ invalid:
return TRUE;
}
/**
* nm_setting_connection_permissions_user_allowed:
* @setting: the #NMSettingConnection
* @uname: the user name to check permissions for
*
* Checks whether the given username is allowed to view/access this connection.
*
* Returns: %TRUE if the requested user is allowed to view this connection,
* %FALSE if the given user is not allowed to view this connection
*/
gboolean
nm_setting_connection_permissions_user_allowed(NMSettingConnection *setting, const char *uname)
static gboolean
_permissions_user_allowed(NMSettingConnection *setting, const char *uname, gulong uid)
{
gs_free struct passwd *pw = NULL;
NMSettingConnectionPrivate *priv;
guint i;
@ -384,13 +375,52 @@ nm_setting_connection_permissions_user_allowed(NMSettingConnection *setting, con
for (i = 0; i < priv->permissions->len; i++) {
const Permission *permission = &nm_g_array_index(priv->permissions, Permission, i);
if (permission->ptype == PERM_TYPE_USER && nm_streq(permission->item, uname))
if (permission->ptype != PERM_TYPE_USER)
continue;
if (!uname) {
if (uid != G_MAXULONG) {
pw = nm_getpwuid(uid);
uname = nm_passwd_name(pw);
}
if (!uname)
return FALSE;
}
if (nm_streq(permission->item, uname))
return TRUE;
}
return FALSE;
}
/**
* nm_setting_connection_permissions_user_allowed:
* @setting: the #NMSettingConnection
* @uname: the user name to check permissions for
*
* Checks whether the given username is allowed to view/access this connection.
*
* Returns: %TRUE if the requested user is allowed to view this connection,
* %FALSE if the given user is not allowed to view this connection
*/
gboolean
nm_setting_connection_permissions_user_allowed(NMSettingConnection *setting, const char *uname)
{
g_return_val_if_fail(NM_IS_SETTING_CONNECTION(setting), FALSE);
g_return_val_if_fail(uname != NULL, FALSE);
return _permissions_user_allowed(setting, uname, G_MAXULONG);
}
gboolean
nm_setting_connection_permissions_user_allowed_by_uid(NMSettingConnection *setting, gulong uid)
{
g_return_val_if_fail(NM_IS_SETTING_CONNECTION(setting), FALSE);
return _permissions_user_allowed(setting, NULL, uid);
}
/**
* nm_setting_connection_add_permission:
* @setting: the #NMSettingConnection

View file

@ -533,6 +533,9 @@ GPtrArray *_nm_setting_bridge_port_get_vlans(NMSettingBridgePort *setting);
GArray *_nm_setting_connection_get_secondaries(NMSettingConnection *setting);
gboolean nm_setting_connection_permissions_user_allowed_by_uid(NMSettingConnection *setting,
gulong uid);
/*****************************************************************************/
NMSettingBluetooth *_nm_connection_get_setting_bluetooth_for_nap(NMConnection *connection);

View file

@ -5771,40 +5771,81 @@ nm_utils_is_specific_hostname(const char *name)
/*****************************************************************************/
/* taken from systemd's uid_to_name(). */
char *
nm_utils_uid_to_name(uid_t uid)
{
gs_free char *buf_heap = NULL;
char buf_stack[4096];
gsize bufsize;
char *buf;
typedef struct {
struct passwd pw;
_nm_alignas(max_align_t) char buf[];
} GetPwuidData;
bufsize = sizeof(buf_stack);
buf = buf_stack;
/**
* nm_getpwuid:
* uid: the user Id to look up
*
* Calls getpwuid_r() to lookup the passwd entry. See the manual.
* Allocates and returns a suitable buffer.
*
* The returned buffer is likely much large than required. You don't want to
* keep this buffer around for longer than necessary.
*
* Returns: (transfer full): the passwd entry, if found or NULL on error
* or if the entry was not found.
*/
struct passwd *
nm_getpwuid(uid_t uid)
{
gs_free GetPwuidData *data = NULL;
gsize bufsize;
const gsize OFFSET = G_STRUCT_OFFSET(GetPwuidData, buf);
long int size_max;
size_max = sysconf(_SC_GETPW_R_SIZE_MAX);
if (size_max > 0 && ((unsigned long int) size_max < G_MAXSIZE - OFFSET))
bufsize = size_max;
else
bufsize = 4096;
for (;;) {
struct passwd pwbuf;
struct passwd *pw = NULL;
int r;
r = getpwuid_r(uid, &pwbuf, buf, bufsize, &pw);
if (r == 0 && pw)
return nm_strdup_not_empty(pw->pw_name);
if (bufsize >= G_MAXSIZE - OFFSET)
return NULL;
nm_clear_g_free(&data);
data = g_malloc(OFFSET + bufsize);
r = getpwuid_r(uid, &data->pw, data->buf, bufsize, &pw);
if (r == 0) {
if (!pw)
return NULL;
nm_assert(pw == (gpointer) data);
return (gpointer) g_steal_pointer(&data);
}
if (r != ERANGE)
return NULL;
if (bufsize > G_MAXSIZE / 2u)
return NULL;
bufsize *= 2u;
g_free(buf_heap);
buf_heap = g_malloc(bufsize);
buf = buf_heap;
}
}
const char *
nm_passwd_name(const struct passwd *pw)
{
/* Normalize "pw->pw_name" and return it. */
return pw ? nm_str_not_empty(pw->pw_name) : NULL;
}
char *
nm_utils_uid_to_name(uid_t uid)
{
gs_free struct passwd *pw = NULL;
pw = nm_getpwuid(uid);
return g_strdup(nm_passwd_name(pw));
}
/* taken from systemd's nss_user_record_by_name() */
gboolean
nm_utils_name_to_uid(const char *name, uid_t *out_uid)

View file

@ -3196,6 +3196,12 @@ gboolean nm_utils_is_localhost(const char *name);
gboolean nm_utils_is_specific_hostname(const char *name);
struct passwd;
struct passwd *nm_getpwuid(uid_t uid);
const char *nm_passwd_name(const struct passwd *pw);
char *nm_utils_uid_to_name(uid_t uid);
gboolean nm_utils_name_to_uid(const char *name, uid_t *out_uid);

View file

@ -5,6 +5,8 @@
#include "libnm-glib-aux/nm-default-glib-i18n-prog.h"
#include <pwd.h>
#include "libnm-std-aux/unaligned.h"
#include "libnm-glib-aux/nm-random-utils.h"
#include "libnm-glib-aux/nm-str-buf.h"
@ -2580,6 +2582,40 @@ test_nm_prioq(void)
/*****************************************************************************/
static const char *
_getpwuid_name(uid_t uid)
{
static struct passwd *pw;
pw = getpwuid(uid);
return pw ? nm_str_not_empty(pw->pw_name) : NULL;
}
static void
test_uid_to_name(void)
{
int i;
for (i = 0; i < 20; i++) {
gs_free char *name = NULL;
gs_free struct passwd *pw = NULL;
uid_t uid;
if (i < 5)
uid = i;
else
uid = nmtst_get_rand_uint32() % 2000u;
name = nm_utils_uid_to_name(uid);
g_assert_cmpstr(name, ==, _getpwuid_name(uid));
pw = nm_getpwuid(uid);
g_assert_cmpstr(name, ==, nm_passwd_name(pw));
}
}
/*****************************************************************************/
NMTST_DEFINE();
int
@ -2630,6 +2666,7 @@ main(int argc, char **argv)
g_test_add_func("/general/test_garray", test_garray);
g_test_add_func("/general/test_nm_prioq", test_nm_prioq);
g_test_add_func("/general/test_nm_random", test_nm_random);
g_test_add_func("/general/test_uid_to_name", test_uid_to_name);
return g_test_run();
}