version 1.42.2.3, 2001/03/21 19:46:28 |
version 1.42.2.4, 2001/05/07 21:09:33 |
|
|
#include "canohost.h" |
#include "canohost.h" |
#include "session.h" |
#include "session.h" |
|
|
#ifdef HAVE_LOGIN_CAP |
|
#include <login_cap.h> |
|
#endif |
|
|
|
/* types */ |
/* types */ |
|
|
#define TTYSZ 64 |
#define TTYSZ 64 |
|
|
struct Session { |
struct Session { |
int used; |
int used; |
int self; |
int self; |
int extended; |
|
struct passwd *pw; |
struct passwd *pw; |
pid_t pid; |
pid_t pid; |
/* tty */ |
/* tty */ |
|
|
int single_connection; |
int single_connection; |
/* proto 2 */ |
/* proto 2 */ |
int chanid; |
int chanid; |
|
int is_subsystem; |
}; |
}; |
|
|
/* func */ |
/* func */ |
|
|
void session_set_fds(Session *s, int fdin, int fdout, int fderr); |
void session_set_fds(Session *s, int fdin, int fdout, int fderr); |
void session_pty_cleanup(Session *s); |
void session_pty_cleanup(Session *s); |
void session_proctitle(Session *s); |
void session_proctitle(Session *s); |
void do_exec_pty(Session *s, const char *command, struct passwd * pw); |
void do_exec_pty(Session *s, const char *command); |
void do_exec_no_pty(Session *s, const char *command, struct passwd * pw); |
void do_exec_no_pty(Session *s, const char *command); |
void do_login(Session *s, const char *command); |
void do_login(Session *s, const char *command); |
void do_child(Session *s, const char *command); |
void do_child(Session *s, const char *command); |
|
void do_motd(void); |
|
int check_quietlogin(Session *s, const char *command); |
|
|
|
void do_authenticated1(Authctxt *authctxt); |
|
void do_authenticated2(Authctxt *authctxt); |
|
|
/* import */ |
/* import */ |
extern ServerOptions options; |
extern ServerOptions options; |
extern char *__progname; |
extern char *__progname; |
extern int log_stderr; |
extern int log_stderr; |
extern int debug_flag; |
extern int debug_flag; |
extern u_int utmp_len; |
extern u_int utmp_len; |
|
|
extern int startup_pipe; |
extern int startup_pipe; |
|
extern void destroy_sensitive_data(void); |
|
|
/* Local Xauthority file. */ |
/* Local Xauthority file. */ |
static char *xauthfile; |
static char *xauthfile; |
|
|
static login_cap_t *lc; |
static login_cap_t *lc; |
#endif |
#endif |
|
|
|
void |
|
do_authenticated(Authctxt *authctxt) |
|
{ |
|
/* |
|
* Cancel the alarm we set to limit the time taken for |
|
* authentication. |
|
*/ |
|
alarm(0); |
|
if (startup_pipe != -1) { |
|
close(startup_pipe); |
|
startup_pipe = -1; |
|
} |
|
#ifdef HAVE_LOGIN_CAP |
|
if ((lc = login_getclass(authctxt->pw->pw_class)) == NULL) { |
|
error("unable to get login class"); |
|
return; |
|
} |
|
#ifdef BSD_AUTH |
|
if (auth_approval(NULL, lc, authctxt->pw->pw_name, "ssh") <= 0) { |
|
packet_disconnect("Approval failure for %s", |
|
authctxt->pw->pw_name); |
|
} |
|
#endif |
|
#endif |
|
/* setup the channel layer */ |
|
if (!no_port_forwarding_flag && options.allow_tcp_forwarding) |
|
channel_permit_all_opens(); |
|
|
|
if (compat20) |
|
do_authenticated2(authctxt); |
|
else |
|
do_authenticated1(authctxt); |
|
} |
|
|
/* |
/* |
* Remove local Xauthority file. |
* Remove local Xauthority file. |
*/ |
*/ |
|
|
* are requested, etc. |
* are requested, etc. |
*/ |
*/ |
void |
void |
do_authenticated(struct passwd * pw) |
do_authenticated1(Authctxt *authctxt) |
{ |
{ |
Session *s; |
Session *s; |
int type, fd; |
|
int compression_level = 0, enable_compression_after_reply = 0; |
|
int have_pty = 0; |
|
char *command; |
char *command; |
int n_bytes; |
int success, type, fd, n_bytes, plen, screen_flag, have_pty = 0; |
int plen; |
int compression_level = 0, enable_compression_after_reply = 0; |
u_int proto_len, data_len, dlen; |
u_int proto_len, data_len, dlen; |
int screen_flag; |
|
|
|
/* |
|
* Cancel the alarm we set to limit the time taken for |
|
* authentication. |
|
*/ |
|
alarm(0); |
|
if (startup_pipe != -1) { |
|
close(startup_pipe); |
|
startup_pipe = -1; |
|
} |
|
|
|
s = session_new(); |
s = session_new(); |
s->pw = pw; |
s->pw = authctxt->pw; |
|
|
if (!no_port_forwarding_flag && options.allow_tcp_forwarding) |
|
channel_permit_all_opens(); |
|
|
|
#ifdef HAVE_LOGIN_CAP |
|
if ((lc = login_getclass(pw->pw_class)) == NULL) { |
|
error("unable to get login class"); |
|
return; |
|
} |
|
#endif |
|
|
|
/* |
/* |
* We stay in this loop until the client requests to execute a shell |
* We stay in this loop until the client requests to execute a shell |
* or a command. |
* or a command. |
*/ |
*/ |
for (;;) { |
for (;;) { |
int success = 0; |
success = 0; |
|
|
/* Get a packet from the client. */ |
/* Get a packet from the client. */ |
type = packet_read(&plen); |
type = packet_read(&plen); |
|
|
break; |
break; |
} |
} |
fatal_add_cleanup(pty_cleanup_proc, (void *)s); |
fatal_add_cleanup(pty_cleanup_proc, (void *)s); |
pty_setowner(pw, s->tty); |
pty_setowner(s->pw, s->tty); |
|
|
/* Get TERM from the packet. Note that the value may be of arbitrary length. */ |
/* Get TERM from the packet. Note that the value may be of arbitrary length. */ |
s->term = packet_get_string(&dlen); |
s->term = packet_get_string(&dlen); |
|
|
/* Setup to always have a local .Xauthority. */ |
/* Setup to always have a local .Xauthority. */ |
xauthfile = xmalloc(MAXPATHLEN); |
xauthfile = xmalloc(MAXPATHLEN); |
strlcpy(xauthfile, "/tmp/ssh-XXXXXXXX", MAXPATHLEN); |
strlcpy(xauthfile, "/tmp/ssh-XXXXXXXX", MAXPATHLEN); |
temporarily_use_uid(pw->pw_uid); |
temporarily_use_uid(s->pw); |
if (mkdtemp(xauthfile) == NULL) { |
if (mkdtemp(xauthfile) == NULL) { |
restore_uid(); |
restore_uid(); |
error("private X11 dir: mkdtemp %s failed: %s", |
error("private X11 dir: mkdtemp %s failed: %s", |
|
|
break; |
break; |
} |
} |
debug("Received authentication agent forwarding request."); |
debug("Received authentication agent forwarding request."); |
success = auth_input_request_forwarding(pw); |
success = auth_input_request_forwarding(s->pw); |
break; |
break; |
|
|
case SSH_CMSG_PORT_FORWARD_REQUEST: |
case SSH_CMSG_PORT_FORWARD_REQUEST: |
|
|
break; |
break; |
} |
} |
debug("Received TCP/IP port forwarding request."); |
debug("Received TCP/IP port forwarding request."); |
channel_input_port_forward_request(pw->pw_uid == 0, options.gateway_ports); |
channel_input_port_forward_request(s->pw->pw_uid == 0, options.gateway_ports); |
success = 1; |
success = 1; |
break; |
break; |
|
|
|
|
debug("Forced command '%.500s'", forced_command); |
debug("Forced command '%.500s'", forced_command); |
} |
} |
if (have_pty) |
if (have_pty) |
do_exec_pty(s, command, pw); |
do_exec_pty(s, command); |
else |
else |
do_exec_no_pty(s, command, pw); |
do_exec_no_pty(s, command); |
|
|
if (command != NULL) |
if (command != NULL) |
xfree(command); |
xfree(command); |
|
|
* setting up file descriptors and such. |
* setting up file descriptors and such. |
*/ |
*/ |
void |
void |
do_exec_no_pty(Session *s, const char *command, struct passwd * pw) |
do_exec_no_pty(Session *s, const char *command) |
{ |
{ |
int pid; |
int pid; |
|
|
|
|
close(perr[1]); |
close(perr[1]); |
|
|
if (compat20) { |
if (compat20) { |
session_set_fds(s, pin[1], pout[0], s->extended ? perr[0] : -1); |
session_set_fds(s, pin[1], pout[0], s->is_subsystem ? -1 : perr[0]); |
} else { |
} else { |
/* Enter the interactive session. */ |
/* Enter the interactive session. */ |
server_loop(pid, pin[1], pout[0], perr[0]); |
server_loop(pid, pin[1], pout[0], perr[0]); |
/* server_loop has closed pin[1], pout[1], and perr[1]. */ |
/* server_loop has closed pin[1], pout[0], and perr[0]. */ |
} |
} |
#else /* USE_PIPES */ |
#else /* USE_PIPES */ |
/* We are the parent. Close the child sides of the socket pairs. */ |
/* We are the parent. Close the child sides of the socket pairs. */ |
|
|
* handle the case that fdin and fdout are the same. |
* handle the case that fdin and fdout are the same. |
*/ |
*/ |
if (compat20) { |
if (compat20) { |
session_set_fds(s, inout[1], inout[1], s->extended ? err[1] : -1); |
session_set_fds(s, inout[1], inout[1], s->is_subsystem ? -1 : err[1]); |
} else { |
} else { |
server_loop(pid, inout[1], inout[1], err[1]); |
server_loop(pid, inout[1], inout[1], err[1]); |
/* server_loop has closed inout[1] and err[1]. */ |
/* server_loop has closed inout[1] and err[1]. */ |
|
|
* lastlog, and other such operations. |
* lastlog, and other such operations. |
*/ |
*/ |
void |
void |
do_exec_pty(Session *s, const char *command, struct passwd * pw) |
do_exec_pty(Session *s, const char *command) |
{ |
{ |
int fdout, ptyfd, ttyfd, ptymaster; |
int fdout, ptyfd, ttyfd, ptymaster; |
pid_t pid; |
pid_t pid; |
|
|
} |
} |
} |
} |
|
|
const char * |
|
get_remote_name_or_ip(void) |
|
{ |
|
static const char *remote = ""; |
|
if (utmp_len > 0) |
|
remote = get_canonical_hostname(options.reverse_mapping_check); |
|
if (utmp_len == 0 || strlen(remote) > utmp_len) |
|
remote = get_remote_ipaddr(); |
|
return remote; |
|
} |
|
|
|
/* administrative, login(1)-like work */ |
/* administrative, login(1)-like work */ |
void |
void |
do_login(Session *s, const char *command) |
do_login(Session *s, const char *command) |
{ |
{ |
FILE *f; |
|
char *time_string; |
char *time_string; |
char buf[256]; |
|
char hostname[MAXHOSTNAMELEN]; |
char hostname[MAXHOSTNAMELEN]; |
socklen_t fromlen; |
socklen_t fromlen; |
struct sockaddr_storage from; |
struct sockaddr_storage from; |
struct stat st; |
|
time_t last_login_time; |
time_t last_login_time; |
struct passwd * pw = s->pw; |
struct passwd * pw = s->pw; |
pid_t pid = getpid(); |
pid_t pid = getpid(); |
|
|
} |
} |
|
|
/* Get the time and hostname when the user last logged in. */ |
/* Get the time and hostname when the user last logged in. */ |
hostname[0] = '\0'; |
if (options.print_lastlog) { |
last_login_time = get_last_login_time(pw->pw_uid, pw->pw_name, |
hostname[0] = '\0'; |
hostname, sizeof(hostname)); |
last_login_time = get_last_login_time(pw->pw_uid, pw->pw_name, |
|
hostname, sizeof(hostname)); |
|
} |
|
|
/* Record that there was a login on that tty from the remote host. */ |
/* Record that there was a login on that tty from the remote host. */ |
record_login(pid, s->tty, pw->pw_name, pw->pw_uid, |
record_login(pid, s->tty, pw->pw_name, pw->pw_uid, |
get_remote_name_or_ip(), (struct sockaddr *)&from); |
get_remote_name_or_ip(utmp_len, options.reverse_mapping_check), |
|
(struct sockaddr *)&from); |
|
|
/* Done if .hushlogin exists or a command given. */ |
if (check_quietlogin(s, command)) |
if (command != NULL) |
|
return; |
return; |
snprintf(buf, sizeof(buf), "%.200s/.hushlogin", pw->pw_dir); |
|
#ifdef HAVE_LOGIN_CAP |
if (options.print_lastlog && last_login_time != 0) { |
if (login_getcapbool(lc, "hushlogin", 0) || stat(buf, &st) >= 0) |
|
#else |
|
if (stat(buf, &st) >= 0) |
|
#endif |
|
return; |
|
if (last_login_time != 0) { |
|
time_string = ctime(&last_login_time); |
time_string = ctime(&last_login_time); |
if (strchr(time_string, '\n')) |
if (strchr(time_string, '\n')) |
*strchr(time_string, '\n') = 0; |
*strchr(time_string, '\n') = 0; |
|
|
else |
else |
printf("Last login: %s from %s\r\n", time_string, hostname); |
printf("Last login: %s from %s\r\n", time_string, hostname); |
} |
} |
|
|
|
do_motd(); |
|
} |
|
|
|
/* |
|
* Display the message of the day. |
|
*/ |
|
void |
|
do_motd(void) |
|
{ |
|
FILE *f; |
|
char buf[256]; |
|
|
if (options.print_motd) { |
if (options.print_motd) { |
#ifdef HAVE_LOGIN_CAP |
#ifdef HAVE_LOGIN_CAP |
f = fopen(login_getcapstr(lc, "welcome", "/etc/motd", |
f = fopen(login_getcapstr(lc, "welcome", "/etc/motd", |
|
|
} |
} |
} |
} |
|
|
|
|
/* |
/* |
|
* Check for quiet login, either .hushlogin or command given. |
|
*/ |
|
int |
|
check_quietlogin(Session *s, const char *command) |
|
{ |
|
char buf[256]; |
|
struct passwd * pw = s->pw; |
|
struct stat st; |
|
|
|
/* Return 1 if .hushlogin exists or a command given. */ |
|
if (command != NULL) |
|
return 1; |
|
snprintf(buf, sizeof(buf), "%.200s/.hushlogin", pw->pw_dir); |
|
#ifdef HAVE_LOGIN_CAP |
|
if (login_getcapbool(lc, "hushlogin", 0) || stat(buf, &st) >= 0) |
|
return 1; |
|
#else |
|
if (stat(buf, &st) >= 0) |
|
return 1; |
|
#endif |
|
return 0; |
|
} |
|
|
|
/* |
* Sets the value of the given variable in the environment. If the variable |
* Sets the value of the given variable in the environment. If the variable |
* already exists, its value is overriden. |
* already exists, its value is overriden. |
*/ |
*/ |
|
|
char *argv[10]; |
char *argv[10]; |
int do_xauth = s->auth_proto != NULL && s->auth_data != NULL; |
int do_xauth = s->auth_proto != NULL && s->auth_data != NULL; |
|
|
|
/* remove hostkey from the child's memory */ |
|
destroy_sensitive_data(); |
|
|
/* login(1) is only called if we execute the login shell */ |
/* login(1) is only called if we execute the login shell */ |
if (options.use_login && command != NULL) |
if (options.use_login && command != NULL) |
options.use_login = 0; |
options.use_login = 0; |
|
|
(LOGIN_SETALL & ~LOGIN_SETPATH)) < 0) { |
(LOGIN_SETALL & ~LOGIN_SETPATH)) < 0) { |
perror("unable to set user context"); |
perror("unable to set user context"); |
exit(1); |
exit(1); |
|
|
} |
} |
#else |
#else |
if (setlogin(pw->pw_name) < 0) |
if (setlogin(pw->pw_name) < 0) |
|
|
endgrent(); |
endgrent(); |
|
|
/* Permanently switch to the desired uid. */ |
/* Permanently switch to the desired uid. */ |
permanently_set_uid(pw->pw_uid); |
permanently_set_uid(pw); |
#endif |
#endif |
} |
} |
if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid) |
if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid) |
|
|
} |
} |
/* we have to stash the hostname before we close our socket. */ |
/* we have to stash the hostname before we close our socket. */ |
if (options.use_login) |
if (options.use_login) |
hostname = get_remote_name_or_ip(); |
hostname = get_remote_name_or_ip(utmp_len, |
|
options.reverse_mapping_check); |
/* |
/* |
* Close the connection descriptors; note that this is the child, and |
* Close the connection descriptors; note that this is the child, and |
* the server will still have the socket open, and it is important |
* the server will still have the socket open, and it is important |
|
|
* in this order). |
* in this order). |
*/ |
*/ |
if (!options.use_login) { |
if (!options.use_login) { |
if (stat(_PATH_SSH_USER_RC, &st) >= 0) { |
/* ignore _PATH_SSH_USER_RC for subsystems */ |
|
if (!s->is_subsystem && (stat(_PATH_SSH_USER_RC, &st) >= 0)) { |
if (debug_flag) |
if (debug_flag) |
fprintf(stderr, "Running %s %s\n", _PATH_BSHELL, |
fprintf(stderr, "Running %s %s\n", _PATH_BSHELL, |
_PATH_SSH_USER_RC); |
_PATH_SSH_USER_RC); |
|
|
else |
else |
cp = shell; |
cp = shell; |
} |
} |
|
|
|
/* restore SIGPIPE for child */ |
|
signal(SIGPIPE, SIG_DFL); |
|
|
/* |
/* |
* If we have no command, execute the shell. In this case, the shell |
* If we have no command, execute the shell. In this case, the shell |
* name to be passed in argv[0] is preceded by '-' to indicate that |
* name to be passed in argv[0] is preceded by '-' to indicate that |
|
|
for(i = 0; i < MAX_SESSIONS; i++) { |
for(i = 0; i < MAX_SESSIONS; i++) { |
Session *s = &sessions[i]; |
Session *s = &sessions[i]; |
if (! s->used) { |
if (! s->used) { |
s->pid = 0; |
memset(s, 0, sizeof(*s)); |
s->extended = 0; |
|
s->chanid = -1; |
s->chanid = -1; |
s->ptyfd = -1; |
s->ptyfd = -1; |
s->ttyfd = -1; |
s->ttyfd = -1; |
s->term = NULL; |
|
s->pw = NULL; |
|
s->display = NULL; |
|
s->screen = 0; |
|
s->auth_data = NULL; |
|
s->auth_proto = NULL; |
|
s->used = 1; |
s->used = 1; |
s->pw = NULL; |
|
debug("session_new: session %d", i); |
debug("session_new: session %d", i); |
return s; |
return s; |
} |
} |
|
|
session_pty_req(Session *s) |
session_pty_req(Session *s) |
{ |
{ |
u_int len; |
u_int len; |
char *term_modes; /* encoded terminal modes */ |
int n_bytes; |
|
|
if (no_pty_flag) |
if (no_pty_flag) |
return 0; |
return 0; |
|
|
s->row = packet_get_int(); |
s->row = packet_get_int(); |
s->xpixel = packet_get_int(); |
s->xpixel = packet_get_int(); |
s->ypixel = packet_get_int(); |
s->ypixel = packet_get_int(); |
term_modes = packet_get_string(&len); |
|
packet_done(); |
|
|
|
if (strcmp(s->term, "") == 0) { |
if (strcmp(s->term, "") == 0) { |
xfree(s->term); |
xfree(s->term); |
|
|
s->ptyfd = -1; |
s->ptyfd = -1; |
s->ttyfd = -1; |
s->ttyfd = -1; |
error("session_pty_req: session %d alloc failed", s->self); |
error("session_pty_req: session %d alloc failed", s->self); |
xfree(term_modes); |
|
return 0; |
return 0; |
} |
} |
debug("session_pty_req: session %d alloc %s", s->self, s->tty); |
debug("session_pty_req: session %d alloc %s", s->self, s->tty); |
|
|
/* Get window size from the packet. */ |
/* Get window size from the packet. */ |
pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel); |
pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel); |
|
|
|
/* Get tty modes from the packet. */ |
|
tty_parse_modes(s->ttyfd, &n_bytes); |
|
packet_done(); |
|
|
session_proctitle(s); |
session_proctitle(s); |
|
|
/* XXX parse and set terminal modes */ |
|
xfree(term_modes); |
|
return 1; |
return 1; |
} |
} |
|
|
|
|
for (i = 0; i < options.num_subsystems; i++) { |
for (i = 0; i < options.num_subsystems; i++) { |
if(strcmp(subsys, options.subsystem_name[i]) == 0) { |
if(strcmp(subsys, options.subsystem_name[i]) == 0) { |
debug("subsystem: exec() %s", options.subsystem_command[i]); |
debug("subsystem: exec() %s", options.subsystem_command[i]); |
do_exec_no_pty(s, options.subsystem_command[i], s->pw); |
s->is_subsystem = 1; |
|
do_exec_no_pty(s, options.subsystem_command[i]); |
success = 1; |
success = 1; |
} |
} |
} |
} |
|
|
} |
} |
xauthfile = xmalloc(MAXPATHLEN); |
xauthfile = xmalloc(MAXPATHLEN); |
strlcpy(xauthfile, "/tmp/ssh-XXXXXXXX", MAXPATHLEN); |
strlcpy(xauthfile, "/tmp/ssh-XXXXXXXX", MAXPATHLEN); |
temporarily_use_uid(s->pw->pw_uid); |
temporarily_use_uid(s->pw); |
if (mkdtemp(xauthfile) == NULL) { |
if (mkdtemp(xauthfile) == NULL) { |
restore_uid(); |
restore_uid(); |
error("private X11 dir: mkdtemp %s failed: %s", |
error("private X11 dir: mkdtemp %s failed: %s", |
|
|
/* if forced_command == NULL, the shell is execed */ |
/* if forced_command == NULL, the shell is execed */ |
char *shell = forced_command; |
char *shell = forced_command; |
packet_done(); |
packet_done(); |
s->extended = 1; |
|
if (s->ttyfd == -1) |
if (s->ttyfd == -1) |
do_exec_no_pty(s, shell, s->pw); |
do_exec_no_pty(s, shell); |
else |
else |
do_exec_pty(s, shell, s->pw); |
do_exec_pty(s, shell); |
return 1; |
return 1; |
} |
} |
|
|
|
|
command = forced_command; |
command = forced_command; |
debug("Forced command '%.500s'", forced_command); |
debug("Forced command '%.500s'", forced_command); |
} |
} |
s->extended = 1; |
|
if (s->ttyfd == -1) |
if (s->ttyfd == -1) |
do_exec_no_pty(s, command, s->pw); |
do_exec_no_pty(s, command); |
else |
else |
do_exec_pty(s, command, s->pw); |
do_exec_pty(s, command); |
if (forced_command == NULL) |
if (forced_command == NULL) |
xfree(command); |
xfree(command); |
return 1; |
return 1; |
|
|
s->self, id, rtype, reply); |
s->self, id, rtype, reply); |
|
|
/* |
/* |
* a session is in LARVAL state until a shell |
* a session is in LARVAL state until a shell, a command |
* or programm is executed |
* or a subsystem is executed |
*/ |
*/ |
if (c->type == SSH_CHANNEL_LARVAL) { |
if (c->type == SSH_CHANNEL_LARVAL) { |
if (strcmp(rtype, "shell") == 0) { |
if (strcmp(rtype, "shell") == 0) { |
|
|
void |
void |
do_authenticated2(Authctxt *authctxt) |
do_authenticated2(Authctxt *authctxt) |
{ |
{ |
/* |
|
* Cancel the alarm we set to limit the time taken for |
|
* authentication. |
|
*/ |
|
alarm(0); |
|
if (startup_pipe != -1) { |
|
close(startup_pipe); |
|
startup_pipe = -1; |
|
} |
|
if (!no_port_forwarding_flag && options.allow_tcp_forwarding) |
|
channel_permit_all_opens(); |
|
#ifdef HAVE_LOGIN_CAP |
|
if ((lc = login_getclass(authctxt->pw->pw_class)) == NULL) { |
|
error("unable to get login class"); |
|
return; |
|
} |
|
#endif |
|
server_loop2(); |
server_loop2(); |
if (xauthfile) |
if (xauthfile) |
xauthfile_cleanup_proc(NULL); |
xauthfile_cleanup_proc(NULL); |