168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
|
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
|
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
+
+
-
-
-
+
-
-
-
+
+
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
+
-
-
-
-
-
-
-
-
-
-
+
-
-
-
|
{
signal(signum, SIG_DFL);
cleanup_utmp();
setuid(getuid());
raise(signum);
}
/*
* Called to set up the pty.
*
* Returns an error message, or NULL on success.
*
* Also places the canonical host name into `realhost'. It must be
* freed by the caller.
*/
static char *pty_init(char *host, int port, char **realhost, int nodelay)
static void pty_open_master(void)
{
int slavefd;
char name[FILENAME_MAX];
pid_t pid, pgrp;
#ifdef BSD_PTYS
{
const char chars1[] = "pqrstuvwxyz";
const char chars2[] = "0123456789abcdef";
const char *p1, *p2;
char master_name[20];
const char chars1[] = "pqrstuvwxyz";
const char chars2[] = "0123456789abcdef";
const char *p1, *p2;
char master_name[20];
struct group *gp;
for (p1 = chars1; *p1; p1++)
for (p2 = chars2; *p2; p2++) {
sprintf(master_name, "/dev/pty%c%c", *p1, *p2);
pty_master_fd = open(master_name, O_RDWR);
if (pty_master_fd >= 0) {
if (geteuid() == 0 ||
access(master_name, R_OK | W_OK) == 0)
goto got_one;
close(pty_master_fd);
}
}
for (p1 = chars1; *p1; p1++)
for (p2 = chars2; *p2; p2++) {
sprintf(master_name, "/dev/pty%c%c", *p1, *p2);
pty_master_fd = open(master_name, O_RDWR);
if (pty_master_fd >= 0) {
if (geteuid() == 0 ||
access(master_name, R_OK | W_OK) == 0)
goto got_one;
close(pty_master_fd);
}
}
/* If we get here, we couldn't get a tty at all. */
fprintf(stderr, "pterm: unable to open a pseudo-terminal device\n");
exit(1);
/* If we get here, we couldn't get a tty at all. */
fprintf(stderr, "pterm: unable to open a pseudo-terminal device\n");
exit(1);
got_one:
strcpy(name, master_name);
name[5] = 't'; /* /dev/ptyXX -> /dev/ttyXX */
}
got_one:
strcpy(pty_name, master_name);
pty_name[5] = 't'; /* /dev/ptyXX -> /dev/ttyXX */
/* We need to chown/chmod the /dev/ttyXX device. */
gp = getgrnam("tty");
chown(pty_name, getuid(), gp ? gp->gr_gid : -1);
chmod(pty_name, 0600);
#else
pty_master_fd = open("/dev/ptmx", O_RDWR);
if (pty_master_fd < 0) {
perror("/dev/ptmx: open");
exit(1);
}
if (grantpt(pty_master_fd) < 0) {
perror("grantpt");
exit(1);
}
if (unlockpt(pty_master_fd) < 0) {
perror("unlockpt");
exit(1);
}
name[FILENAME_MAX-1] = '\0';
strncpy(name, ptsname(pty_master_fd), FILENAME_MAX-1);
pty_name[FILENAME_MAX-1] = '\0';
strncpy(pty_name, ptsname(pty_master_fd), FILENAME_MAX-1);
#endif
}
/*
* Pre-initialisation. This is here to get around the fact that GTK
* doesn't like being run in setuid/setgid programs (probably
* sensibly). So before we initialise GTK - and therefore before we
* even process the command line - we check to see if we're running
* set[ug]id. If so, we open our pty master _now_, chown it as
* necessary, and drop privileges. We can always close it again
* later. If we're potentially going to be doing utmp as well, we
* also fork off a utmp helper process and communicate with it by
* means of a pipe; the utmp helper will keep privileges in order
* to clean up utmp when we exit (i.e. when its end of our pipe
* closes).
*/
void pty_pre_init(void)
{
pid_t pid;
int pipefd[2];
pty_master_fd = -1;
if (geteuid() != getuid() || getegid() != getgid()) {
pty_open_master();
}
#ifndef OMIT_UTMP
/*
* Fork off the utmp helper.
*/
if (pipe(pipefd) < 0) {
perror("pterm: pipe");
exit(1);
}
pid = fork();
if (pid < 0) {
perror("pterm: fork");
exit(1);
} else if (pid == 0) {
char display[128], buffer[128];
int dlen, ret;
close(pipefd[1]);
/*
* Now sit here until we receive a display name from the
* other end of the pipe, and then stamp utmp. Unstamp utmp
* again, and exit, when the pipe closes.
*/
dlen = 0;
while (1) {
ret = read(pipefd[0], buffer, lenof(buffer));
if (ret <= 0) {
cleanup_utmp();
exit(0);
} else if (!pty_stamped_utmp) {
if (dlen < lenof(display))
memcpy(display+dlen, buffer,
min(ret, lenof(display)-dlen));
if (buffer[ret-1] == '\0') {
/*
* Now we have a display name. NUL-terminate
* it, and stamp utmp.
*/
display[lenof(display)-1] = '\0';
/*
* Trap as many fatal signals as we can in the
* hope of having the best possible chance to
* clean up utmp before termination. We are
* unfortunately unprotected against SIGKILL,
* but that's life.
*/
signal(SIGHUP, fatal_sig_handler);
signal(SIGINT, fatal_sig_handler);
signal(SIGQUIT, fatal_sig_handler);
signal(SIGILL, fatal_sig_handler);
signal(SIGABRT, fatal_sig_handler);
signal(SIGFPE, fatal_sig_handler);
signal(SIGPIPE, fatal_sig_handler);
signal(SIGALRM, fatal_sig_handler);
signal(SIGTERM, fatal_sig_handler);
signal(SIGSEGV, fatal_sig_handler);
signal(SIGUSR1, fatal_sig_handler);
signal(SIGUSR2, fatal_sig_handler);
#ifdef SIGBUS
signal(SIGBUS, fatal_sig_handler);
#endif
#ifdef SIGPOLL
signal(SIGPOLL, fatal_sig_handler);
#endif
#ifdef SIGPROF
signal(SIGPROF, fatal_sig_handler);
#endif
#ifdef SIGSYS
signal(SIGSYS, fatal_sig_handler);
#endif
#ifdef SIGTRAP
signal(SIGTRAP, fatal_sig_handler);
#endif
#ifdef SIGVTALRM
signal(SIGVTALRM, fatal_sig_handler);
#endif
#ifdef SIGXCPU
signal(SIGXCPU, fatal_sig_handler);
#endif
#ifdef SIGXFSZ
signal(SIGXFSZ, fatal_sig_handler);
#endif
#ifdef SIGIO
signal(SIGIO, fatal_sig_handler);
#endif
/* Also clean up utmp on normal exit. */
atexit(cleanup_utmp);
setup_utmp(pty_name, display);
}
}
}
} else {
close(pipefd[0]);
pty_utmp_helper_pid = pid;
pty_utmp_helper_pipe = pipefd[1];
}
#endif
/* Drop privs. */
{
int gid = getgid(), uid = getuid();
#ifndef HAVE_NO_SETRESUID
int setresgid(gid_t, gid_t, gid_t);
int setresuid(uid_t, uid_t, uid_t);
setresgid(gid, gid, gid);
setresuid(uid, uid, uid);
#else
setgid(getgid());
setuid(getuid());
#endif
}
}
/*
* Called to set up the pty.
*
* Returns an error message, or NULL on success.
*
* Also places the canonical host name into `realhost'. It must be
* freed by the caller.
*/
static char *pty_init(char *host, int port, char **realhost, int nodelay)
{
int slavefd;
pid_t pid, pgrp;
if (pty_master_fd < 0)
pty_open_master();
/*
* Set the backspace character to be whichever of ^H and ^? is
* specified by bksp_is_delete.
*/
{
struct termios attrs;
tcgetattr(pty_master_fd, &attrs);
attrs.c_cc[VERASE] = cfg.bksp_is_delete ? '\177' : '\010';
tcsetattr(pty_master_fd, TCSANOW, &attrs);
}
/*
* Trap as many fatal signals as we can in the hope of having
* the best chance to clean up utmp before termination.
* Stamp utmp (that is, tell the utmp helper process to do so),
* or not.
*/
signal(SIGHUP, fatal_sig_handler);
signal(SIGINT, fatal_sig_handler);
signal(SIGQUIT, fatal_sig_handler);
signal(SIGILL, fatal_sig_handler);
signal(SIGABRT, fatal_sig_handler);
signal(SIGFPE, fatal_sig_handler);
signal(SIGPIPE, fatal_sig_handler);
signal(SIGALRM, fatal_sig_handler);
signal(SIGTERM, fatal_sig_handler);
signal(SIGSEGV, fatal_sig_handler);
signal(SIGUSR1, fatal_sig_handler);
signal(SIGUSR2, fatal_sig_handler);
#ifdef SIGBUS
signal(SIGBUS, fatal_sig_handler);
#endif
if (!cfg.stamp_utmp)
#ifdef SIGPOLL
signal(SIGPOLL, fatal_sig_handler);
#endif
close(pty_utmp_helper_pipe); /* just let the child process die */
else {
#ifdef SIGPROF
signal(SIGPROF, fatal_sig_handler);
#endif
char *location = get_x_display();
#ifdef SIGSYS
signal(SIGSYS, fatal_sig_handler);
#endif
int len = strlen(location)+1, pos = 0; /* +1 to include NUL */
while (pos < len) {
#ifdef SIGTRAP
signal(SIGTRAP, fatal_sig_handler);
#endif
int ret = write(pty_utmp_helper_pipe, location+pos, len - pos);
if (ret < 0) {
#ifdef SIGVTALRM
signal(SIGVTALRM, fatal_sig_handler);
#endif
#ifdef SIGXCPU
signal(SIGXCPU, fatal_sig_handler);
#endif
#ifdef SIGXFSZ
signal(SIGXFSZ, fatal_sig_handler);
#endif
#ifdef SIGIO
signal(SIGIO, fatal_sig_handler);
#endif
/* Also clean up utmp on normal exit. */
atexit(cleanup_utmp);
setup_utmp(name);
perror("pterm: writing to utmp helper process");
close(pty_utmp_helper_pipe); /* arrgh, just give up */
break;
}
pos += ret;
}
}
/*
* Fork and execute the command.
*/
pid = fork();
if (pid < 0) {
perror("fork");
exit(1);
}
if (pid == 0) {
int i;
/*
* We are the child.
*/
slavefd = open(name, O_RDWR);
slavefd = open(pty_name, O_RDWR);
if (slavefd < 0) {
perror("slave pty: open");
exit(1);
}
#ifdef BSD_PTYS
/* We need to chown/chmod the /dev/ttyXX device. */
{
struct group *gp = getgrnam("tty");
fchown(slavefd, getuid(), gp ? gp->gr_gid : -1);
fchmod(slavefd, 0600);
}
#endif
close(pty_master_fd);
close(0);
close(1);
close(2);
fcntl(slavefd, F_SETFD, 0); /* don't close on exec */
dup2(slavefd, 0);
dup2(slavefd, 1);
dup2(slavefd, 2);
setsid();
ioctl(slavefd, TIOCSCTTY, 1);
pgrp = getpid();
tcsetpgrp(slavefd, pgrp);
setpgrp();
close(open(name, O_WRONLY, 0));
close(open(pty_name, O_WRONLY, 0));
setpgrp();
/* In case we were setgid-utmp or setuid-root, drop privs. */
setgid(getgid());
setuid(getuid());
/* Close everything _else_, for tidiness. */
for (i = 3; i < 1024; i++)
close(i);
{
char term_env_var[10 + sizeof(cfg.termtype)];
sprintf(term_env_var, "TERM=%s", cfg.termtype);
putenv(term_env_var);
|