aboutsummaryrefslogtreecommitdiffstats
path: root/lib-src
diff options
context:
space:
mode:
authorYuuki Harano2021-07-25 23:34:55 +0900
committerYuuki Harano2021-07-25 23:34:55 +0900
commit13a9a5e836cbe6e64aadaba40fe1f7eb83320d08 (patch)
tree242ac1f485cf6762680a904952747d63b295e198 /lib-src
parentb242394f24b154f8e20f5abf4b2f826629e99ea6 (diff)
parent41a55a330f518254da795719ac6e3085254d4110 (diff)
downloademacs-13a9a5e836cbe6e64aadaba40fe1f7eb83320d08.tar.gz
emacs-13a9a5e836cbe6e64aadaba40fe1f7eb83320d08.zip
Merge branch 'master' of git.sv.gnu.org:/srv/git/emacs into feature/pgtk
Diffstat (limited to 'lib-src')
-rw-r--r--lib-src/emacsclient.c226
1 files changed, 147 insertions, 79 deletions
diff --git a/lib-src/emacsclient.c b/lib-src/emacsclient.c
index 6d3f827a253..8fa571fd4ef 100644
--- a/lib-src/emacsclient.c
+++ b/lib-src/emacsclient.c
@@ -80,6 +80,9 @@ char *w32_getenv (const char *);
80#include <sys/stat.h> 80#include <sys/stat.h>
81#include <unistd.h> 81#include <unistd.h>
82 82
83#ifndef WINDOWSNT
84# include <acl.h>
85#endif
83#include <filename.h> 86#include <filename.h>
84#include <intprops.h> 87#include <intprops.h>
85#include <min-max.h> 88#include <min-max.h>
@@ -91,6 +94,10 @@ char *w32_getenv (const char *);
91# pragma GCC diagnostic ignored "-Wformat-truncation=2" 94# pragma GCC diagnostic ignored "-Wformat-truncation=2"
92#endif 95#endif
93 96
97#if !defined O_PATH && !defined WINDOWSNT
98# define O_PATH O_SEARCH
99#endif
100
94 101
95/* Name used to invoke this program. */ 102/* Name used to invoke this program. */
96static char const *progname; 103static char const *progname;
@@ -1133,24 +1140,74 @@ process_grouping (void)
1133 1140
1134#ifdef SOCKETS_IN_FILE_SYSTEM 1141#ifdef SOCKETS_IN_FILE_SYSTEM
1135 1142
1136/* Return the file status of NAME, ordinarily a socket. 1143/* A local socket address. The union avoids the need to cast. */
1137 It should be owned by UID. Return one of the following: 1144union local_sockaddr
1138 >0 - 'stat' failed with this errno value 1145{
1139 -1 - isn't owned by us 1146 struct sockaddr_un un;
1140 0 - success: none of the above */ 1147 struct sockaddr sa;
1148};
1149
1150/* Relative to the directory DIRFD, connect the socket file named ADDR
1151 to the socket S. Return 0 if successful, -1 if DIRFD is not
1152 AT_FDCWD and DIRFD's permissions would allow a symlink attack, an
1153 errno otherwise. */
1141 1154
1142static int 1155static int
1143socket_status (const char *name, uid_t uid) 1156connect_socket (int dirfd, char const *addr, int s, uid_t uid)
1144{ 1157{
1145 struct stat statbfr; 1158 int sock_status = 0;
1146 1159
1147 if (stat (name, &statbfr) != 0) 1160 union local_sockaddr server;
1148 return errno; 1161 if (sizeof server.un.sun_path <= strlen (addr))
1162 return ENAMETOOLONG;
1163 server.un.sun_family = AF_UNIX;
1164 strcpy (server.un.sun_path, addr);
1149 1165
1150 if (statbfr.st_uid != uid) 1166 /* If -1, WDFD is not set yet. If nonnegative, WDFD is a file
1151 return -1; 1167 descriptor for the initial working directory. Otherwise -1 - WDFD is
1168 the error number for the initial working directory. */
1169 static int wdfd = -1;
1152 1170
1153 return 0; 1171 if (dirfd != AT_FDCWD)
1172 {
1173 /* Fail if DIRFD's permissions are bogus. */
1174 struct stat st;
1175 if (fstat (dirfd, &st) != 0)
1176 return errno;
1177 if (st.st_uid != uid || (st.st_mode & (S_IWGRP | S_IWOTH)))
1178 return -1;
1179
1180 if (wdfd == -1)
1181 {
1182 /* Save the initial working directory. */
1183 wdfd = open (".", O_PATH | O_CLOEXEC);
1184 if (wdfd < 0)
1185 wdfd = -1 - errno;
1186 }
1187 if (wdfd < 0)
1188 return -1 - wdfd;
1189 if (fchdir (dirfd) != 0)
1190 return errno;
1191
1192 /* Fail if DIRFD has an ACL, which means its permissions are
1193 almost surely bogus. */
1194 int has_acl = file_has_acl (".", &st);
1195 if (has_acl)
1196 sock_status = has_acl < 0 ? errno : -1;
1197 }
1198
1199 if (!sock_status)
1200 sock_status = connect (s, &server.sa, sizeof server.un) == 0 ? 0 : errno;
1201
1202 /* Fail immediately if we cannot change back to the initial working
1203 directory, as that can mess up the rest of execution. */
1204 if (dirfd != AT_FDCWD && fchdir (wdfd) != 0)
1205 {
1206 message (true, "%s: .: %s\n", progname, strerror (errno));
1207 exit (EXIT_FAILURE);
1208 }
1209
1210 return sock_status;
1154} 1211}
1155 1212
1156 1213
@@ -1327,32 +1384,49 @@ act_on_signals (HSOCKET emacs_socket)
1327 } 1384 }
1328} 1385}
1329 1386
1330/* Create in SOCKNAME (of size SOCKNAMESIZE) a name for a local socket. 1387enum { socknamesize = sizeof ((struct sockaddr_un *) NULL)->sun_path };
1331 The first TMPDIRLEN bytes of SOCKNAME are already initialized to be 1388
1332 the name of a temporary directory. Use UID and SERVER_NAME to 1389/* Given a local socket S, create in *SOCKNAME a name for a local socket
1333 concoct the name. Return the total length of the name if successful, 1390 and connect to that socket. The first TMPDIRLEN bytes of *SOCKNAME are
1334 -1 if it does not fit (and store a truncated name in that case). 1391 already initialized to be the name of a temporary directory.
1335 Fail if TMPDIRLEN is out of range. */ 1392 Use UID and SERVER_NAME to concoct the name. Return 0 if
1393 successful, -1 if the socket's parent directory is not safe, and an
1394 errno if there is some other problem. */
1336 1395
1337static int 1396static int
1338local_sockname (char *sockname, int socknamesize, int tmpdirlen, 1397local_sockname (int s, char sockname[socknamesize], int tmpdirlen,
1339 uintmax_t uid, char const *server_name) 1398 uid_t uid, char const *server_name)
1340{ 1399{
1341 /* If ! (0 <= TMPDIRLEN && TMPDIRLEN < SOCKNAMESIZE) the truncated 1400 /* If ! (0 <= TMPDIRLEN && TMPDIRLEN < SOCKNAMESIZE) the truncated
1342 temporary directory name is already in SOCKNAME, so nothing more 1401 temporary directory name is already in SOCKNAME, so nothing more
1343 need be stored. */ 1402 need be stored. */
1344 if (0 <= tmpdirlen) 1403 if (! (0 <= tmpdirlen && tmpdirlen < socknamesize))
1345 { 1404 return ENAMETOOLONG;
1346 int remaining = socknamesize - tmpdirlen; 1405
1347 if (0 < remaining) 1406 /* Put the full address name into the buffer, since the caller might
1348 { 1407 need it for diagnostics. But don't overrun the buffer. */
1349 int suffixlen = snprintf (&sockname[tmpdirlen], remaining, 1408 uintmax_t uidmax = uid;
1350 "/emacs%"PRIuMAX"/%s", uid, server_name); 1409 int emacsdirlen;
1351 if (0 <= suffixlen && suffixlen < remaining) 1410 int suffixlen = snprintf (sockname + tmpdirlen, socknamesize - tmpdirlen,
1352 return tmpdirlen + suffixlen; 1411 "/emacs%"PRIuMAX"%n/%s", uidmax, &emacsdirlen,
1353 } 1412 server_name);
1354 } 1413 if (! (0 <= suffixlen && suffixlen < socknamesize - tmpdirlen))
1355 return -1; 1414 return ENAMETOOLONG;
1415
1416 /* Make sure the address's parent directory is not a symlink and is
1417 this user's directory and does not let others write to it; this
1418 fends off some symlink attacks. To avoid races, keep the parent
1419 directory open while checking. */
1420 char *emacsdirend = sockname + tmpdirlen + emacsdirlen;
1421 *emacsdirend = '\0';
1422 int dir = openat (AT_FDCWD, sockname,
1423 O_PATH | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC);
1424 *emacsdirend = '/';
1425 if (dir < 0)
1426 return errno;
1427 int sock_status = connect_socket (dir, server_name, s, uid);
1428 close (dir);
1429 return sock_status;
1356} 1430}
1357 1431
1358/* Create a local socket for SERVER_NAME and connect it to Emacs. If 1432/* Create a local socket for SERVER_NAME and connect it to Emacs. If
@@ -1363,28 +1437,43 @@ local_sockname (char *sockname, int socknamesize, int tmpdirlen,
1363static HSOCKET 1437static HSOCKET
1364set_local_socket (char const *server_name) 1438set_local_socket (char const *server_name)
1365{ 1439{
1366 union { 1440 union local_sockaddr server;
1367 struct sockaddr_un un; 1441 int sock_status;
1368 struct sockaddr sa;
1369 } server = {{ .sun_family = AF_UNIX }};
1370 char *sockname = server.un.sun_path; 1442 char *sockname = server.un.sun_path;
1371 enum { socknamesize = sizeof server.un.sun_path };
1372 int tmpdirlen = -1; 1443 int tmpdirlen = -1;
1373 int socknamelen = -1; 1444 int socknamelen = -1;
1374 uid_t uid = geteuid (); 1445 uid_t uid = geteuid ();
1375 bool tmpdir_used = false; 1446 bool tmpdir_used = false;
1447 int s = cloexec_socket (AF_UNIX, SOCK_STREAM, 0);
1448 if (s < 0)
1449 {
1450 message (true, "%s: can't create socket: %s\n",
1451 progname, strerror (errno));
1452 fail ();
1453 }
1376 1454
1377 if (strchr (server_name, '/') 1455 if (strchr (server_name, '/')
1378 || (ISSLASH ('\\') && strchr (server_name, '\\'))) 1456 || (ISSLASH ('\\') && strchr (server_name, '\\')))
1379 socknamelen = snprintf (sockname, socknamesize, "%s", server_name); 1457 {
1458 socknamelen = snprintf (sockname, socknamesize, "%s", server_name);
1459 sock_status = (0 <= socknamelen && socknamelen < socknamesize
1460 ? connect_socket (AT_FDCWD, sockname, s, 0)
1461 : ENAMETOOLONG);
1462 }
1380 else 1463 else
1381 { 1464 {
1382 /* socket_name is a file name component. */ 1465 /* socket_name is a file name component. */
1466 sock_status = ENOENT;
1383 char const *xdg_runtime_dir = egetenv ("XDG_RUNTIME_DIR"); 1467 char const *xdg_runtime_dir = egetenv ("XDG_RUNTIME_DIR");
1384 if (xdg_runtime_dir) 1468 if (xdg_runtime_dir)
1385 socknamelen = snprintf (sockname, socknamesize, "%s/emacs/%s", 1469 {
1386 xdg_runtime_dir, server_name); 1470 socknamelen = snprintf (sockname, socknamesize, "%s/emacs/%s",
1387 else 1471 xdg_runtime_dir, server_name);
1472 sock_status = (0 <= socknamelen && socknamelen < socknamesize
1473 ? connect_socket (AT_FDCWD, sockname, s, 0)
1474 : ENAMETOOLONG);
1475 }
1476 if (sock_status == ENOENT)
1388 { 1477 {
1389 char const *tmpdir = egetenv ("TMPDIR"); 1478 char const *tmpdir = egetenv ("TMPDIR");
1390 if (tmpdir) 1479 if (tmpdir)
@@ -1403,23 +1492,24 @@ set_local_socket (char const *server_name)
1403 if (tmpdirlen < 0) 1492 if (tmpdirlen < 0)
1404 tmpdirlen = snprintf (sockname, socknamesize, "/tmp"); 1493 tmpdirlen = snprintf (sockname, socknamesize, "/tmp");
1405 } 1494 }
1406 socknamelen = local_sockname (sockname, socknamesize, tmpdirlen, 1495 sock_status = local_sockname (s, sockname, tmpdirlen,
1407 uid, server_name); 1496 uid, server_name);
1408 tmpdir_used = true; 1497 tmpdir_used = true;
1409 } 1498 }
1410 } 1499 }
1411 1500
1412 if (! (0 <= socknamelen && socknamelen < socknamesize)) 1501 if (sock_status == 0)
1502 return s;
1503
1504 if (sock_status == ENAMETOOLONG)
1413 { 1505 {
1414 message (true, "%s: socket-name %s... too long\n", progname, sockname); 1506 message (true, "%s: socket-name %s... too long\n", progname, sockname);
1415 fail (); 1507 fail ();
1416 } 1508 }
1417 1509
1418 /* See if the socket exists, and if it's owned by us. */ 1510 if (tmpdir_used)
1419 int sock_status = socket_status (sockname, uid);
1420 if (sock_status)
1421 { 1511 {
1422 /* Failing that, see if LOGNAME or USER exist and differ from 1512 /* See whether LOGNAME or USER exist and differ from
1423 our euid. If so, look for a socket based on the UID 1513 our euid. If so, look for a socket based on the UID
1424 associated with the name. This is reminiscent of the logic 1514 associated with the name. This is reminiscent of the logic
1425 that init_editfns uses to set the global Vuser_full_name. */ 1515 that init_editfns uses to set the global Vuser_full_name. */
@@ -1436,48 +1526,26 @@ set_local_socket (char const *server_name)
1436 if (pw && pw->pw_uid != uid) 1526 if (pw && pw->pw_uid != uid)
1437 { 1527 {
1438 /* We're running under su, apparently. */ 1528 /* We're running under su, apparently. */
1439 socknamelen = local_sockname (sockname, socknamesize, tmpdirlen, 1529 sock_status = local_sockname (s, sockname, tmpdirlen,
1440 pw->pw_uid, server_name); 1530 pw->pw_uid, server_name);
1441 if (socknamelen < 0) 1531 if (sock_status == 0)
1532 return s;
1533 if (sock_status == ENAMETOOLONG)
1442 { 1534 {
1443 message (true, "%s: socket-name %s... too long\n", 1535 message (true, "%s: socket-name %s... too long\n",
1444 progname, sockname); 1536 progname, sockname);
1445 exit (EXIT_FAILURE); 1537 exit (EXIT_FAILURE);
1446 } 1538 }
1447
1448 sock_status = socket_status (sockname, uid);
1449 } 1539 }
1450 } 1540 }
1451 } 1541 }
1452 1542
1453 if (sock_status == 0) 1543 close (s);
1454 {
1455 HSOCKET s = cloexec_socket (AF_UNIX, SOCK_STREAM, 0);
1456 if (s < 0)
1457 {
1458 message (true, "%s: socket: %s\n", progname, strerror (errno));
1459 return INVALID_SOCKET;
1460 }
1461 if (connect (s, &server.sa, sizeof server.un) != 0)
1462 {
1463 message (true, "%s: connect: %s\n", progname, strerror (errno));
1464 CLOSE_SOCKET (s);
1465 return INVALID_SOCKET;
1466 }
1467 1544
1468 struct stat connect_stat; 1545 if (sock_status == -1)
1469 if (fstat (s, &connect_stat) != 0) 1546 message (true,
1470 sock_status = errno; 1547 "%s: Invalid permissions on parent directory of socket: %s\n",
1471 else if (connect_stat.st_uid == uid) 1548 progname, sockname);
1472 return s;
1473 else
1474 sock_status = -1;
1475
1476 CLOSE_SOCKET (s);
1477 }
1478
1479 if (sock_status < 0)
1480 message (true, "%s: Invalid socket owner\n", progname);
1481 else if (sock_status == ENOENT) 1549 else if (sock_status == ENOENT)
1482 { 1550 {
1483 if (tmpdir_used) 1551 if (tmpdir_used)
@@ -1507,7 +1575,7 @@ set_local_socket (char const *server_name)
1507 } 1575 }
1508 } 1576 }
1509 else 1577 else
1510 message (true, "%s: can't stat %s: %s\n", 1578 message (true, "%s: can't connect to %s: %s\n",
1511 progname, sockname, strerror (sock_status)); 1579 progname, sockname, strerror (sock_status));
1512 1580
1513 return INVALID_SOCKET; 1581 return INVALID_SOCKET;