mirror of
https://git.proxmox.com/git/mirror_lxc
synced 2025-08-07 15:29:02 +00:00
Added support for user groups in lxc-usernet
Signed-off-by: Henrik Kjölhede <hkjolhede@gmail.com>
This commit is contained in:
parent
54c23a6a62
commit
3043883c5a
@ -23,6 +23,7 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <pwd.h>
|
#include <pwd.h>
|
||||||
|
#include <grp.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <sys/file.h>
|
#include <sys/file.h>
|
||||||
@ -96,6 +97,116 @@ static char *get_username(void)
|
|||||||
return pwd->pw_name;
|
return pwd->pw_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char **get_groupnames(void)
|
||||||
|
{
|
||||||
|
int ngroups;
|
||||||
|
gid_t *group_ids;
|
||||||
|
int ret, i, j;
|
||||||
|
char **group_names;
|
||||||
|
struct group *gr;
|
||||||
|
|
||||||
|
ngroups = getgroups(0, NULL);
|
||||||
|
group_ids = (gid_t *)malloc(sizeof(gid_t)*ngroups);
|
||||||
|
ret = getgroups(ngroups, group_ids);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
free(group_ids);
|
||||||
|
fprintf(stderr, "Failed to get process groups\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
group_names = (char **)malloc(sizeof(char *)*(ngroups+1));
|
||||||
|
|
||||||
|
for (i=0; i<ngroups; i++ ) {
|
||||||
|
gr = getgrgid(group_ids[i]);
|
||||||
|
|
||||||
|
if (gr == NULL) {
|
||||||
|
fprintf(stderr, "Failed to get group name\n");
|
||||||
|
free(group_ids);
|
||||||
|
for (j=0; j<i; j++) {
|
||||||
|
free(group_names[j]);
|
||||||
|
}
|
||||||
|
free(group_names);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
group_names[i] = strdup(gr->gr_name);
|
||||||
|
|
||||||
|
if (group_names[i] == NULL) {
|
||||||
|
fprintf(stderr, "Failed to copy group name: %s", gr->gr_name);
|
||||||
|
free(group_ids);
|
||||||
|
for (j=0; j<i; j++) {
|
||||||
|
free(group_names[j]);
|
||||||
|
}
|
||||||
|
free(group_names);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
group_names[ngroups] = NULL;
|
||||||
|
|
||||||
|
free(group_ids);
|
||||||
|
|
||||||
|
return group_names;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct alloted_s {
|
||||||
|
char *name;
|
||||||
|
int allowed;
|
||||||
|
struct alloted_s *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct alloted_s *append_alloted(struct alloted_s **head, char *name, int n)
|
||||||
|
{
|
||||||
|
struct alloted_s *cur, *al;
|
||||||
|
|
||||||
|
if (head == NULL || name == NULL) {
|
||||||
|
// sanity check. parameters should not be null
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
al = (struct alloted_s *)malloc(sizeof(struct alloted_s));
|
||||||
|
|
||||||
|
if (al == NULL) {
|
||||||
|
// unable to allocate memory to new struct
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
al->name = strdup(name);
|
||||||
|
al->allowed = n;
|
||||||
|
al->next = NULL;
|
||||||
|
|
||||||
|
if (*head == NULL) {
|
||||||
|
*head = al;
|
||||||
|
return al;
|
||||||
|
}
|
||||||
|
|
||||||
|
cur = *head;
|
||||||
|
while (cur->next != NULL)
|
||||||
|
cur = cur->next;
|
||||||
|
|
||||||
|
cur->next = al;
|
||||||
|
return al;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void free_alloted(struct alloted_s **head)
|
||||||
|
{
|
||||||
|
struct alloted_s *cur;
|
||||||
|
|
||||||
|
if (head == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cur = *head;
|
||||||
|
|
||||||
|
while (cur != NULL) {
|
||||||
|
cur = cur->next;
|
||||||
|
free((*head)->name);
|
||||||
|
free(*head);
|
||||||
|
*head = cur;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* The configuration file consists of lines of the form:
|
/* The configuration file consists of lines of the form:
|
||||||
*
|
*
|
||||||
* user type bridge count
|
* user type bridge count
|
||||||
@ -103,13 +214,15 @@ static char *get_username(void)
|
|||||||
* Return the count entry for the calling user if there is one. Else
|
* Return the count entry for the calling user if there is one. Else
|
||||||
* return -1.
|
* return -1.
|
||||||
*/
|
*/
|
||||||
static int get_alloted(char *me, char *intype, char *link)
|
static int get_alloted(char *me, char *intype, char *link, struct alloted_s **alloted)
|
||||||
{
|
{
|
||||||
FILE *fin = fopen(LXC_USERNIC_CONF, "r");
|
FILE *fin = fopen(LXC_USERNIC_CONF, "r");
|
||||||
char *line = NULL;
|
char *line = NULL;
|
||||||
char user[100], type[100], br[100];
|
char user[100], type[100], br[100];
|
||||||
size_t len = 0;
|
size_t len = 0;
|
||||||
int n = -1, ret;
|
int n, ret, count = 0;
|
||||||
|
char **groups;
|
||||||
|
char **g;
|
||||||
|
|
||||||
if (!fin) {
|
if (!fin) {
|
||||||
fprintf(stderr, "Failed to open %s: %s\n", LXC_USERNIC_CONF,
|
fprintf(stderr, "Failed to open %s: %s\n", LXC_USERNIC_CONF,
|
||||||
@ -128,13 +241,48 @@ static int get_alloted(char *me, char *intype, char *link)
|
|||||||
continue;
|
continue;
|
||||||
if (strcmp(link, br) != 0)
|
if (strcmp(link, br) != 0)
|
||||||
continue;
|
continue;
|
||||||
free(line);
|
|
||||||
fclose(fin);
|
append_alloted(alloted, me, n);
|
||||||
return n;
|
count += n;
|
||||||
|
break; // found the user with the appropriate settings, therefore finish the search.
|
||||||
|
// what to do if there are more than one applicable lines? not specified in the docs.
|
||||||
|
// since getline is implemented with realloc, we don't need to free line until exiting func.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// now parse any possible groups specified
|
||||||
|
groups = get_groupnames();
|
||||||
|
if (groups != NULL) {
|
||||||
|
|
||||||
|
fseek(fin, 0, SEEK_SET);
|
||||||
|
while ((getline(&line, &len, fin)) != -1) {
|
||||||
|
ret = sscanf(line, "%99[^ \t] %99[^ \t] %99[^ \t] %d", user, type, br, &n);
|
||||||
|
|
||||||
|
if (ret != 4)
|
||||||
|
continue;
|
||||||
|
if (user[0] != '@')
|
||||||
|
continue;
|
||||||
|
if (strcmp(type, intype) != 0)
|
||||||
|
continue;
|
||||||
|
if (strcmp(link, br) != 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (g=groups; *g!=NULL; g++) {
|
||||||
|
if (strcmp(user+1, *g) == 0) {
|
||||||
|
append_alloted(alloted, user, n);
|
||||||
|
count += n;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (g=groups; *g!=NULL; g++)
|
||||||
|
free(*g);
|
||||||
|
free(groups);
|
||||||
|
}
|
||||||
|
|
||||||
fclose(fin);
|
fclose(fin);
|
||||||
free(line);
|
free(line);
|
||||||
return -1;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *get_eol(char *s, char *e)
|
static char *get_eol(char *s, char *e)
|
||||||
@ -361,7 +509,7 @@ static bool cull_entries(int fd, char *me, char *t, char *br)
|
|||||||
p += entry_lines[n-1].len + 1;
|
p += entry_lines[n-1].len + 1;
|
||||||
if (p >= e)
|
if (p >= e)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
p = buf;
|
p = buf;
|
||||||
for (i=0; i<n; i++) {
|
for (i=0; i<n; i++) {
|
||||||
if (!entry_lines[i].keep)
|
if (!entry_lines[i].keep)
|
||||||
@ -396,18 +544,23 @@ static int count_entries(char *buf, off_t len, char *me, char *t, char *br)
|
|||||||
* The dbfile has lines of the format:
|
* The dbfile has lines of the format:
|
||||||
* user type bridge nicname
|
* user type bridge nicname
|
||||||
*/
|
*/
|
||||||
static bool get_nic_if_avail(int fd, char *me, int pid, char *intype, char *br, int allowed, char **nicname, char **cnic)
|
static bool get_nic_if_avail(int fd, struct alloted_s *names, int pid, char *intype, char *br, int allowed, char **nicname, char **cnic)
|
||||||
{
|
{
|
||||||
off_t len, slen;
|
off_t len, slen;
|
||||||
struct stat sb;
|
struct stat sb;
|
||||||
char *buf = NULL, *newline;
|
char *buf = NULL, *newline;
|
||||||
int ret, count = 0;
|
int ret, count = 0;
|
||||||
|
char *owner;
|
||||||
|
struct alloted_s *n;
|
||||||
|
|
||||||
cull_entries(fd, me, intype, br);
|
for (n=names; n!=NULL; n=n->next)
|
||||||
|
cull_entries(fd, n->name, intype, br);
|
||||||
|
|
||||||
if (allowed == 0)
|
if (allowed == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
owner = names->name;
|
||||||
|
|
||||||
if (fstat(fd, &sb) < 0) {
|
if (fstat(fd, &sb) < 0) {
|
||||||
fprintf(stderr, "Failed to fstat: %s\n", strerror(errno));
|
fprintf(stderr, "Failed to fstat: %s\n", strerror(errno));
|
||||||
return false;
|
return false;
|
||||||
@ -420,17 +573,27 @@ static bool get_nic_if_avail(int fd, char *me, int pid, char *intype, char *br,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
count = count_entries(buf, len, me, intype, br);
|
owner = NULL;
|
||||||
if (count >= allowed)
|
for (n=names; n!=NULL; n=n->next) {
|
||||||
return false;
|
count = count_entries(buf, len, n->name, intype, br);
|
||||||
|
|
||||||
|
if (count >= n->allowed)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
owner = n->name;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (owner == NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
if (!get_new_nicname(nicname, br, pid, cnic))
|
if (!get_new_nicname(nicname, br, pid, cnic))
|
||||||
return false;
|
return false;
|
||||||
/* me ' ' intype ' ' br ' ' *nicname + '\n' + '\0' */
|
/* owner ' ' intype ' ' br ' ' *nicname + '\n' + '\0' */
|
||||||
slen = strlen(me) + strlen(intype) + strlen(br) + strlen(*nicname) + 5;
|
slen = strlen(owner) + strlen(intype) + strlen(br) + strlen(*nicname) + 5;
|
||||||
newline = alloca(slen);
|
newline = alloca(slen);
|
||||||
ret = snprintf(newline, slen, "%s %s %s %s\n", me, intype, br, *nicname);
|
ret = snprintf(newline, slen, "%s %s %s %s\n", owner, intype, br, *nicname);
|
||||||
if (ret < 0 || ret >= slen) {
|
if (ret < 0 || ret >= slen) {
|
||||||
if (lxc_netdev_delete_by_name(*nicname) != 0)
|
if (lxc_netdev_delete_by_name(*nicname) != 0)
|
||||||
fprintf(stderr, "Error unlinking %s!\n", *nicname);
|
fprintf(stderr, "Error unlinking %s!\n", *nicname);
|
||||||
@ -592,6 +755,7 @@ int main(int argc, char *argv[])
|
|||||||
char *cnic = NULL; // created nic name in container is returned here.
|
char *cnic = NULL; // created nic name in container is returned here.
|
||||||
char *vethname = NULL;
|
char *vethname = NULL;
|
||||||
int pid;
|
int pid;
|
||||||
|
struct alloted_s *alloted = NULL;
|
||||||
|
|
||||||
/* set a sane env, because we are setuid-root */
|
/* set a sane env, because we are setuid-root */
|
||||||
if (clearenv() < 0) {
|
if (clearenv() < 0) {
|
||||||
@ -631,14 +795,16 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
if (!may_access_netns(pid)) {
|
if (!may_access_netns(pid)) {
|
||||||
fprintf(stderr, "User %s may not modify netns for pid %d\n",
|
fprintf(stderr, "User %s may not modify netns for pid %d\n",
|
||||||
me, pid);
|
me, pid);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
n = get_alloted(me, argv[2], argv[3]);
|
n = get_alloted(me, argv[2], argv[3], &alloted);
|
||||||
if (n > 0)
|
if (n > 0)
|
||||||
gotone = get_nic_if_avail(fd, me, pid, argv[2], argv[3], n, &nicname, &cnic);
|
gotone = get_nic_if_avail(fd, alloted, pid, argv[2], argv[3], n, &nicname, &cnic);
|
||||||
|
|
||||||
close(fd);
|
close(fd);
|
||||||
|
free_alloted(&alloted);
|
||||||
if (!gotone) {
|
if (!gotone) {
|
||||||
fprintf(stderr, "Quota reached\n");
|
fprintf(stderr, "Quota reached\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
|
Loading…
Reference in New Issue
Block a user