This is the mail archive of the libc-alpha@sourceware.org mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [PATCH] resolv/resolv.h: allow alternative resolv.conf files


> That's certainly better.  Do you have an example of another system which
> offers a similar environment variable?
I don't know actually. I can search around.
Features similar to namespaces should exist in BSD and Solaris (zones).

I have found that someone else had a similar problem
https://github.com/hadess/resolvconf-override
This library rewrites _res.nsaddr_list[i]... which is more a dirty trick 
than a solution. It may break with future changes in glibc.

Queries for per-user configuration of resolv.conf can be found googling around:
e.g. using the key "per-user resolv.conf".

I have tested the proposals I got from this mailing list (thank you all!).

The code at the end of this message partially solves my problem.
It can be tested in this way:

$ gcc -o newresconf newresconf.c
$ echo nameserver 80.80.80.80 > /tmp/resolv.conf
$ cat /etc/resolv.conf
# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)
#     DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN
nameserver 127.0.0.1
$ PATH_RESCONF=/tmp/resolv.conf ./newresconf bash
$ cat /etc/resolv.conf
nameserver 80.80.80.80
$ exit

There are still issues: e.g. setuid does not work in this namespace so e.g. I got:
$ ping 127.0.0.1
ping: socket: Operation not permitted

I had to define a USER+NS namespace, remap uids and gids (which needs a separate process),
and finally mount-bind the file.

It seems a bizantine procedure just to tell resolv to use a different resolv.conf.
There is some extra work for the kernel (keep track of the new namespaces, separate mounttables,
more mount items) for a problem that could be solved entirely at user level.

resolv.conf is just a configuration file of the library. A security model based
on the idea that something is safe because the library forbids the access to a
functionality is weak. System calls must enforce processes to behave correctly,
system calls can really forbid bad behaviors. When a syscall says no it is no.
Users can run different libraries if it is just the library to deny something.

So, why not providing a linear and straightforward simple way to solve a simple problem?

Providing complex methods only, does not make the system safer but prevents developers
from writing services or at least from writing good clean code to implement their services.
Isn't it?

(just my 2 euro cents).

thank you

	renzo

/* 
 * newresconf: experimental code. bind /etc/resolv.conf to a different file.
 * code inspired by vdens GPLv2+ by Renzo Davoli et al.
 */

#define _GNU_SOURCE
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sched.h>
#include <limits.h>
#include <errno.h>
#include <sys/capability.h>
#include <sys/prctl.h>
#include <sys/mount.h>

#define errExit(msg)    ({ perror(msg); exit(EXIT_FAILURE); })

static void uid_gid_map(pid_t pid) {
	char map_file[PATH_MAX];
	FILE *f;
	uid_t euid = geteuid();
	gid_t egid = getegid();
	snprintf(map_file, PATH_MAX, "/proc/%d/uid_map", pid);
	f = fopen(map_file, "w");
	if (f) {
		fprintf(f,"%d %d 1\n",euid,euid);
		fclose(f);
	}
	snprintf(map_file, PATH_MAX, "/proc/%d/setgroups", pid);
	f = fopen(map_file, "w");
	if (f) {
		fprintf(f,"deny\n");
		fclose(f);
	}
	snprintf(map_file, PATH_MAX, "/proc/%d/gid_map", pid);
	f = fopen(map_file, "w");
	if (f) {
		fprintf(f,"%d %d 1\n",egid,egid);
		fclose(f);
	}
}

static void unsharens(void) {
	int pipe_fd[2];
	pid_t child_pid;
	char buf[1];
	if (pipe2(pipe_fd, O_CLOEXEC) == -1)
		errExit("pipe");
	switch (child_pid = fork()) {
		case 0:
			close(pipe_fd[1]);
			read(pipe_fd[0], &buf, sizeof(buf));
			uid_gid_map(getppid());
			exit(0);
		default:
			close(pipe_fd[0]);
			if (unshare(CLONE_NEWUSER | CLONE_NEWNS) == -1)
				errExit("unshare");
			close(pipe_fd[1]);
			if (waitpid(child_pid, NULL, 0) == -1)      /* Wait for child */
				errExit("waitpid");
			break;
		case -1:
			errExit("unshare fork");
	}
}

void new_resolv_conf(const char *path_resconf) {

	if (path_resconf && *path_resconf) {
		unsharens();
		if (mount(path_resconf, "/etc/resolv.conf", "", MS_MGC_VAL|MS_BIND, NULL) < 0)
			errExit("mount");
	}
}

int main(int argc, char *argv[])
{
	char **cmdargv;
	char *argvsh[]={getenv("SHELL"),NULL};

	if (argc == 1)
		cmdargv = argvsh;
	else
		cmdargv = argv + 1;

	if (cmdargv[0] == NULL) {
		fprintf(stderr, "Error: $SHELL env variable not set\n");
		exit(EXIT_FAILURE); 
	}

	new_resolv_conf(getenv("PATH_RESCONF"));

	execvp(cmdargv[0], cmdargv);

	exit(EXIT_SUCCESS);
}


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]