diff options
| author | Paul Eggert | 2018-12-02 22:32:28 -0800 |
|---|---|---|
| committer | Paul Eggert | 2018-12-02 23:55:00 -0800 |
| commit | 3e5d7755454bea9b6ffd232b1d115c629cdb193d (patch) | |
| tree | e73573782b9b426fde071e978236c84311fc25f2 /lib-src | |
| parent | e5634aae531ce932ecb8d84243d690c7ca89bec3 (diff) | |
| download | emacs-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.c | 78 |
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 | ||
| 1087 | static int | 1088 | static int |
| 1088 | socket_status (const char *name) | 1089 | socket_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 */ |