Diff

Differences From Artifact [325188697d]:

To Artifact [7068ffe502]:


51
52
53
54
55
56
57

58
59

60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
#ifndef OMIT_UTMP
#if !defined HAVE_PUTUTLINE
#define HAVE_PUTUTLINE
#endif
#endif

int pty_master_fd;

static int pty_stamped_utmp = 0;
static int pty_child_pid;

static sig_atomic_t pty_child_dead;
#ifndef OMIT_UTMP
static struct utmp utmp_entry;
#endif
char **pty_argv;

int pty_child_is_dead(void)
{
    return pty_child_dead;
}

static void pty_size(void);

static void setup_utmp(char *ttyname)
{
#ifndef OMIT_UTMP
#ifdef HAVE_LASTLOG
    struct lastlog lastlog_entry;
    FILE *lastlog;
#endif
    struct passwd *pw;
    char *location;
    FILE *wtmp;

    if (!cfg.stamp_utmp)
	return;

    pw = getpwuid(getuid());
    location = get_x_display();
    memset(&utmp_entry, 0, sizeof(utmp_entry));
    utmp_entry.ut_type = USER_PROCESS;
    utmp_entry.ut_pid = getpid();
    strncpy(utmp_entry.ut_line, ttyname+5, lenof(utmp_entry.ut_line));
    strncpy(utmp_entry.ut_id, ttyname+8, lenof(utmp_entry.ut_id));
    strncpy(utmp_entry.ut_user, pw->pw_name, lenof(utmp_entry.ut_user));
    strncpy(utmp_entry.ut_host, location, lenof(utmp_entry.ut_host));







>


>













|







<


<
<
<

<







51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82

83
84



85

86
87
88
89
90
91
92
#ifndef OMIT_UTMP
#if !defined HAVE_PUTUTLINE
#define HAVE_PUTUTLINE
#endif
#endif

int pty_master_fd;
static char pty_name[FILENAME_MAX];
static int pty_stamped_utmp = 0;
static int pty_child_pid;
static int pty_utmp_helper_pid, pty_utmp_helper_pipe;
static sig_atomic_t pty_child_dead;
#ifndef OMIT_UTMP
static struct utmp utmp_entry;
#endif
char **pty_argv;

int pty_child_is_dead(void)
{
    return pty_child_dead;
}

static void pty_size(void);

static void setup_utmp(char *ttyname, char *location)
{
#ifndef OMIT_UTMP
#ifdef HAVE_LASTLOG
    struct lastlog lastlog_entry;
    FILE *lastlog;
#endif
    struct passwd *pw;

    FILE *wtmp;




    pw = getpwuid(getuid());

    memset(&utmp_entry, 0, sizeof(utmp_entry));
    utmp_entry.ut_type = USER_PROCESS;
    utmp_entry.ut_pid = getpid();
    strncpy(utmp_entry.ut_line, ttyname+5, lenof(utmp_entry.ut_line));
    strncpy(utmp_entry.ut_id, ttyname+8, lenof(utmp_entry.ut_id));
    strncpy(utmp_entry.ut_user, pw->pw_name, lenof(utmp_entry.ut_user));
    strncpy(utmp_entry.ut_host, location, lenof(utmp_entry.ut_host));
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
}

static void cleanup_utmp(void)
{
#ifndef OMIT_UTMP
    FILE *wtmp;

    if (!cfg.stamp_utmp || !pty_stamped_utmp)
	return;

    utmp_entry.ut_type = DEAD_PROCESS;
    memset(utmp_entry.ut_user, 0, lenof(utmp_entry.ut_user));
    time(&utmp_entry.ut_time);

    if ((wtmp = fopen(WTMP_FILE, "a")) != NULL) {







|







122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
}

static void cleanup_utmp(void)
{
#ifndef OMIT_UTMP
    FILE *wtmp;

    if (!pty_stamped_utmp)
	return;

    utmp_entry.ut_type = DEAD_PROCESS;
    memset(utmp_entry.ut_user, 0, lenof(utmp_entry.ut_user));
    time(&utmp_entry.ut_time);

    if ((wtmp = fopen(WTMP_FILE, "a")) != NULL) {
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
{
    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)
{
    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];


	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);

	got_one:
	strcpy(name, master_name);
	name[5] = 't';		       /* /dev/ptyXX -> /dev/ttyXX */
    }




#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);
#endif




























































































































































    /*
     * 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.

     */
    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(name);



    /*
     * 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);
	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));
	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);







<
<
<
<
<
<
<
<
|

<
<
<
<

<
|
|
|
|
>

|
|
|
|
|
|
|
|
|
|
|

|
|
|

|
|
|
|
>
>
>
>


















|
|

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>













<
|
>

<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
>
|
<
<
|
<
<
>
|
<
<
>
|
<
<
<
<
<
<
<
<
<
<
<
<
|
>
|
>
|
>
>
















|





<
<
<
<
<
<
<
<
<













|

<
<
<







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);
}









static void pty_open_master(void)
{




#ifdef BSD_PTYS

    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);
	    }
	}

    /* 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(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);
    }

    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);
    }

    /*

     * Stamp utmp (that is, tell the utmp helper process to do so),
     * or not.
     */














    if (!cfg.stamp_utmp)


	close(pty_utmp_helper_pipe);   /* just let the child process die */
    else {


	char *location = get_x_display();


	int len = strlen(location)+1, pos = 0;   /* +1 to include NUL */
	while (pos < len) {


	    int ret = write(pty_utmp_helper_pipe, location+pos, len - pos);
	    if (ret < 0) {












		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(pty_name, O_RDWR);
	if (slavefd < 0) {
	    perror("slave pty: open");
	    exit(1);
	}










	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(pty_name, O_WRONLY, 0));
	setpgrp();



	/* 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);