Vde_plug sets automagically the minimum permissions
[vde.git] / vde-2 / src / lib / libvdeplug.c
blob10ba67bbbe74ba8412d4a2994b56fe164fb9f863
1 /*
2 * libvdeplug - A library to connect to a VDE Switch.
3 * Copyright (C) 2006 Renzo Davoli, University of Bologna
5 * This library is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation version 2.1 of the License, or (at
8 * your option) any later version.
10 * This library is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
13 * General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 #include <errno.h>
21 #include <sys/socket.h>
22 #include <sys/un.h>
23 #include <stdint.h>
24 #include <stdio.h>
25 #include <limits.h>
26 #include <stdlib.h>
27 #include <pwd.h>
28 #include <grp.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <unistd.h>
32 #include <string.h>
34 #include <config.h>
35 #include <vde.h>
36 #include <vdecommon.h>
38 #include <libvdeplug.h>
40 /* Per-User standard switch definition */
41 /* This will be prefixed by getenv("HOME") */
42 /* it can be a symbolic link to the switch dir */
43 #define STDSOCK "/.vde2/stdsock"
45 #ifdef USE_IPN
46 #if 0
47 /* AF_IPN has not been officially assigned yet
48 we "steal" unused AF_NETBEUI in the meanwhile
49 this code will be uncommented when AF_IPN is assigned. */
50 #ifndef AF_IPN
51 #define AF_IPN 0 /* IPN sockets: */
52 #define PF_IPN AF_IPN
53 #endif
54 #endif
55 #ifndef AF_NETBEUI
56 #ifdef PF_NETBEUI
57 #define AF_NETBEUI PF_NETBEUI
58 #else
59 #define AF_NETBEUI 13
60 #endif
61 #endif
62 #define AF_IPN_STOLEN AF_NETBEUI /* IPN temporary sockets */
63 #define PF_IPN_STOLEN AF_IPN_STOLEN
64 #define IPN_ANY 0
66 #define IPN_SO_PORT 0
67 #define IPN_SO_DESCR 1
68 #endif
70 /* Fallback names for the control socket, NULL-terminated array of absolute
71 * filenames. */
72 char *fallback_sockname[] = {
73 "/var/run/vde.ctl/ctl",
74 "/tmp/vde.ctl/ctl",
75 "/tmp/vde.ctl",
76 NULL,
79 /* Fallback directories for the data socket, NULL-terminated array of absolute
80 * directory names, with no trailing /. */
81 const char *fallback_dirname[] = {
82 "/var/run",
83 "/var/tmp",
84 "/tmp",
85 NULL,
88 struct vdeconn {
89 int fdctl;
90 int fddata;
91 struct sockaddr_un inpath;
94 #define SWITCH_MAGIC 0xfeedface
95 #define MAXDESCR 128
97 enum request_type { REQ_NEW_CONTROL, REQ_NEW_PORT0 };
99 struct request_v3 {
100 uint32_t magic;
101 uint32_t version;
102 enum request_type type;
103 struct sockaddr_un sock;
104 char description[MAXDESCR];
105 } __attribute__((packed));
107 VDECONN *vde_open_real(char *given_sockname, char *descr,int interface_version,
108 struct vde_open_args *open_args)
110 struct vdeconn *conn=NULL;
111 struct passwd *callerpwd;
112 struct request_v3 *req=NULL;
113 int pid = getpid();
114 struct sockaddr_un *sockun=NULL;
115 struct sockaddr_un *dataout=NULL;
116 int port=0;
117 char *group=NULL;
118 mode_t mode=0700;
119 int sockno=0;
120 int res;
121 char *std_sockname=NULL;
122 char *real_sockname=NULL;
123 char *sockname=NULL;
125 if (open_args != NULL) {
126 if (interface_version == 1) {
127 port=open_args->port;
128 group=open_args->group;
129 mode=open_args->mode;
131 else {
132 errno=EINVAL;
133 goto abort;
137 if ((req=(struct request_v3 *)calloc(1, sizeof(struct request_v3)))==NULL) {
138 errno=ENOMEM;
139 goto abort;
141 if ((sockun=(struct sockaddr_un *)calloc(1, sizeof(struct sockaddr_un)))==NULL) {
142 errno=ENOMEM;
143 goto abort;
145 if ((dataout=(struct sockaddr_un *)calloc(1, sizeof(struct sockaddr_un)))==NULL) {
146 errno=ENOMEM;
147 goto abort;
149 if ((std_sockname=(char *)calloc(PATH_MAX,sizeof(char)))==NULL) {
150 errno=ENOMEM;
151 goto abort;
153 if ((real_sockname=(char *)calloc(PATH_MAX,sizeof(char)))==NULL) {
154 errno=ENOMEM;
155 goto abort;
157 sockname = real_sockname;
158 if ((conn=calloc(1,sizeof(struct vdeconn)))==NULL)
160 errno=ENOMEM;
161 goto abort;
163 conn->fdctl=conn->fddata=-1;
164 //get the login name
165 callerpwd=getpwuid(getuid());
166 req->type = REQ_NEW_CONTROL;
167 if (given_sockname == NULL || *given_sockname == '\0') {
168 char *homedir = getenv("HOME");
169 given_sockname = NULL;
170 if (homedir) {
171 struct stat statbuf;
172 snprintf(std_sockname, PATH_MAX, "%s%s", homedir, STDSOCK);
173 if (lstat(std_sockname,&statbuf)==0)
174 given_sockname = std_sockname;
176 } else {
177 char *split;
178 if(given_sockname[strlen(given_sockname)-1] == ']' && (split=rindex(given_sockname,'[')) != NULL) {
179 *split=0;
180 split++;
181 port=atoi(split);
182 if (port == 0)
183 req->type = REQ_NEW_PORT0;
184 if (*given_sockname==0)
185 given_sockname = NULL;
189 /* Canonicalize the sockname: we need to send an absolute pathname to the
190 * switch (we don't know its cwd) for the data socket. Appending
191 * given_sockname to getcwd() would be enough, but we could end up with a
192 * name longer than PATH_MAX that couldn't be used as sun_path. */
193 if (given_sockname && vde_realpath(given_sockname, real_sockname) == NULL)
194 goto abort;
196 #ifdef USE_IPN
197 #if 0
198 /* AF_IPN has not been officially assigned yet
199 we "steal" unused AF_NETBEUI in the meanwhile
200 this code will be uncommented when AF_IPN is assigned. */
201 if((conn->fddata = socket(AF_IPN,SOCK_RAW,IPN_ANY)) >= 0) {
202 /* IPN service exists */
203 sockun.sun_family = AF_IPN;
205 #endif
206 if((conn->fddata = socket(AF_IPN_STOLEN,SOCK_RAW,IPN_ANY)) >= 0) {
207 /* IPN_STOLEN service exists */
208 sockun->sun_family = AF_IPN_STOLEN;
210 if (conn->fddata >= 0) {
211 if (port != 0 || req->type == REQ_NEW_PORT0)
212 setsockopt(conn->fddata,0,IPN_SO_PORT,&port,sizeof(port));
213 /* If we're given a sockname, just try it */
214 if (given_sockname)
216 snprintf(sockun->sun_path, sizeof(sockun->sun_path), "%s", sockname);
217 res = connect(conn->fddata, (struct sockaddr *) sockun, sizeof(*sockun));
219 /* Else try all the fallback socknames, one by one */
220 else
222 int i;
223 for (i = 0, res = -1; fallback_sockname[i] && (res != 0); i++)
225 snprintf(sockun->sun_path, sizeof(sockun->sun_path), "%s", fallback_sockname[i]);
226 res = connect(conn->fddata, (struct sockaddr *) sockun, sizeof(*sockun));
230 /* If one of the connect succeeded, we're done */
231 if (res == 0)
233 snprintf(req->description,MAXDESCR,"%s user=%s PID=%d %s",
234 descr,(callerpwd != NULL)?callerpwd->pw_name:"??",
235 pid,getenv("SSH_CLIENT")?getenv("SSH_CLIENT"):"");
236 setsockopt(conn->fddata,0,IPN_SO_DESCR,req->description,
237 strlen(req->description+1));
238 *(conn->inpath.sun_path)=0; /*null string, do not delete "return path"*/
239 conn->fdctl=-1;
240 goto cleanup;
243 #endif
244 if((conn->fdctl = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
245 goto abort;
246 if((conn->fddata = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)
247 goto abort;
248 sockun->sun_family = AF_UNIX;
250 /* If we're given a sockname, just try it (remember: sockname is the
251 * canonicalized version of given_sockname - though we don't strictly need
252 * the canonicalized versiono here). sockname should be the name of a
253 * *directory* which contains the control socket, named ctl. Older
254 * versions of VDE used a socket instead of a directory (so an additional
255 * attempt with %s instead of %s/ctl could be made), but they should
256 * really not be used anymore. */
257 if (given_sockname)
259 snprintf(sockun->sun_path, sizeof(sockun->sun_path), "%s/ctl", sockname);
260 res = connect(conn->fdctl, (struct sockaddr *) sockun, sizeof(*sockun));
262 /* Else try all the fallback socknames, one by one */
263 else
265 int i;
266 for (i = 0, res = -1; fallback_sockname[i] && (res != 0); i++)
268 /* Remember sockname for the data socket directory */
269 sockname = fallback_sockname[i];
270 snprintf(sockun->sun_path, sizeof(sockun->sun_path), "%s", sockname);
271 res = connect(conn->fdctl, (struct sockaddr *) sockun, sizeof(*sockun));
275 if (res != 0)
276 goto abort;
278 req->magic=SWITCH_MAGIC;
279 req->version=3;
280 req->type=req->type+(port << 8);
281 req->sock.sun_family=AF_UNIX;
283 /* First choice, store the return socket from the switch in the control
284 * dir. We assume that given_sockname (hence sockname) is a directory.
285 * Should be a safe assumption unless someone modifies the previous group
286 * of connect() attempts (see the comments above for more information). */
287 memset(req->sock.sun_path, 0, sizeof(req->sock.sun_path));
290 /* Here sockname is the last successful one in the previous step. */
291 sprintf(req->sock.sun_path, "%s/.%05d-%05d", sockname, pid, sockno++);
292 res=bind(conn->fddata, (struct sockaddr *) &req->sock, sizeof (req->sock));
294 while (res < 0 && errno == EADDRINUSE);
296 /* It didn't work. So we cycle on the fallback directories until we find a
297 * suitable one (or the list ends). */
298 if (res < 0)
300 int i;
301 for (i = 0, res = -1; fallback_dirname[i] && (res != 0); i++)
303 memset(req->sock.sun_path, 0, sizeof(req->sock.sun_path));
306 sprintf(req->sock.sun_path, "%s/vde.%05d-%05d", fallback_dirname[i], pid, sockno++);
307 res = bind(conn->fddata, (struct sockaddr *) &req->sock, sizeof (req->sock));
309 while (res < 0 && errno == EADDRINUSE);
313 /* Nothing worked, so cleanup and return with an error. */
314 if (res < 0)
315 goto abort;
317 memcpy(&(conn->inpath),&req->sock,sizeof(req->sock));
318 if (group) {
319 struct group *gs;
320 gid_t gid;
321 if ((gs=getgrnam(group)) == NULL)
322 gid=atoi(group);
323 else
324 gid=gs->gr_gid;
325 chown(conn->inpath.sun_path,-1,gid);
326 } else {
327 /* when group is not defined, set permission for the reverse channel */
328 struct stat ctlstat;
329 /* if no permission gets "voluntarily" granted to the socket */
330 if ((mode & 077) == 0) {
331 if (stat(sockun->sun_path, &ctlstat) == 0) {
332 /* if the switch is owned by root or by the same user it should
333 work 0700 */
334 if (ctlstat.st_uid != 0 && ctlstat.st_uid != geteuid()) {
335 /* try to change the group ownership to the same of the switch */
336 /* this call succeeds if the vde user and the owner of the switch
337 belong to the group */
338 if (chown(conn->inpath.sun_path,-1,ctlstat.st_gid) == 0)
339 mode |= 070;
340 else
341 mode |= 077;
346 chmod(conn->inpath.sun_path,mode);
348 snprintf(req->description,MAXDESCR,"%s user=%s PID=%d %s SOCK=%s",
349 descr,(callerpwd != NULL)?callerpwd->pw_name:"??",
350 pid,getenv("SSH_CLIENT")?getenv("SSH_CLIENT"):"",req->sock.sun_path);
352 if (send(conn->fdctl,req,sizeof(*req)-MAXDESCR+strlen(req->description),0)<0)
353 goto abort;
355 if (recv(conn->fdctl,dataout,sizeof(struct sockaddr_un),0)<0)
356 goto abort;
358 if (connect(conn->fddata,(struct sockaddr *)dataout,sizeof(struct sockaddr_un))<0)
359 goto abort;
361 chmod(dataout->sun_path,mode);
363 goto cleanup;
365 abort:
367 int err=errno;
368 if (conn) {
369 if (conn->fdctl >= 0)
370 close(conn->fdctl);
371 if (conn->fddata >= 0)
372 close(conn->fddata);
373 if (*(conn->inpath.sun_path))
374 unlink(conn->inpath.sun_path);
375 free(conn);
377 conn = NULL;
378 errno=err;
380 cleanup:
381 if (req) free(req);
382 if (sockun) free(sockun);
383 if (dataout) free(dataout);
384 if (std_sockname) free(std_sockname);
385 if (real_sockname) free(real_sockname);
386 return conn;
389 ssize_t vde_recv(VDECONN *conn,void *buf,size_t len,int flags)
391 if (__builtin_expect(conn!=0,1))
392 return recv(conn->fddata,buf,len,0);
393 else {
394 errno=EBADF;
395 return -1;
399 ssize_t vde_send(VDECONN *conn,const void *buf,size_t len,int flags)
401 if (__builtin_expect(conn!=0,1))
402 return send(conn->fddata,buf,len,0);
403 else {
404 errno=EBADF;
405 return -1;
409 int vde_datafd(VDECONN *conn)
411 if (__builtin_expect(conn!=0,1))
412 return conn->fddata;
413 else {
414 errno=EBADF;
415 return -1;
419 int vde_ctlfd(VDECONN *conn)
421 if (__builtin_expect(conn!=0,1))
422 return conn->fdctl;
423 else {
424 errno=EBADF;
425 return -1;
429 int vde_close(VDECONN *conn)
431 if (__builtin_expect(conn!=0,1)) {
432 if (*(conn->inpath.sun_path))
433 unlink(conn->inpath.sun_path);
434 close(conn->fddata);
435 close(conn->fdctl);
436 free(conn);
437 return 0;
438 } else {
439 errno=EBADF;
440 return -1;