aboutsummaryrefslogtreecommitdiffstats
path: root/lib-src
diff options
context:
space:
mode:
authorPaul Eggert2018-12-02 22:32:28 -0800
committerPaul Eggert2018-12-02 23:55:00 -0800
commit3e5d7755454bea9b6ffd232b1d115c629cdb193d (patch)
treee73573782b9b426fde071e978236c84311fc25f2 /lib-src
parente5634aae531ce932ecb8d84243d690c7ca89bec3 (diff)
downloademacs-3e5d7755454bea9b6ffd232b1d115c629cdb193d.tar.gz
emacs-3e5d7755454bea9b6ffd232b1d115c629cdb193d.zip
emacsclient: fix symlink/socket race
* lib-src/emacsclient.c (socket_status): New arg UID. All uses changed. (set_local_socket): Don’t create the unbound socket unless the initial sanity checks on the socket file succeed; this simplifies cleaning it up. Check socket ownership again after connecting, to fix a race (Bug#33366).
Diffstat (limited to 'lib-src')
-rw-r--r--lib-src/emacsclient.c78
1 files changed, 41 insertions, 37 deletions
diff --git a/lib-src/emacsclient.c b/lib-src/emacsclient.c
index ba72651343f..df44bc4087c 100644
--- a/lib-src/emacsclient.c
+++ b/lib-src/emacsclient.c
@@ -1079,20 +1079,21 @@ find_tty (const char **tty_type, const char **tty_name, bool noabort)
1079 1079
1080#ifdef SOCKETS_IN_FILE_SYSTEM 1080#ifdef SOCKETS_IN_FILE_SYSTEM
1081 1081
1082/* Three possibilities: 1082/* Return the file status of NAME, ordinarily a socket.
1083 It should be owned by UID. Return one of the following:
1083 >0 - 'stat' failed with this errno value 1084 >0 - 'stat' failed with this errno value
1084 -1 - isn't owned by us 1085 -1 - isn't owned by us
1085 0 - success: none of the above */ 1086 0 - success: none of the above */
1086 1087
1087static int 1088static int
1088socket_status (const char *name) 1089socket_status (const char *name, uid_t uid)
1089{ 1090{
1090 struct stat statbfr; 1091 struct stat statbfr;
1091 1092
1092 if (stat (name, &statbfr) != 0) 1093 if (stat (name, &statbfr) != 0)
1093 return errno; 1094 return errno;
1094 1095
1095 if (statbfr.st_uid != geteuid ()) 1096 if (statbfr.st_uid != uid)
1096 return -1; 1097 return -1;
1097 1098
1098 return 0; 1099 return 0;
@@ -1316,18 +1317,11 @@ set_local_socket (char const *server_name)
1316 struct sockaddr_un un; 1317 struct sockaddr_un un;
1317 struct sockaddr sa; 1318 struct sockaddr sa;
1318 } server = {{ .sun_family = AF_UNIX }}; 1319 } server = {{ .sun_family = AF_UNIX }};
1319
1320 HSOCKET s = socket (AF_UNIX, SOCK_STREAM, 0);
1321 if (s < 0)
1322 {
1323 message (true, "%s: socket: %s\n", progname, strerror (errno));
1324 return INVALID_SOCKET;
1325 }
1326
1327 char *sockname = server.un.sun_path; 1320 char *sockname = server.un.sun_path;
1328 enum { socknamesize = sizeof server.un.sun_path }; 1321 enum { socknamesize = sizeof server.un.sun_path };
1329 int tmpdirlen = -1; 1322 int tmpdirlen = -1;
1330 int socknamelen = -1; 1323 int socknamelen = -1;
1324 uid_t uid = geteuid ();
1331 1325
1332 if (strchr (server_name, '/') 1326 if (strchr (server_name, '/')
1333 || (ISSLASH ('\\') && strchr (server_name, '\\'))) 1327 || (ISSLASH ('\\') && strchr (server_name, '\\')))
@@ -1359,7 +1353,7 @@ set_local_socket (char const *server_name)
1359 tmpdirlen = snprintf (sockname, socknamesize, "/tmp"); 1353 tmpdirlen = snprintf (sockname, socknamesize, "/tmp");
1360 } 1354 }
1361 socknamelen = local_sockname (sockname, socknamesize, tmpdirlen, 1355 socknamelen = local_sockname (sockname, socknamesize, tmpdirlen,
1362 geteuid (), server_name); 1356 uid, server_name);
1363 } 1357 }
1364 } 1358 }
1365 1359
@@ -1370,7 +1364,7 @@ set_local_socket (char const *server_name)
1370 } 1364 }
1371 1365
1372 /* See if the socket exists, and if it's owned by us. */ 1366 /* See if the socket exists, and if it's owned by us. */
1373 int sock_status = socket_status (sockname); 1367 int sock_status = socket_status (sockname, uid);
1374 if (sock_status) 1368 if (sock_status)
1375 { 1369 {
1376 /* Failing that, see if LOGNAME or USER exist and differ from 1370 /* Failing that, see if LOGNAME or USER exist and differ from
@@ -1387,7 +1381,7 @@ set_local_socket (char const *server_name)
1387 { 1381 {
1388 struct passwd *pw = getpwnam (user_name); 1382 struct passwd *pw = getpwnam (user_name);
1389 1383
1390 if (pw && (pw->pw_uid != geteuid ())) 1384 if (pw && pw->pw_uid != uid)
1391 { 1385 {
1392 /* We're running under su, apparently. */ 1386 /* We're running under su, apparently. */
1393 socknamelen = local_sockname (sockname, socknamesize, tmpdirlen, 1387 socknamelen = local_sockname (sockname, socknamesize, tmpdirlen,
@@ -1399,39 +1393,49 @@ set_local_socket (char const *server_name)
1399 exit (EXIT_FAILURE); 1393 exit (EXIT_FAILURE);
1400 } 1394 }
1401 1395
1402 sock_status = socket_status (sockname); 1396 sock_status = socket_status (sockname, uid);
1403 } 1397 }
1404 } 1398 }
1405 } 1399 }
1406 1400
1407 switch (sock_status) 1401 if (sock_status == 0)
1408 { 1402 {
1409 case -1: 1403 HSOCKET s = socket (AF_UNIX, SOCK_STREAM, 0);
1410 /* There's a socket, but it isn't owned by us. */ 1404 if (s < 0)
1411 message (true, "%s: Invalid socket owner\n", progname); 1405 {
1412 break; 1406 message (true, "%s: socket: %s\n", progname, strerror (errno));
1407 return INVALID_SOCKET;
1408 }
1409 if (connect (s, &server.sa, sizeof server.un) != 0)
1410 {
1411 message (true, "%s: connect: %s\n", progname, strerror (errno));
1412 CLOSE_SOCKET (s);
1413 return INVALID_SOCKET;
1414 }
1413 1415
1414 case 0: 1416 struct stat connect_stat;
1415 if (connect (s, &server.sa, sizeof server.un) == 0) 1417 if (fstat (s, &connect_stat) != 0)
1418 sock_status = errno;
1419 else if (connect_stat.st_uid == uid)
1416 return s; 1420 return s;
1417 message (true, "%s: connect: %s\n", progname, strerror (errno));
1418 break;
1419
1420 default:
1421 /* 'stat' failed. */
1422 if (sock_status == ENOENT)
1423 message (true,
1424 ("%s: can't find socket; have you started the server?\n"
1425 "%s: To start the server in Emacs,"
1426 " type \"M-x server-start\".\n"),
1427 progname, progname);
1428 else 1421 else
1429 message (true, "%s: can't stat %s: %s\n", 1422 sock_status = -1;
1430 progname, sockname, strerror (sock_status)); 1423
1431 break; 1424 CLOSE_SOCKET (s);
1432 } 1425 }
1433 1426
1434 CLOSE_SOCKET (s); 1427 if (sock_status < 0)
1428 message (true, "%s: Invalid socket owner\n", progname);
1429 else if (sock_status == ENOENT)
1430 message (true,
1431 ("%s: can't find socket; have you started the server?\n"
1432 "%s: To start the server in Emacs,"
1433 " type \"M-x server-start\".\n"),
1434 progname, progname);
1435 else
1436 message (true, "%s: can't stat %s: %s\n",
1437 progname, sockname, strerror (sock_status));
1438
1435 return INVALID_SOCKET; 1439 return INVALID_SOCKET;
1436} 1440}
1437#endif /* SOCKETS_IN_FILE_SYSTEM */ 1441#endif /* SOCKETS_IN_FILE_SYSTEM */