diff options
Diffstat (limited to 'usr/src/cmd/ssh/sshd')
37 files changed, 0 insertions, 19180 deletions
diff --git a/usr/src/cmd/ssh/sshd/Makefile b/usr/src/cmd/ssh/sshd/Makefile deleted file mode 100644 index 5c4d296bf3..0000000000 --- a/usr/src/cmd/ssh/sshd/Makefile +++ /dev/null @@ -1,119 +0,0 @@ -# -# CDDL HEADER START -# -# The contents of this file are subject to the terms of the -# Common Development and Distribution License (the "License"). -# You may not use this file except in compliance with the License. -# -# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE -# or http://www.opensolaris.org/os/licensing. -# See the License for the specific language governing permissions -# and limitations under the License. -# -# When distributing Covered Code, include this CDDL HEADER in each -# file and include the License file at usr/src/OPENSOLARIS.LICENSE. -# If applicable, add the following below this CDDL HEADER, with the -# fields enclosed by brackets "[]" replaced with your own identifying -# information: Portions Copyright [yyyy] [name of copyright owner] -# -# CDDL HEADER END -# -# Copyright 2009 Sun Microsystems, Inc. All rights reserved. -# Use is subject to license terms. -# -# cmd/ssh/sshd/Makefile - -PROG= sshd - -DIRS= $(ROOTLIBSSH) $(ROOTLIBSUNSSH) - -OBJS = sshd.o \ - altprivsep.o \ - auth.o \ - auth1.o \ - auth2.o \ - auth-options.o \ - auth2-chall.o \ - auth2-gss.o \ - auth2-hostbased.o \ - auth2-kbdint.o \ - auth2-none.o \ - auth2-passwd.o \ - auth2-pam.o \ - auth2-pubkey.o \ - auth-bsdauth.o \ - auth-chall.o \ - auth-rhosts.o \ - auth-krb4.o \ - auth-krb5.o \ - auth-pam.o \ - auth-passwd.o \ - auth-rsa.o \ - auth-rh-rsa.o \ - auth-sia.o \ - auth-skey.o \ - bsmaudit.o \ - groupaccess.o \ - gss-serv.o \ - loginrec.o \ - servconf.o \ - serverloop.o \ - session.o \ - sshlogin.o \ - sshpty.o - -EXTOBJS = sftp-server.o - -SRCS = $(OBJS:.o=.c) ../sftp-server/sftp-server.c - -include ../../Makefile.cmd -include ../Makefile.ssh-common - -LDLIBS += $(SSH_COMMON_LDLIBS) -lsocket \ - -lnsl \ - -lz \ - -lpam \ - -lbsm \ - -lwrap \ - -lcrypto \ - -lgss \ - -lcontract -MAPFILES = $(MAPFILE.INT) $(MAPFILE.NGB) -LDFLAGS += $(MAPFILES:%=-M%) - -POFILE_DIR= .. - -.KEEP_STATE: - -.PARALLEL: $(OBJS) - -all: $(PROG) - -$(PROG): $(OBJS) $(EXTOBJS) $(MAPFILES) ../libssh/$(MACH)/libssh.a \ - ../libopenbsd-compat/$(MACH)/libopenbsd-compat.a - $(LINK.c) $(OBJS) $(EXTOBJS) -o $@ $(LDLIBS) $(DYNFLAGS) - $(POST_PROCESS) - -%.o : ../sftp-server/%.c - $(COMPILE.c) -o $@ $< - $(POST_PROCESS_O) - -install: all $(DIRS) $(ROOTLIBSSHPROG) $(ROOTLIBSSH) $(ROOTLIBSUNSSH) - - -$(ROOTLIBSSHPROG)/%: % - $(INS.file) - -$(ROOTLIBSUNSSHPROG)/%: % - $(INS.file) - -$(DIRS): - $(INS.dir) - -clean: - $(RM) $(OBJS) $(EXTOBJS) - -lint: lint_SRCS - -include ../Makefile.msg.targ -include ../../Makefile.targ diff --git a/usr/src/cmd/ssh/sshd/altprivsep.c b/usr/src/cmd/ssh/sshd/altprivsep.c deleted file mode 100644 index 6f954feab5..0000000000 --- a/usr/src/cmd/ssh/sshd/altprivsep.c +++ /dev/null @@ -1,1187 +0,0 @@ -/* - * CDDL HEADER START - * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License (the "License"). - * You may not use this file except in compliance with the License. - * - * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE - * or http://www.opensolaris.org/os/licensing. - * See the License for the specific language governing permissions - * and limitations under the License. - * - * When distributing Covered Code, include this CDDL HEADER in each - * file and include the License file at usr/src/OPENSOLARIS.LICENSE. - * If applicable, add the following below this CDDL HEADER, with the - * fields enclosed by brackets "[]" replaced with your own identifying - * information: Portions Copyright [yyyy] [name of copyright owner] - * - * CDDL HEADER END - * - * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. - */ - -#include <fcntl.h> -#include <sys/types.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <sys/socket.h> - -#include <pwd.h> - -#include "includes.h" -#include "atomicio.h" -#include "auth.h" -#include "bufaux.h" -#include "buffer.h" -#include "cipher.h" -#include "compat.h" -#include "dispatch.h" -#include "getput.h" -#include "kex.h" -#include "log.h" -#include "mac.h" -#include "packet.h" -#include "uidswap.h" -#include "ssh2.h" -#include "sshlogin.h" -#include "xmalloc.h" -#include "altprivsep.h" -#include "canohost.h" -#include "engine.h" -#include "servconf.h" - -#ifdef HAVE_BSM -#include "bsmaudit.h" -adt_session_data_t *ah = NULL; -#endif /* HAVE_BSM */ - -#ifdef GSSAPI -#include "ssh-gss.h" -extern Gssctxt *xxx_gssctxt; -#endif /* GSSAPI */ - -extern Kex *xxx_kex; -extern u_char *session_id2; -extern int session_id2_len; - -static Buffer to_monitor; -static Buffer from_monitor; - -/* - * Sun's Alternative Privilege Separation basics: - * - * Abstract - * -------- - * - * sshd(1M) fork()s and drops privs in the child while retaining privs - * in the parent (a.k.a., the monitor). The unprivileged sshd and the - * monitor talk over a pipe using a simple protocol. - * - * The monitor protocol is all about having the monitor carry out the - * only operations that require privileges OR access to privileged - * resources. These are: utmpx/wtmpx record keeping, auditing, and - * SSHv2 re-keying. - * - * Re-Keying - * --------- - * - * Re-keying is the only protocol version specific aspect of sshd in - * which the monitor gets involved. - * - * The monitor processes all SSHv2 re-key protocol packets, but the - * unprivileged sshd process does the transport layer crypto for those - * packets. - * - * The monitor and its unprivileged sshd child process treat - * SSH_MSG_NEWKEYS SSH2 messages specially: a) the monitor does not call - * set_newkeys(), but b) the child asks the monitor for the set of - * negotiated algorithms, key, IV and what not for the relevant - * transport direction and then calls set_newkeys(). - * - * Monitor Protocol - * ---------------- - * - * Monitor IPC message formats are similar to SSHv2 messages, minus - * compression, encryption, padding and MACs: - * - * - 4 octet message length - * - message data - * - 1 octet message type - * - message data - * - * In broad strokes: - * - * - IPC: pipe, exit(2)/wait4(2) - * - * - threads: the monitor and child are single-threaded - * - * - monitor main loop: a variant of server_loop2(), for re-keying only - * - unpriv child main loop: server_loop2(), as usual - * - * - protocol: - * - key exchange packets are always forwarded as is to the monitor - * - newkeys, record_login(), record_logout() are special packets - * using the packet type range reserved for local extensions - * - * - the child drops privs and runs like a normal sshd, except that it - * sets dispatch handlers for key exchange packets that forward the - * packets to the monitor - * - * Event loops: - * - * - all monitor protocols are synchronous: because the SSHv2 rekey - * protocols are synchronous and because the other monitor operations - * are synchronous (or have no replies), - * - * - server_loop2() is modified to check the monitor pipe for rekey - * packets to forward to the client - * - * - and dispatch handlers are set, upon receipt of KEXINIT (and reset - * when NEWKEYS is sent out) to forward incoming rekey packets to the - * monitor. - * - * - the monitor runs an event loop not unlike server_loop2() and runs - * key exchanges almost exactly as a pre-altprivsep sshd would - * - * - unpriv sshd exit -> monitor cleanup (including audit logout) and exit - * - * - fatal() in monitor -> forcibly shutdown() socket and kill/wait for - * child (so that the audit event for the logout better reflects - * reality -- i.e., logged out means logged out, but for bg jobs) - * - * Message formats: - * - * - key exchange packets/replies forwarded "as is" - * - * - all other monitor requests are sent as SSH2_PRIV_MSG_ALTPRIVSEP and have a - * sub-type identifier (one octet) - * - private request sub-types include: - * - get new shared secret from last re-key - * - record login (utmpx/wtmpx), request data contains three arguments: - * pid, ttyname, program name - * - record logout (utmpx/wtmpx), request data contains one argument: pid - * - * Reply sub-types include: - * - * - NOP (for record_login/logout) - * - new shared secret from last re-key - */ - -static int aps_started = 0; -static int is_monitor = 0; - -static pid_t monitor_pid, child_pid; -static int pipe_fds[2]; -static int pipe_fd = -1; -static Buffer input_pipe, output_pipe; /* for pipe I/O */ - -static Authctxt *xxx_authctxt; - -/* Monitor functions */ -extern void aps_monitor_loop(Authctxt *authctxt, pid_t child_pid); -static void aps_record_login(void); -static void aps_record_logout(void); -static void aps_start_rekex(void); -Authctxt *aps_read_auth_context(void); - -/* main functions for handling the monitor */ -static pid_t altprivsep_start_monitor(Authctxt **authctxt); -static void altprivsep_do_monitor(Authctxt *authctxt, pid_t child_pid); -static int altprivsep_started(void); -static int altprivsep_is_monitor(void); - -/* calls _to_ monitor from unprivileged process */ -static void altprivsep_get_newkeys(enum kex_modes mode); - -/* monitor-side fatal_cleanup callbacks */ -static void altprivsep_shutdown_sock(void *arg); - -/* Altprivsep packet utilities for communication with the monitor */ -static void altprivsep_packet_start(u_char); -static int altprivsep_packet_send(void); -static int altprivsep_fwd_packet(u_char type); - -static int altprivsep_packet_read(void); -static void altprivsep_packet_read_expect(int type); - -static void altprivsep_packet_put_char(int ch); -static void altprivsep_packet_put_int(u_int value); -static void altprivsep_packet_put_cstring(const char *str); -static void altprivsep_packet_put_raw(const void *buf, u_int len); - -static u_int altprivsep_packet_get_char(void); -static void *altprivsep_packet_get_raw(u_int *length_ptr); -static void *altprivsep_packet_get_string(u_int *length_ptr); - -Kex *prepare_for_ssh2_kex(void); - -/* - * Start monitor from privileged sshd process. - * - * Return values are like fork(2); the parent is the monitor. The caller should - * fatal() on error. - * - * Note that the monitor waits until the still privileged child finishes the - * authentication. The child drops its privileges after the authentication. - */ -static pid_t -altprivsep_start_monitor(Authctxt **authctxt) -{ - pid_t pid; - int junk; - - if (aps_started) - fatal("Monitor startup failed: missing state"); - - buffer_init(&output_pipe); - buffer_init(&input_pipe); - - if (pipe(pipe_fds) != 0) { - error("Monitor startup failure: could not create pipes: %s", - strerror(errno)); - return (-1); - } - - (void) fcntl(pipe_fds[0], F_SETFD, FD_CLOEXEC); - (void) fcntl(pipe_fds[1], F_SETFD, FD_CLOEXEC); - - monitor_pid = getpid(); - - if ((pid = fork()) > 0) { - /* - * From now on, all debug messages from monitor will have prefix - * "monitor " - */ - set_log_txt_prefix("monitor "); - (void) prepare_for_ssh2_kex(); - packet_set_server(); - /* parent */ - child_pid = pid; - - debug2("Monitor pid %ld, unprivileged child pid %ld", - monitor_pid, child_pid); - - (void) close(pipe_fds[1]); - pipe_fd = pipe_fds[0]; - - /* - * Signal readiness of the monitor and then read the - * authentication context from the child. - */ - (void) write(pipe_fd, &pid, sizeof (pid)); - packet_set_monitor(pipe_fd); - xxx_authctxt = *authctxt = aps_read_auth_context(); - - if (fcntl(pipe_fd, F_SETFL, O_NONBLOCK) < 0) - error("fcntl O_NONBLOCK: %.100s", strerror(errno)); - - aps_started = 1; - is_monitor = 1; - - debug2("Monitor started"); - - return (pid); - } - - if (pid < 0) { - debug2("Monitor startup failure: could not fork unprivileged" - " process: %s", strerror(errno)); - return (pid); - } - - /* this is the child that will later drop privileges */ - - /* note that Solaris has bi-directional pipes so one pipe is enough */ - (void) close(pipe_fds[0]); - pipe_fd = pipe_fds[1]; - - /* wait for monitor to be ready */ - debug2("Waiting for monitor"); - (void) read(pipe_fd, &junk, sizeof (junk)); - debug2("Monitor signalled readiness"); - - buffer_init(&to_monitor); - buffer_init(&from_monitor); - - /* AltPrivSep interfaces are set up */ - aps_started = 1; - return (pid); -} - -int -altprivsep_get_pipe_fd(void) -{ - return (pipe_fd); -} - -/* - * This function is used in the unprivileged child for all packets in the range - * between SSH2_MSG_KEXINIT and SSH2_MSG_TRANSPORT_MAX. - */ -void -altprivsep_rekey(int type, u_int32_t seq, void *ctxt) -{ - Kex *kex = (Kex *)ctxt; - - if (kex == NULL) - fatal("Missing key exchange context in unprivileged process"); - - if (type != SSH2_MSG_NEWKEYS) { - debug2("Forwarding re-key packet (%d) to monitor", type); - if (!altprivsep_fwd_packet(type)) - fatal("altprivsep_rekey: Monitor not responding"); - } - - /* tell server_loop2() that we're re-keying */ - kex->done = 0; - - /* NEWKEYS is special: get the new keys for client->server direction */ - if (type == SSH2_MSG_NEWKEYS) { - debug2("received SSH2_MSG_NEWKEYS packet - " - "getting new inbound keys from the monitor"); - altprivsep_get_newkeys(MODE_IN); - kex->done = 1; - } -} - -void -altprivsep_process_input(fd_set *rset) -{ - void *data; - int type; - u_int dlen; - - if (pipe_fd == -1) - return; - - if (!FD_ISSET(pipe_fd, rset)) - return; - - debug2("reading from pipe to monitor (%d)", pipe_fd); - if ((type = altprivsep_packet_read()) == -1) - fatal("altprivsep_process_input: Monitor not responding"); - - if (!compat20) - return; /* shouldn't happen! but be safe */ - - if (type == 0) - return; /* EOF -- nothing to do here */ - - if (type >= SSH2_MSG_MAX) - fatal("Received garbage from monitor"); - - debug2("Read packet type %d from pipe to monitor", (u_int)type); - - if (type == SSH2_PRIV_MSG_ALTPRIVSEP) - return; /* shouldn't happen! */ - - /* NEWKEYS is special: get the new keys for server->client direction */ - if (type == SSH2_MSG_NEWKEYS) { - debug2("forwarding SSH2_MSG_NEWKEYS packet we got from monitor to " - "the client"); - packet_start(SSH2_MSG_NEWKEYS); - packet_send(); - debug2("getting new outbound keys from the monitor"); - altprivsep_get_newkeys(MODE_OUT); - return; - } - - data = altprivsep_packet_get_raw(&dlen); - - packet_start((u_char)type); - - if (data != NULL && dlen > 0) - packet_put_raw(data, dlen); - - packet_send(); -} - -static void -altprivsep_do_monitor(Authctxt *authctxt, pid_t child_pid) -{ - aps_monitor_loop(authctxt, child_pid); -} - -static int -altprivsep_started(void) -{ - return (aps_started); -} - -static int -altprivsep_is_monitor(void) -{ - return (is_monitor); -} - -/* - * A fatal cleanup function to forcibly shutdown the connection socket - */ -static void -altprivsep_shutdown_sock(void *arg) -{ - int sock; - - if (arg == NULL) - return; - - sock = *(int *)arg; - - (void) shutdown(sock, SHUT_RDWR); -} - -/* Calls _to_ monitor from unprivileged process */ -static int -altprivsep_fwd_packet(u_char type) -{ - u_int len; - void *data; - - altprivsep_packet_start(type); - data = packet_get_raw(&len); - altprivsep_packet_put_raw(data, len); - - /* packet_send()s any replies from the monitor to the client */ - return (altprivsep_packet_send()); -} - -extern Newkeys *current_keys[MODE_MAX]; - -/* To be called from packet.c:set_newkeys() before referencing current_keys */ -static void -altprivsep_get_newkeys(enum kex_modes mode) -{ - Newkeys *newkeys; - Comp *comp; - Enc *enc; - Mac *mac; - u_int len; - - if (!altprivsep_started()) - return; - - if (altprivsep_is_monitor()) - return; /* shouldn't happen */ - - /* request new keys */ - altprivsep_packet_start(SSH2_PRIV_MSG_ALTPRIVSEP); - altprivsep_packet_put_char(APS_MSG_NEWKEYS_REQ); - altprivsep_packet_put_int((u_int)mode); - altprivsep_packet_send(); - altprivsep_packet_read_expect(SSH2_PRIV_MSG_ALTPRIVSEP); - if (altprivsep_packet_get_char() != APS_MSG_NEWKEYS_REP) - fatal("Received garbage from monitor during re-keying"); - - newkeys = xmalloc(sizeof (*newkeys)); - memset(newkeys, 0, sizeof (*newkeys)); - - enc = &newkeys->enc; - mac = &newkeys->mac; - comp = &newkeys->comp; - - /* Cipher name, key, IV */ - enc->name = altprivsep_packet_get_string(NULL); - if ((enc->cipher = cipher_by_name(enc->name)) == NULL) - fatal("Monitor negotiated an unknown cipher during re-key"); - - enc->key = altprivsep_packet_get_string(&enc->key_len); - enc->iv = altprivsep_packet_get_string(&enc->block_size); - - /* MAC name */ - mac->name = altprivsep_packet_get_string(NULL); - if (mac_setup(mac, mac->name) < 0) - fatal("Monitor negotiated an unknown MAC algorithm " - "during re-key"); - - mac->key = altprivsep_packet_get_string(&len); - if (len > mac->key_len) - fatal("%s: bad mac key length: %d > %d", __func__, len, - mac->key_len); - - /* Compression algorithm name */ - comp->name = altprivsep_packet_get_string(NULL); - if (strcmp(comp->name, "zlib") != 0 && strcmp(comp->name, "none") != 0) - fatal("Monitor negotiated an unknown compression " - "algorithm during re-key"); - - comp->type = 0; - comp->enabled = 0; /* forces compression re-init, as per-spec */ - if (strcmp(comp->name, "zlib") == 0) - comp->type = 1; - - /* - * Now install new keys - * - * For now abuse kex.c/packet.c non-interfaces. Someday, when - * the many internal interfaces are parametrized, made reentrant - * and thread-safe, made more consistent, and when necessary-but- - * currently-missing interfaces are added then this bit of - * ugliness can be revisited. - * - * The ugliness is in the set_newkeys(), its name and the lack - * of a (Newkeys *) parameter, which forces us to pass the - * newkeys through current_keys[mode]. But this saves us some - * lines of code for now, though not comments. - * - * Also, we've abused, in the code above, knowledge of what - * set_newkeys() expects the current_keys[mode] to contain. - */ - current_keys[mode] = newkeys; - set_newkeys(mode); - -} - -void -altprivsep_record_login(pid_t pid, const char *ttyname) -{ - altprivsep_packet_start(SSH2_PRIV_MSG_ALTPRIVSEP); - altprivsep_packet_put_char(APS_MSG_RECORD_LOGIN); - altprivsep_packet_put_int(pid); - altprivsep_packet_put_cstring(ttyname); - altprivsep_packet_send(); - altprivsep_packet_read_expect(SSH2_PRIV_MSG_ALTPRIVSEP); -} - -void -altprivsep_record_logout(pid_t pid) -{ - altprivsep_packet_start(SSH2_PRIV_MSG_ALTPRIVSEP); - altprivsep_packet_put_char(APS_MSG_RECORD_LOGOUT); - altprivsep_packet_put_int(pid); - altprivsep_packet_send(); - altprivsep_packet_read_expect(SSH2_PRIV_MSG_ALTPRIVSEP); -} - -void -altprivsep_start_rekex(void) -{ - altprivsep_packet_start(SSH2_PRIV_MSG_ALTPRIVSEP); - altprivsep_packet_put_char(APS_MSG_START_REKEX); - altprivsep_packet_send(); - altprivsep_packet_read_expect(SSH2_PRIV_MSG_ALTPRIVSEP); -} - -/* - * The monitor needs some information that its child learns during the - * authentication process. Since the child was forked before the key exchange - * and authentication started it must send some context to the monitor after the - * authentication is finished. Less obvious part - monitor needs the session ID - * since it is used in the key generation process after the key (re-)exchange is - * finished. - */ -void -altprivsep_send_auth_context(Authctxt *authctxt) -{ - debug("sending auth context to the monitor"); - altprivsep_packet_start(SSH2_PRIV_MSG_ALTPRIVSEP); - altprivsep_packet_put_char(APS_MSG_AUTH_CONTEXT); - altprivsep_packet_put_int(authctxt->pw->pw_uid); - altprivsep_packet_put_int(authctxt->pw->pw_gid); - altprivsep_packet_put_cstring(authctxt->pw->pw_name); - altprivsep_packet_put_raw(session_id2, session_id2_len); - debug("will send %d bytes of auth context to the monitor", - buffer_len(&to_monitor)); - altprivsep_packet_send(); - altprivsep_packet_read_expect(SSH2_PRIV_MSG_ALTPRIVSEP); -} - -static void aps_send_newkeys(void); - -/* Monitor side dispatch handler for SSH2_PRIV_MSG_ALTPRIVSEP */ -/* ARGSUSED */ -void -aps_input_altpriv_msg(int type, u_int32_t seq, void *ctxt) -{ - u_char req_type; - - req_type = packet_get_char(); - - switch (req_type) { - case APS_MSG_NEWKEYS_REQ: - aps_send_newkeys(); - break; - case APS_MSG_RECORD_LOGIN: - aps_record_login(); - break; - case APS_MSG_RECORD_LOGOUT: - aps_record_logout(); - break; - case APS_MSG_START_REKEX: - aps_start_rekex(); - break; - default: - break; - } -} - -/* Monitor-side handlers for APS_MSG_* */ -static -void -aps_send_newkeys(void) -{ - Newkeys *newkeys; - Enc *enc; - Mac *mac; - Comp *comp; - enum kex_modes mode; - - /* get direction for which newkeys are wanted */ - mode = (enum kex_modes) packet_get_int(); - packet_check_eom(); - - /* get those newkeys */ - newkeys = kex_get_newkeys(mode); - enc = &newkeys->enc; - mac = &newkeys->mac; - comp = &newkeys->comp; - - /* - * Negotiated algorithms, client->server and server->client, for - * cipher, mac and compression. - */ - packet_start(SSH2_PRIV_MSG_ALTPRIVSEP); - packet_put_char(APS_MSG_NEWKEYS_REP); - packet_put_cstring(enc->name); - packet_put_string(enc->key, enc->key_len); - packet_put_string(enc->iv, enc->block_size); - packet_put_cstring(mac->name); - packet_put_string(mac->key, mac->key_len); - packet_put_cstring(comp->name); - - packet_send(); - free_keys(newkeys); -} - -struct _aps_login_rec { - pid_t lr_pid; - char *lr_tty; - struct _aps_login_rec *next; -}; - -typedef struct _aps_login_rec aps_login_rec; - -static aps_login_rec *aps_login_list = NULL; - -static -void -aps_record_login(void) -{ - aps_login_rec *new_rec; - struct stat sbuf; - size_t proc_path_len; - char *proc_path; - - new_rec = xmalloc(sizeof (aps_login_rec)); - memset(new_rec, 0, sizeof (aps_login_rec)); - - new_rec->lr_pid = packet_get_int(); - new_rec->lr_tty = packet_get_string(NULL); - - proc_path_len = snprintf(NULL, 0, "/proc/%d", new_rec->lr_pid); - proc_path = xmalloc(proc_path_len + 1); - (void) snprintf(proc_path, proc_path_len + 1, "/proc/%d", - new_rec->lr_pid); - - if (stat(proc_path, &sbuf) || - sbuf.st_uid != xxx_authctxt->pw->pw_uid || - stat(new_rec->lr_tty, &sbuf) < 0 || - sbuf.st_uid != xxx_authctxt->pw->pw_uid) { - debug2("Spurious record_login request from unprivileged sshd"); - xfree(proc_path); - xfree(new_rec->lr_tty); - xfree(new_rec); - return; - } - - /* Insert new record on list */ - new_rec->next = aps_login_list; - aps_login_list = new_rec; - - record_login(new_rec->lr_pid, new_rec->lr_tty, NULL, - xxx_authctxt->user); - - packet_start(SSH2_PRIV_MSG_ALTPRIVSEP); - packet_send(); - - xfree(proc_path); -} - -static -void -aps_record_logout(void) -{ - aps_login_rec **p, *q; - pid_t pid; - - pid = packet_get_int(); - packet_check_eom(); - - for (p = &aps_login_list; *p != NULL; p = &q->next) { - q = *p; - if (q->lr_pid == pid) { - record_logout(q->lr_pid, q->lr_tty, NULL, - xxx_authctxt->user); - - /* dequeue */ - *p = q->next; - xfree(q->lr_tty); - xfree(q); - break; - } - } - - packet_start(SSH2_PRIV_MSG_ALTPRIVSEP); - packet_send(); -} - -static -void -aps_start_rekex(void) -{ - /* - * Send confirmation. We could implement it without that but it doesn't - * bring any harm to do that and we are consistent with other subtypes - * of our private SSH2_PRIV_MSG_ALTPRIVSEP message type. - */ - packet_start(SSH2_PRIV_MSG_ALTPRIVSEP); - packet_send(); - - /* - * KEX_INIT message could be the one that reached the limit. In that - * case, it was already forwarded to us from the unnprivileged child, - * and maybe even acted upon. Obviously we must not send another - * KEX_INIT message. - */ - if (!(xxx_kex->flags & KEX_INIT_SENT)) - kex_send_kexinit(xxx_kex); - else - debug2("rekeying already in progress"); -} - -/* - * This is the monitor side of altprivsep_send_auth_context(). - */ -Authctxt * -aps_read_auth_context(void) -{ - unsigned char *tmp; - Authctxt *authctxt; - - /* - * After the successful authentication we get the context. Getting - * end-of-file means that authentication failed and we can exit as well. - */ - debug("reading the context from the child"); - packet_read_expect(SSH2_PRIV_MSG_ALTPRIVSEP); - debug3("got SSH2_PRIV_MSG_ALTPRIVSEP"); - if (packet_get_char() != APS_MSG_AUTH_CONTEXT) { - fatal("APS_MSG_AUTH_CONTEXT message subtype expected."); - } - - authctxt = xcalloc(1, sizeof(Authctxt)); - authctxt->pw = xcalloc(1, sizeof(struct passwd)); - - /* uid_t and gid_t are integers (UNIX spec) */ - authctxt->pw->pw_uid = packet_get_int(); - authctxt->pw->pw_gid = packet_get_int(); - authctxt->pw->pw_name = packet_get_string(NULL); - authctxt->user = xstrdup(authctxt->pw->pw_name); - debug3("uid/gid/username %d/%d/%s", authctxt->pw->pw_uid, - authctxt->pw->pw_gid, authctxt->user); - session_id2 = (unsigned char *)packet_get_raw((unsigned int*)&session_id2_len); - - /* we don't have this for SSH1. In that case, session_id2_len is 0. */ - if (session_id2_len > 0) { - tmp = (unsigned char *)xmalloc(session_id2_len); - memcpy(tmp, session_id2, session_id2_len); - session_id2 = tmp; - debug3("read session ID (%d B)", session_id2_len); - xxx_kex->session_id = tmp; - xxx_kex->session_id_len = session_id2_len; - } - debug("finished reading the context"); - - /* send confirmation */ - packet_start(SSH2_PRIV_MSG_ALTPRIVSEP); - packet_send(); - - return (authctxt); -} - - -/* Utilities for communication with the monitor */ -static void -altprivsep_packet_start(u_char type) -{ - buffer_clear(&to_monitor); - buffer_put_char(&to_monitor, type); -} - -static void -altprivsep_packet_put_char(int ch) -{ - buffer_put_char(&to_monitor, ch); -} - -static void -altprivsep_packet_put_int(u_int value) -{ - buffer_put_int(&to_monitor, value); -} - -static void -altprivsep_packet_put_cstring(const char *str) -{ - buffer_put_cstring(&to_monitor, str); -} - -static void -altprivsep_packet_put_raw(const void *buf, u_int len) -{ - buffer_append(&to_monitor, buf, len); -} - -/* - * Send a monitor packet to the monitor. This function is blocking. - * - * Returns -1 if the monitor pipe has been closed earlier, fatal()s if - * there's any other problems. - */ -static int -altprivsep_packet_send(void) -{ - ssize_t len; - u_int32_t plen; /* packet length */ - u_char plen_buf[sizeof (plen)]; - u_char padlen; /* padding length */ - fd_set *setp; - int err; - - if (pipe_fd == -1) - return (-1); - - if ((plen = buffer_len(&to_monitor)) == 0) - return (0); - - /* - * We talk the SSHv2 binary packet protocol to the monitor, - * using the none cipher, mac and compression algorithms. - * - * But, interestingly, the none cipher has a block size of 8 - * bytes, thus we must pad the packet. - * - * Also, encryption includes the packet length, so the padding - * must account for that field. I.e., (sizeof (packet length) + - * sizeof (padding length) + packet length + padding length) % - * block_size must == 0. - * - * Also, there must be at least four (4) bytes of padding. - */ - padlen = (8 - ((plen + sizeof (plen) + sizeof (padlen)) % 8)) % 8; - if (padlen < 4) - padlen += 8; - - /* packet length counts padding and padding length field */ - plen += padlen + sizeof (padlen); - - PUT_32BIT(plen_buf, plen); - - setp = xmalloc(howmany(pipe_fd + 1, NFDBITS) * sizeof (fd_mask)); - memset(setp, 0, howmany(pipe_fd + 1, NFDBITS) * sizeof (fd_mask)); - FD_SET(pipe_fd, setp); - - while (select(pipe_fd + 1, NULL, setp, NULL, NULL) == -1) { - if (errno == EAGAIN || errno == EINTR) - continue; - else - goto pipe_gone; - } - - xfree(setp); - - /* packet length field */ - len = atomicio(write, pipe_fd, plen_buf, sizeof (plen)); - - if (len != sizeof (plen)) - goto pipe_gone; - - /* padding length field */ - len = atomicio(write, pipe_fd, &padlen, sizeof (padlen)); - - if (len != sizeof (padlen)) - goto pipe_gone; - - len = atomicio(write, pipe_fd, buffer_ptr(&to_monitor), plen - 1); - - if (len != (plen - 1)) - goto pipe_gone; - - buffer_clear(&to_monitor); - - return (1); - -pipe_gone: - - err = errno; - - (void) close(pipe_fd); - - pipe_fd = -1; - - fatal("altprvsep_packet_send: Monitor not responding: %.100s", - strerror(err)); - - /* NOTREACHED */ - return (0); -} - -/* - * Read a monitor packet from the monitor. This function is blocking. - */ -static int -altprivsep_packet_read(void) -{ - ssize_t len = -1; - u_int32_t plen; - u_char plen_buf[sizeof (plen)]; - u_char padlen; - fd_set *setp; - int err; - - if (pipe_fd == -1) - return (-1); - - setp = xmalloc(howmany(pipe_fd + 1, NFDBITS) * sizeof (fd_mask)); - memset(setp, 0, howmany(pipe_fd + 1, NFDBITS) * sizeof (fd_mask)); - FD_SET(pipe_fd, setp); - - while (select(pipe_fd + 1, setp, NULL, NULL, NULL) == -1) { - if (errno == EAGAIN || errno == EINTR) - continue; - else - goto pipe_gone; - } - - xfree(setp); - - /* packet length field */ - len = atomicio(read, pipe_fd, plen_buf, sizeof (plen)); - - plen = GET_32BIT(plen_buf); - - if (len != sizeof (plen)) - goto pipe_gone; - - /* padding length field */ - len = atomicio(read, pipe_fd, &padlen, sizeof (padlen)); - - if (len != sizeof (padlen)) - goto pipe_gone; - - plen -= sizeof (padlen); - - buffer_clear(&from_monitor); - buffer_append_space(&from_monitor, plen); - - /* packet data + padding */ - len = atomicio(read, pipe_fd, buffer_ptr(&from_monitor), plen); - - if (len != plen) - goto pipe_gone; - - /* remove padding */ - if (padlen > 0) - buffer_consume_end(&from_monitor, padlen); - - /* packet type */ - return (buffer_get_char(&from_monitor)); - -pipe_gone: - - err = errno; - - (void) close(pipe_fd); - - pipe_fd = -1; - - if (len < 0) - fatal("altpriv_packet_read: Monitor not responding %.100s", - strerror(err)); - - debug2("Monitor pipe closed by monitor"); - return (0); -} - -static void -altprivsep_packet_read_expect(int expected) -{ - int type; - - type = altprivsep_packet_read(); - - if (type <= 0) - fatal("altprivsep_packet_read_expect: Monitor not responding"); - - if (type != expected) - fatal("Protocol error in privilege separation; expected " - "packet type %d, got %d", expected, type); -} - -static u_int -altprivsep_packet_get_char(void) -{ - return (buffer_get_char(&from_monitor)); -} -void -*altprivsep_packet_get_raw(u_int *length_ptr) -{ - if (length_ptr != NULL) - *length_ptr = buffer_len(&from_monitor); - - return (buffer_ptr(&from_monitor)); -} -void -*altprivsep_packet_get_string(u_int *length_ptr) -{ - return (buffer_get_string(&from_monitor, length_ptr)); -} - -/* - * Start and execute the code for the monitor which never returns from this - * function. The child will return and continue in the caller. - */ -void -altprivsep_start_and_do_monitor(int use_engine, int inetd, int newsock, - int statup_pipe) -{ - pid_t aps_child; - Authctxt *authctxt; - - /* - * The monitor will packet_close() in packet_set_monitor() called from - * altprivsep_start_monitor() below to clean up the socket stuff before - * it switches to pipes for communication to the child. The socket fd is - * closed there so we must dup it here - monitor needs that socket to - * shutdown the connection in case of any problem; see comments below. - * Note that current newsock was assigned to connection_(in|out) which - * are the variables used in packet_close() to close the communication - * socket. - */ - newsock = dup(newsock); - - if ((aps_child = altprivsep_start_monitor(&authctxt)) == -1) - fatal("Monitor could not be started."); - - if (aps_child > 0) { - /* ALTPRIVSEP Monitor */ - - /* - * The ALTPRIVSEP monitor here does: - * - * - record keeping and auditing - * - PAM cleanup - */ - - /* this is for MaxStartups and the child takes care of that */ - (void) close(statup_pipe); - (void) pkcs11_engine_load(use_engine); - - /* - * If the monitor fatal()s it will audit/record a logout, so - * we'd better do something to really mean it: shutdown the - * socket but leave the child alone -- it's been disconnected - * and we hope it exits, but killing any pid from a privileged - * monitor could be dangerous. - * - * NOTE: Order matters -- these fatal cleanups must come before - * the audit logout fatal cleanup as these functions are called - * in LIFO. - */ - fatal_add_cleanup((void (*)(void *))altprivsep_shutdown_sock, - (void *)&newsock); - - if (compat20) { - debug3("Recording SSHv2 session login in wtmpx"); - /* - * record_login() relies on connection_in to be the - * socket to get the peer address. The problem is that - * connection_in had to be set to the pipe descriptor in - * altprivsep_start_monitor(). It's not nice but the - * easiest way to get the peer's address is to - * temporarily set connection_in to the socket's file - * descriptor. - */ - packet_set_fds(inetd == 1 ? -1 : newsock, 0); - record_login(getpid(), NULL, "sshd", authctxt->user); - packet_set_fds(0, 1); - } - -#ifdef HAVE_BSM - /* Initialize the group list, audit sometimes needs it. */ - if (initgroups(authctxt->pw->pw_name, - authctxt->pw->pw_gid) < 0) { - perror("initgroups"); - exit (1); - } - - /* - * The monitor process fork()ed before the authentication - * process started so at this point we have an unaudited - * context. Thus we need to obtain the audit session data - * from the authentication process (aps_child) which will - * have the correct audit context for the user logging in. - * To do so we pass along the process-ID of the aps_child - * process so that it is referenced for this audit session - * rather than referencing the monitor's unaudited context. - */ - audit_sshd_login(&ah, aps_child); - - fatal_add_cleanup((void (*)(void *))audit_sshd_logout, - (void *)&ah); -#endif /* HAVE_BSM */ - -#ifdef GSSAPI - fatal_add_cleanup((void (*)(void *))ssh_gssapi_cleanup_creds, - (void *)&xxx_gssctxt); -#endif /* GSSAPI */ - - altprivsep_do_monitor(authctxt, aps_child); - - /* If we got here the connection is dead. */ - fatal_remove_cleanup((void (*)(void *))altprivsep_shutdown_sock, - (void *)&newsock); - - if (compat20) { - debug3("Recording SSHv2 session logout in wtmpx"); - record_logout(getpid(), NULL, "sshd", authctxt->user); - } - - /* - * Make sure the socket is closed. The monitor can't call - * packet_close here as it's done a packet_set_connection() - * with the pipe to the child instead of the socket. - */ - (void) shutdown(newsock, SHUT_RDWR); - -#ifdef GSSAPI - fatal_remove_cleanup((void (*)(void *))ssh_gssapi_cleanup_creds, - &xxx_gssctxt); - ssh_gssapi_cleanup_creds(xxx_gssctxt); - ssh_gssapi_server_mechs(NULL); /* release cached mechs list */ -#endif /* GSSAPI */ - -#ifdef HAVE_BSM - fatal_remove_cleanup((void (*)(void *))audit_sshd_logout, (void *)&ah); - audit_sshd_logout(&ah); -#endif /* HAVE_BSM */ - - exit(0); - } else { - /* - * This is the child, close the dup()ed file descriptor for a - * socket. It's not needed in the child. - */ - close(newsock); - } -} diff --git a/usr/src/cmd/ssh/sshd/auth-bsdauth.c b/usr/src/cmd/ssh/sshd/auth-bsdauth.c deleted file mode 100644 index 090fa0ef39..0000000000 --- a/usr/src/cmd/ssh/sshd/auth-bsdauth.c +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (c) 2001 Markus Friedl. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#include "includes.h" -RCSID("$OpenBSD: auth-bsdauth.c,v 1.5 2002/06/30 21:59:45 deraadt Exp $"); - -#pragma ident "%Z%%M% %I% %E% SMI" - -#ifdef BSD_AUTH -#include "xmalloc.h" -#include "auth.h" -#include "log.h" - -static void * -bsdauth_init_ctx(Authctxt *authctxt) -{ - return authctxt; -} - -int -bsdauth_query(void *ctx, char **name, char **infotxt, - u_int *numprompts, char ***prompts, u_int **echo_on) -{ - Authctxt *authctxt = ctx; - char *challenge = NULL; - - if (authctxt->as != NULL) { - debug2("bsdauth_query: try reuse session"); - challenge = auth_getitem(authctxt->as, AUTHV_CHALLENGE); - if (challenge == NULL) { - auth_close(authctxt->as); - authctxt->as = NULL; - } - } - - if (challenge == NULL) { - debug2("bsdauth_query: new bsd auth session"); - debug3("bsdauth_query: style %s", - authctxt->style ? authctxt->style : "<default>"); - authctxt->as = auth_userchallenge(authctxt->user, - authctxt->style, "auth-ssh", &challenge); - if (authctxt->as == NULL) - challenge = NULL; - debug2("bsdauth_query: <%s>", challenge ? challenge : "empty"); - } - - if (challenge == NULL) - return -1; - - *name = xstrdup(""); - *infotxt = xstrdup(""); - *numprompts = 1; - *prompts = xmalloc(*numprompts * sizeof(char *)); - *echo_on = xmalloc(*numprompts * sizeof(u_int)); - (*echo_on)[0] = 0; - (*prompts)[0] = xstrdup(challenge); - - return 0; -} - -int -bsdauth_respond(void *ctx, u_int numresponses, char **responses) -{ - Authctxt *authctxt = ctx; - int authok; - - if (authctxt->as == 0) - error("bsdauth_respond: no bsd auth session"); - - if (numresponses != 1) - return -1; - - authok = auth_userresponse(authctxt->as, responses[0], 0); - authctxt->as = NULL; - debug3("bsdauth_respond: <%s> = <%d>", responses[0], authok); - - return (authok == 0) ? -1 : 0; -} - -static void -bsdauth_free_ctx(void *ctx) -{ - Authctxt *authctxt = ctx; - - if (authctxt && authctxt->as) { - auth_close(authctxt->as); - authctxt->as = NULL; - } -} - -KbdintDevice bsdauth_device = { - "bsdauth", - bsdauth_init_ctx, - bsdauth_query, - bsdauth_respond, - bsdauth_free_ctx -}; - -KbdintDevice mm_bsdauth_device = { - "bsdauth", - bsdauth_init_ctx, - mm_bsdauth_query, - mm_bsdauth_respond, - bsdauth_free_ctx -}; -#endif diff --git a/usr/src/cmd/ssh/sshd/auth-chall.c b/usr/src/cmd/ssh/sshd/auth-chall.c deleted file mode 100644 index 07073f0d98..0000000000 --- a/usr/src/cmd/ssh/sshd/auth-chall.c +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2001 Markus Friedl. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "includes.h" -RCSID("$OpenBSD: auth-chall.c,v 1.8 2001/05/18 14:13:28 markus Exp $"); - -#pragma ident "%Z%%M% %I% %E% SMI" - -#include "auth.h" -#include "log.h" -#include "xmalloc.h" - -/* limited protocol v1 interface to kbd-interactive authentication */ - -extern KbdintDevice *devices[]; -static KbdintDevice *device; - -char * -get_challenge(Authctxt *authctxt) -{ - char *challenge, *name, *info, **prompts; - u_int i, numprompts; - u_int *echo_on; - - device = devices[0]; /* we always use the 1st device for protocol 1 */ - if (device == NULL) - return NULL; - if ((authctxt->kbdintctxt = device->init_ctx(authctxt)) == NULL) - return NULL; - if (device->query(authctxt->kbdintctxt, &name, &info, - &numprompts, &prompts, &echo_on)) { - device->free_ctx(authctxt->kbdintctxt); - authctxt->kbdintctxt = NULL; - return NULL; - } - if (numprompts < 1) - fatal("get_challenge: numprompts < 1"); - challenge = xstrdup(prompts[0]); - for (i = 0; i < numprompts; i++) - xfree(prompts[i]); - xfree(prompts); - xfree(name); - xfree(echo_on); - xfree(info); - - return (challenge); -} -int -verify_response(Authctxt *authctxt, const char *response) -{ - char *resp[1]; - int res; - - if (device == NULL) - return 0; - if (authctxt->kbdintctxt == NULL) - return 0; - resp[0] = (char *)response; - res = device->respond(authctxt->kbdintctxt, 1, resp); - device->free_ctx(authctxt->kbdintctxt); - authctxt->kbdintctxt = NULL; - return res ? 0 : 1; -} diff --git a/usr/src/cmd/ssh/sshd/auth-krb4.c b/usr/src/cmd/ssh/sshd/auth-krb4.c deleted file mode 100644 index 2a9103f232..0000000000 --- a/usr/src/cmd/ssh/sshd/auth-krb4.c +++ /dev/null @@ -1,370 +0,0 @@ -/* - * Copyright (c) 1999 Dug Song. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "includes.h" -RCSID("$OpenBSD: auth-krb4.c,v 1.28 2002/09/26 11:38:43 markus Exp $"); - -#pragma ident "%Z%%M% %I% %E% SMI" - -#include "ssh.h" -#include "ssh1.h" -#include "packet.h" -#include "xmalloc.h" -#include "log.h" -#include "servconf.h" -#include "uidswap.h" -#include "auth.h" - -#ifdef AFS -#include "radix.h" -#endif - -#ifdef KRB4 -extern ServerOptions options; - -static int -krb4_init(void *context) -{ - static int cleanup_registered = 0; - Authctxt *authctxt = (Authctxt *)context; - const char *tkt_root = TKT_ROOT; - struct stat st; - int fd; - - if (!authctxt->krb4_ticket_file) { - /* Set unique ticket string manually since we're still root. */ - authctxt->krb4_ticket_file = xmalloc(MAXPATHLEN); -#ifdef AFS - if (lstat("/ticket", &st) != -1) - tkt_root = "/ticket/"; -#endif /* AFS */ - snprintf(authctxt->krb4_ticket_file, MAXPATHLEN, "%s%u_%ld", - tkt_root, authctxt->pw->pw_uid, (long)getpid()); - krb_set_tkt_string(authctxt->krb4_ticket_file); - } - /* Register ticket cleanup in case of fatal error. */ - if (!cleanup_registered) { - fatal_add_cleanup(krb4_cleanup_proc, authctxt); - cleanup_registered = 1; - } - /* Try to create our ticket file. */ - if ((fd = mkstemp(authctxt->krb4_ticket_file)) != -1) { - close(fd); - return (1); - } - /* Ticket file exists - make sure user owns it (just passed ticket). */ - if (lstat(authctxt->krb4_ticket_file, &st) != -1) { - if (st.st_mode == (S_IFREG | S_IRUSR | S_IWUSR) && - st.st_uid == authctxt->pw->pw_uid) - return (1); - } - /* Failure - cancel cleanup function, leaving ticket for inspection. */ - log("WARNING: bad ticket file %s", authctxt->krb4_ticket_file); - - fatal_remove_cleanup(krb4_cleanup_proc, authctxt); - cleanup_registered = 0; - - xfree(authctxt->krb4_ticket_file); - authctxt->krb4_ticket_file = NULL; - - return (0); -} - -/* - * try krb4 authentication, - * return 1 on success, 0 on failure, -1 if krb4 is not available - */ -int -auth_krb4_password(Authctxt *authctxt, const char *password) -{ - AUTH_DAT adata; - KTEXT_ST tkt; - struct hostent *hp; - struct passwd *pw; - char localhost[MAXHOSTNAMELEN], phost[INST_SZ], realm[REALM_SZ]; - u_int32_t faddr; - int r; - - if ((pw = authctxt->pw) == NULL) - return (0); - - /* - * Try Kerberos password authentication only for non-root - * users and only if Kerberos is installed. - */ - if (pw->pw_uid != 0 && krb_get_lrealm(realm, 1) == KSUCCESS) { - /* Set up our ticket file. */ - if (!krb4_init(authctxt)) { - log("Couldn't initialize Kerberos ticket file for %s!", - pw->pw_name); - goto failure; - } - /* Try to get TGT using our password. */ - r = krb_get_pw_in_tkt((char *) pw->pw_name, "", realm, - "krbtgt", realm, DEFAULT_TKT_LIFE, (char *)password); - if (r != INTK_OK) { - debug("Kerberos v4 password authentication for %s " - "failed: %s", pw->pw_name, krb_err_txt[r]); - goto failure; - } - /* Successful authentication. */ - chown(tkt_string(), pw->pw_uid, pw->pw_gid); - - /* - * Now that we have a TGT, try to get a local - * "rcmd" ticket to ensure that we are not talking - * to a bogus Kerberos server. - */ - gethostname(localhost, sizeof(localhost)); - strlcpy(phost, (char *)krb_get_phost(localhost), - sizeof(phost)); - r = krb_mk_req(&tkt, KRB4_SERVICE_NAME, phost, realm, 33); - - if (r == KSUCCESS) { - if ((hp = gethostbyname(localhost)) == NULL) { - log("Couldn't get local host address!"); - goto failure; - } - memmove((void *)&faddr, (void *)hp->h_addr, - sizeof(faddr)); - - /* Verify our "rcmd" ticket. */ - r = krb_rd_req(&tkt, KRB4_SERVICE_NAME, phost, - faddr, &adata, ""); - if (r == RD_AP_UNDEC) { - /* - * Probably didn't have a srvtab on - * localhost. Disallow login. - */ - log("Kerberos v4 TGT for %s unverifiable, " - "no srvtab installed? krb_rd_req: %s", - pw->pw_name, krb_err_txt[r]); - goto failure; - } else if (r != KSUCCESS) { - log("Kerberos v4 %s ticket unverifiable: %s", - KRB4_SERVICE_NAME, krb_err_txt[r]); - goto failure; - } - } else if (r == KDC_PR_UNKNOWN) { - /* - * Disallow login if no rcmd service exists, and - * log the error. - */ - log("Kerberos v4 TGT for %s unverifiable: %s; %s.%s " - "not registered, or srvtab is wrong?", pw->pw_name, - krb_err_txt[r], KRB4_SERVICE_NAME, phost); - goto failure; - } else { - /* - * TGT is bad, forget it. Possibly spoofed! - */ - debug("WARNING: Kerberos v4 TGT possibly spoofed " - "for %s: %s", pw->pw_name, krb_err_txt[r]); - goto failure; - } - /* Authentication succeeded. */ - return (1); - } else - /* Logging in as root or no local Kerberos realm. */ - debug("Unable to authenticate to Kerberos."); - - failure: - krb4_cleanup_proc(authctxt); - - if (!options.kerberos_or_local_passwd) - return (0); - - /* Fall back to ordinary passwd authentication. */ - return (-1); -} - -void -krb4_cleanup_proc(void *context) -{ - Authctxt *authctxt = (Authctxt *)context; - debug("krb4_cleanup_proc called"); - if (authctxt->krb4_ticket_file) { - (void) dest_tkt(); - xfree(authctxt->krb4_ticket_file); - authctxt->krb4_ticket_file = NULL; - } -} - -int -auth_krb4(Authctxt *authctxt, KTEXT auth, char **client, KTEXT reply) -{ - AUTH_DAT adat = {0}; - Key_schedule schedule; - struct sockaddr_in local, foreign; - char instance[INST_SZ]; - socklen_t slen; - u_int cksum; - int r, s; - - s = packet_get_connection_in(); - - slen = sizeof(local); - memset(&local, 0, sizeof(local)); - if (getsockname(s, (struct sockaddr *) & local, &slen) < 0) - debug("getsockname failed: %.100s", strerror(errno)); - slen = sizeof(foreign); - memset(&foreign, 0, sizeof(foreign)); - if (getpeername(s, (struct sockaddr *) & foreign, &slen) < 0) { - debug("getpeername failed: %.100s", strerror(errno)); - fatal_cleanup(); - } - instance[0] = '*'; - instance[1] = 0; - - /* Get the encrypted request, challenge, and session key. */ - if ((r = krb_rd_req(auth, KRB4_SERVICE_NAME, instance, - 0, &adat, ""))) { - debug("Kerberos v4 krb_rd_req: %.100s", krb_err_txt[r]); - return (0); - } - des_key_sched((des_cblock *) adat.session, schedule); - - *client = xmalloc(MAX_K_NAME_SZ); - (void) snprintf(*client, MAX_K_NAME_SZ, "%s%s%s@%s", adat.pname, - *adat.pinst ? "." : "", adat.pinst, adat.prealm); - - /* Check ~/.klogin authorization now. */ - if (kuserok(&adat, authctxt->user) != KSUCCESS) { - log("Kerberos v4 .klogin authorization failed for %s to " - "account %s", *client, authctxt->user); - xfree(*client); - *client = NULL; - return (0); - } - /* Increment the checksum, and return it encrypted with the - session key. */ - cksum = adat.checksum + 1; - cksum = htonl(cksum); - - /* If we can't successfully encrypt the checksum, we send back an - empty message, admitting our failure. */ - if ((r = krb_mk_priv((u_char *) & cksum, reply->dat, sizeof(cksum) + 1, - schedule, &adat.session, &local, &foreign)) < 0) { - debug("Kerberos v4 mk_priv: (%d) %s", r, krb_err_txt[r]); - reply->dat[0] = 0; - reply->length = 0; - } else - reply->length = r; - - /* Clear session key. */ - memset(&adat.session, 0, sizeof(&adat.session)); - return (1); -} -#endif /* KRB4 */ - -#ifdef AFS -int -auth_krb4_tgt(Authctxt *authctxt, const char *string) -{ - CREDENTIALS creds; - struct passwd *pw; - - if ((pw = authctxt->pw) == NULL) - goto failure; - - temporarily_use_uid(pw); - - if (!radix_to_creds(string, &creds)) { - log("Protocol error decoding Kerberos v4 TGT"); - goto failure; - } - if (strncmp(creds.service, "", 1) == 0) /* backward compatibility */ - strlcpy(creds.service, "krbtgt", sizeof creds.service); - - if (strcmp(creds.service, "krbtgt")) { - log("Kerberos v4 TGT (%s%s%s@%s) rejected for %s", - creds.pname, creds.pinst[0] ? "." : "", creds.pinst, - creds.realm, pw->pw_name); - goto failure; - } - if (!krb4_init(authctxt)) - goto failure; - - if (in_tkt(creds.pname, creds.pinst) != KSUCCESS) - goto failure; - - if (save_credentials(creds.service, creds.instance, creds.realm, - creds.session, creds.lifetime, creds.kvno, &creds.ticket_st, - creds.issue_date) != KSUCCESS) { - debug("Kerberos v4 TGT refused: couldn't save credentials"); - goto failure; - } - /* Successful authentication, passed all checks. */ - chown(tkt_string(), pw->pw_uid, pw->pw_gid); - - debug("Kerberos v4 TGT accepted (%s%s%s@%s)", - creds.pname, creds.pinst[0] ? "." : "", creds.pinst, creds.realm); - memset(&creds, 0, sizeof(creds)); - - restore_uid(); - - return (1); - - failure: - krb4_cleanup_proc(authctxt); - memset(&creds, 0, sizeof(creds)); - restore_uid(); - - return (0); -} - -int -auth_afs_token(Authctxt *authctxt, const char *token_string) -{ - CREDENTIALS creds; - struct passwd *pw; - uid_t uid; - - if ((pw = authctxt->pw) == NULL) - return (0); - - if (!radix_to_creds(token_string, &creds)) { - log("Protocol error decoding AFS token"); - return (0); - } - if (strncmp(creds.service, "", 1) == 0) /* backward compatibility */ - strlcpy(creds.service, "afs", sizeof creds.service); - - if (strncmp(creds.pname, "AFS ID ", 7) == 0) - uid = atoi(creds.pname + 7); - else - uid = pw->pw_uid; - - if (kafs_settoken(creds.realm, uid, &creds)) { - log("AFS token (%s@%s) rejected for %s", - creds.pname, creds.realm, pw->pw_name); - memset(&creds, 0, sizeof(creds)); - return (0); - } - debug("AFS token accepted (%s@%s)", creds.pname, creds.realm); - memset(&creds, 0, sizeof(creds)); - - return (1); -} -#endif /* AFS */ diff --git a/usr/src/cmd/ssh/sshd/auth-krb5.c b/usr/src/cmd/ssh/sshd/auth-krb5.c deleted file mode 100644 index 3f5416af41..0000000000 --- a/usr/src/cmd/ssh/sshd/auth-krb5.c +++ /dev/null @@ -1,407 +0,0 @@ -/* - * Kerberos v5 authentication and ticket-passing routines. - * - * $FreeBSD: src/crypto/openssh/auth-krb5.c,v 1.6 2001/02/13 16:58:04 assar Exp $ - */ -/* - * Copyright (c) 2002 Daniel Kouril. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "includes.h" -RCSID("$OpenBSD: auth-krb5.c,v 1.9 2002/09/09 06:48:06 itojun Exp $"); - -#pragma ident "%Z%%M% %I% %E% SMI" - -#include "ssh.h" -#include "ssh1.h" -#include "packet.h" -#include "xmalloc.h" -#include "log.h" -#include "servconf.h" -#include "uidswap.h" -#include "auth.h" - -#ifdef KRB5 -#include <krb5.h> -#ifndef HEIMDAL -#define krb5_get_err_text(context,code) error_message(code) -#endif /* !HEIMDAL */ - -extern ServerOptions options; - -static int -krb5_init(void *context) -{ - Authctxt *authctxt = (Authctxt *)context; - krb5_error_code problem; - static int cleanup_registered = 0; - - if (authctxt->krb5_ctx == NULL) { - problem = krb5_init_context(&authctxt->krb5_ctx); - if (problem) - return (problem); - krb5_init_ets(authctxt->krb5_ctx); - } - if (!cleanup_registered) { - fatal_add_cleanup(krb5_cleanup_proc, authctxt); - cleanup_registered = 1; - } - return (0); -} - -/* - * Try krb5 authentication. server_user is passed for logging purposes - * only, in auth is received ticket, in client is returned principal - * from the ticket - */ -int -auth_krb5(Authctxt *authctxt, krb5_data *auth, char **client, krb5_data *reply) -{ - krb5_error_code problem; - krb5_principal server; - krb5_ticket *ticket; - int fd, ret; - - ret = 0; - server = NULL; - ticket = NULL; - reply->length = 0; - - problem = krb5_init(authctxt); - if (problem) - goto err; - - problem = krb5_auth_con_init(authctxt->krb5_ctx, - &authctxt->krb5_auth_ctx); - if (problem) - goto err; - - fd = packet_get_connection_in(); -#ifdef HEIMDAL - problem = krb5_auth_con_setaddrs_from_fd(authctxt->krb5_ctx, - authctxt->krb5_auth_ctx, &fd); -#else - problem = krb5_auth_con_genaddrs(authctxt->krb5_ctx, - authctxt->krb5_auth_ctx,fd, - KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR | - KRB5_AUTH_CONTEXT_GENERATE_LOCAL_FULL_ADDR); -#endif - if (problem) - goto err; - - problem = krb5_sname_to_principal(authctxt->krb5_ctx, NULL, NULL , - KRB5_NT_SRV_HST, &server); - if (problem) - goto err; - - problem = krb5_rd_req(authctxt->krb5_ctx, &authctxt->krb5_auth_ctx, - auth, server, NULL, NULL, &ticket); - if (problem) - goto err; - -#ifdef HEIMDAL - problem = krb5_copy_principal(authctxt->krb5_ctx, ticket->client, - &authctxt->krb5_user); -#else - problem = krb5_copy_principal(authctxt->krb5_ctx, - ticket->enc_part2->client, - &authctxt->krb5_user); -#endif - if (problem) - goto err; - - /* if client wants mutual auth */ - problem = krb5_mk_rep(authctxt->krb5_ctx, authctxt->krb5_auth_ctx, - reply); - if (problem) - goto err; - - /* Check .k5login authorization now. */ - if (!krb5_kuserok(authctxt->krb5_ctx, authctxt->krb5_user, - authctxt->pw->pw_name)) - goto err; - - if (client) - krb5_unparse_name(authctxt->krb5_ctx, authctxt->krb5_user, - client); - - ret = 1; - err: - if (server) - krb5_free_principal(authctxt->krb5_ctx, server); - if (ticket) - krb5_free_ticket(authctxt->krb5_ctx, ticket); - if (!ret && reply->length) { - xfree(reply->data); - memset(reply, 0, sizeof(*reply)); - } - - if (problem) { - if (authctxt->krb5_ctx != NULL) - debug("Kerberos v5 authentication failed: %s", - krb5_get_err_text(authctxt->krb5_ctx, problem)); - else - debug("Kerberos v5 authentication failed: %d", - problem); - } - - return (ret); -} - -int -auth_krb5_tgt(Authctxt *authctxt, krb5_data *tgt) -{ - krb5_error_code problem; - krb5_ccache ccache = NULL; - char *pname; - krb5_creds **creds; - - if (authctxt->pw == NULL || authctxt->krb5_user == NULL) - return (0); - - temporarily_use_uid(authctxt->pw); - -#ifdef HEIMDAL - problem = krb5_cc_gen_new(authctxt->krb5_ctx, &krb5_fcc_ops, &ccache); -#else -{ - char ccname[40]; - int tmpfd; - - snprintf(ccname,sizeof(ccname),"FILE:/tmp/krb5cc_%d_XXXXXX",geteuid()); - - if ((tmpfd = mkstemp(ccname+strlen("FILE:")))==-1) { - log("mkstemp(): %.100s", strerror(errno)); - problem = errno; - goto fail; - } - if (fchmod(tmpfd,S_IRUSR | S_IWUSR) == -1) { - log("fchmod(): %.100s", strerror(errno)); - close(tmpfd); - problem = errno; - goto fail; - } - close(tmpfd); - problem = krb5_cc_resolve(authctxt->krb5_ctx, ccname, &ccache); -} -#endif - if (problem) - goto fail; - - problem = krb5_cc_initialize(authctxt->krb5_ctx, ccache, - authctxt->krb5_user); - if (problem) - goto fail; - -#ifdef HEIMDAL - problem = krb5_rd_cred2(authctxt->krb5_ctx, authctxt->krb5_auth_ctx, - ccache, tgt); - if (problem) - goto fail; -#else - problem = krb5_rd_cred(authctxt->krb5_ctx, authctxt->krb5_auth_ctx, - tgt, &creds, NULL); - if (problem) - goto fail; - problem = krb5_cc_store_cred(authctxt->krb5_ctx, ccache, *creds); - if (problem) - goto fail; -#endif - - authctxt->krb5_fwd_ccache = ccache; - ccache = NULL; - - authctxt->krb5_ticket_file = (char *)krb5_cc_get_name(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache); - - problem = krb5_unparse_name(authctxt->krb5_ctx, authctxt->krb5_user, - &pname); - if (problem) - goto fail; - - debug("Kerberos v5 TGT accepted (%s)", pname); - - restore_uid(); - - return (1); - - fail: - if (problem) - debug("Kerberos v5 TGT passing failed: %s", - krb5_get_err_text(authctxt->krb5_ctx, problem)); - if (ccache) - krb5_cc_destroy(authctxt->krb5_ctx, ccache); - - restore_uid(); - - return (0); -} - -int -auth_krb5_password(Authctxt *authctxt, const char *password) -{ -#ifndef HEIMDAL - krb5_creds creds; - krb5_principal server; - char ccname[40]; - int tmpfd; -#endif - krb5_error_code problem; - - if (authctxt->pw == NULL) - return (0); - - temporarily_use_uid(authctxt->pw); - - problem = krb5_init(authctxt); - if (problem) - goto out; - - problem = krb5_parse_name(authctxt->krb5_ctx, authctxt->pw->pw_name, - &authctxt->krb5_user); - if (problem) - goto out; - -#ifdef HEIMDAL - problem = krb5_cc_gen_new(authctxt->krb5_ctx, &krb5_mcc_ops, - &authctxt->krb5_fwd_ccache); - if (problem) - goto out; - - problem = krb5_cc_initialize(authctxt->krb5_ctx, - authctxt->krb5_fwd_ccache, authctxt->krb5_user); - if (problem) - goto out; - - restore_uid(); - problem = krb5_verify_user(authctxt->krb5_ctx, authctxt->krb5_user, - authctxt->krb5_fwd_ccache, password, 1, NULL); - temporarily_use_uid(authctxt->pw); - - if (problem) - goto out; - -#else - problem = krb5_get_init_creds_password(authctxt->krb5_ctx, &creds, - authctxt->krb5_user, (char *)password, NULL, NULL, 0, NULL, NULL); - if (problem) - goto out; - - problem = krb5_sname_to_principal(authctxt->krb5_ctx, NULL, NULL, - KRB5_NT_SRV_HST, &server); - if (problem) - goto out; - - restore_uid(); - problem = krb5_verify_init_creds(authctxt->krb5_ctx, &creds, server, - NULL, NULL, NULL); - krb5_free_principal(authctxt->krb5_ctx, server); - temporarily_use_uid(authctxt->pw); - if (problem) - goto out; - - if (!krb5_kuserok(authctxt->krb5_ctx, authctxt->krb5_user, - authctxt->pw->pw_name)) { - problem = -1; - goto out; - } - - snprintf(ccname,sizeof(ccname),"FILE:/tmp/krb5cc_%d_XXXXXX",geteuid()); - - if ((tmpfd = mkstemp(ccname+strlen("FILE:")))==-1) { - log("mkstemp(): %.100s", strerror(errno)); - problem = errno; - goto out; - } - - if (fchmod(tmpfd,S_IRUSR | S_IWUSR) == -1) { - log("fchmod(): %.100s", strerror(errno)); - close(tmpfd); - problem = errno; - goto out; - } - close(tmpfd); - - problem = krb5_cc_resolve(authctxt->krb5_ctx, ccname, &authctxt->krb5_fwd_ccache); - if (problem) - goto out; - - problem = krb5_cc_initialize(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache, - authctxt->krb5_user); - if (problem) - goto out; - - problem= krb5_cc_store_cred(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache, - &creds); - if (problem) - goto out; -#endif - - authctxt->krb5_ticket_file = (char *)krb5_cc_get_name(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache); - - out: - restore_uid(); - - if (problem) { - if (authctxt->krb5_ctx != NULL && problem!=-1) - debug("Kerberos password authentication failed: %s", - krb5_get_err_text(authctxt->krb5_ctx, problem)); - else - debug("Kerberos password authentication failed: %d", - problem); - - krb5_cleanup_proc(authctxt); - - if (options.kerberos_or_local_passwd) - return (-1); - else - return (0); - } - return (1); -} - -void -krb5_cleanup_proc(void *context) -{ - Authctxt *authctxt = (Authctxt *)context; - - debug("krb5_cleanup_proc called"); - if (authctxt->krb5_fwd_ccache) { - krb5_cc_destroy(authctxt->krb5_ctx, authctxt->krb5_fwd_ccache); - authctxt->krb5_fwd_ccache = NULL; - } - if (authctxt->krb5_user) { - krb5_free_principal(authctxt->krb5_ctx, authctxt->krb5_user); - authctxt->krb5_user = NULL; - } - if (authctxt->krb5_auth_ctx) { - krb5_auth_con_free(authctxt->krb5_ctx, - authctxt->krb5_auth_ctx); - authctxt->krb5_auth_ctx = NULL; - } - if (authctxt->krb5_ctx) { - krb5_free_context(authctxt->krb5_ctx); - authctxt->krb5_ctx = NULL; - } -} - -#endif /* KRB5 */ diff --git a/usr/src/cmd/ssh/sshd/auth-options.c b/usr/src/cmd/ssh/sshd/auth-options.c deleted file mode 100644 index b186cbe045..0000000000 --- a/usr/src/cmd/ssh/sshd/auth-options.c +++ /dev/null @@ -1,299 +0,0 @@ -/* - * Author: Tatu Ylonen <ylo@cs.hut.fi> - * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland - * All rights reserved - * As far as I am concerned, the code I have written for this software - * can be used freely for any purpose. Any derived versions of this - * software must be clearly marked as such, and if the derived work is - * incompatible with the protocol description in the RFC file, it must be - * called by a name other than "ssh" or "Secure Shell". - */ - -#include "includes.h" -RCSID("$OpenBSD: auth-options.c,v 1.26 2002/07/30 17:03:55 markus Exp $"); - -#pragma ident "%Z%%M% %I% %E% SMI" - -#include "xmalloc.h" -#include "match.h" -#include "log.h" -#include "canohost.h" -#include "channels.h" -#include "auth-options.h" -#include "servconf.h" -#include "misc.h" -#include "auth.h" - -/* Flags set authorized_keys flags */ -int no_port_forwarding_flag = 0; -int no_agent_forwarding_flag = 0; -int no_x11_forwarding_flag = 0; -int no_pty_flag = 0; - -/* "command=" option. */ -char *forced_command = NULL; - -/* "environment=" options. */ -struct envstring *custom_environment = NULL; - -extern ServerOptions options; - -void -auth_clear_options(void) -{ - no_agent_forwarding_flag = 0; - no_port_forwarding_flag = 0; - no_pty_flag = 0; - no_x11_forwarding_flag = 0; - while (custom_environment) { - struct envstring *ce = custom_environment; - custom_environment = ce->next; - xfree(ce->s); - xfree(ce); - } - if (forced_command) { - xfree(forced_command); - forced_command = NULL; - } - channel_clear_permitted_opens(); - auth_debug_reset(); -} - -/* - * return 1 if access is granted, 0 if not. - * side effect: sets key option flags - */ -int -auth_parse_options(struct passwd *pw, char *opts, char *file, u_long linenum) -{ - const char *cp; - int i; - - /* reset options */ - auth_clear_options(); - - if (!opts) - return 1; - - while (*opts && *opts != ' ' && *opts != '\t') { - cp = "no-port-forwarding"; - if (strncasecmp(opts, cp, strlen(cp)) == 0) { - auth_debug_add("Port forwarding disabled."); - no_port_forwarding_flag = 1; - opts += strlen(cp); - goto next_option; - } - cp = "no-agent-forwarding"; - if (strncasecmp(opts, cp, strlen(cp)) == 0) { - auth_debug_add("Agent forwarding disabled."); - no_agent_forwarding_flag = 1; - opts += strlen(cp); - goto next_option; - } - cp = "no-X11-forwarding"; - if (strncasecmp(opts, cp, strlen(cp)) == 0) { - auth_debug_add("X11 forwarding disabled."); - no_x11_forwarding_flag = 1; - opts += strlen(cp); - goto next_option; - } - cp = "no-pty"; - if (strncasecmp(opts, cp, strlen(cp)) == 0) { - auth_debug_add("Pty allocation disabled."); - no_pty_flag = 1; - opts += strlen(cp); - goto next_option; - } - cp = "command=\""; - if (strncasecmp(opts, cp, strlen(cp)) == 0) { - opts += strlen(cp); - forced_command = xmalloc(strlen(opts) + 1); - i = 0; - while (*opts) { - if (*opts == '"') - break; - if (*opts == '\\' && opts[1] == '"') { - opts += 2; - forced_command[i++] = '"'; - continue; - } - forced_command[i++] = *opts++; - } - if (!*opts) { - debug("%.100s, line %lu: missing end quote", - file, linenum); - auth_debug_add("%.100s, line %lu: missing end quote", - file, linenum); - xfree(forced_command); - forced_command = NULL; - goto bad_option; - } - forced_command[i] = 0; - auth_debug_add("Forced command: %.900s", forced_command); - opts++; - goto next_option; - } - cp = "environment=\""; - if (options.permit_user_env && - strncasecmp(opts, cp, strlen(cp)) == 0) { - char *s; - struct envstring *new_envstring; - - opts += strlen(cp); - s = xmalloc(strlen(opts) + 1); - i = 0; - while (*opts) { - if (*opts == '"') - break; - if (*opts == '\\' && opts[1] == '"') { - opts += 2; - s[i++] = '"'; - continue; - } - s[i++] = *opts++; - } - if (!*opts) { - debug("%.100s, line %lu: missing end quote", - file, linenum); - auth_debug_add("%.100s, line %lu: missing end quote", - file, linenum); - xfree(s); - goto bad_option; - } - s[i] = 0; - auth_debug_add("Adding to environment: %.900s", s); - debug("Adding to environment: %.900s", s); - opts++; - new_envstring = xmalloc(sizeof(struct envstring)); - new_envstring->s = s; - new_envstring->next = custom_environment; - custom_environment = new_envstring; - goto next_option; - } - cp = "from=\""; - if (strncasecmp(opts, cp, strlen(cp)) == 0) { - const char *remote_ip = get_remote_ipaddr(); - const char *remote_host = get_canonical_hostname( - options.verify_reverse_mapping); - char *patterns = xmalloc(strlen(opts) + 1); - - opts += strlen(cp); - i = 0; - while (*opts) { - if (*opts == '"') - break; - if (*opts == '\\' && opts[1] == '"') { - opts += 2; - patterns[i++] = '"'; - continue; - } - patterns[i++] = *opts++; - } - if (!*opts) { - debug("%.100s, line %lu: missing end quote", - file, linenum); - auth_debug_add("%.100s, line %lu: missing end quote", - file, linenum); - xfree(patterns); - goto bad_option; - } - patterns[i] = 0; - opts++; - if (match_host_and_ip(remote_host, remote_ip, - patterns) != 1) { - xfree(patterns); - log("Authentication tried for %.100s with " - "correct key but not from a permitted " - "host (host=%.200s, ip=%.200s).", - pw->pw_name, remote_host, remote_ip); - auth_debug_add("Your host '%.200s' is not " - "permitted to use this key for login.", - remote_host); - /* deny access */ - return 0; - } - xfree(patterns); - /* Host name matches. */ - goto next_option; - } - cp = "permitopen=\""; - if (strncasecmp(opts, cp, strlen(cp)) == 0) { - char host[256], sport[6]; - u_short port; - char *patterns = xmalloc(strlen(opts) + 1); - - opts += strlen(cp); - i = 0; - while (*opts) { - if (*opts == '"') - break; - if (*opts == '\\' && opts[1] == '"') { - opts += 2; - patterns[i++] = '"'; - continue; - } - patterns[i++] = *opts++; - } - if (!*opts) { - debug("%.100s, line %lu: missing end quote", - file, linenum); - auth_debug_add("%.100s, line %lu: missing end quote", - file, linenum); - xfree(patterns); - goto bad_option; - } - patterns[i] = 0; - opts++; - if (sscanf(patterns, "%255[^:]:%5[0-9]", host, sport) != 2 && - sscanf(patterns, "%255[^/]/%5[0-9]", host, sport) != 2) { - debug("%.100s, line %lu: Bad permitopen specification " - "<%.100s>", file, linenum, patterns); - auth_debug_add("%.100s, line %lu: " - "Bad permitopen specification", file, linenum); - xfree(patterns); - goto bad_option; - } - if ((port = a2port(sport)) == 0) { - debug("%.100s, line %lu: Bad permitopen port <%.100s>", - file, linenum, sport); - auth_debug_add("%.100s, line %lu: " - "Bad permitopen port", file, linenum); - xfree(patterns); - goto bad_option; - } - if (options.allow_tcp_forwarding) - channel_add_permitted_opens(host, port); - xfree(patterns); - goto next_option; - } -next_option: - /* - * Skip the comma, and move to the next option - * (or break out if there are no more). - */ - if (!*opts) - fatal("Bugs in auth-options.c option processing."); - if (*opts == ' ' || *opts == '\t') - break; /* End of options. */ - if (*opts != ',') - goto bad_option; - opts++; - /* Process the next option. */ - } - - auth_debug_send(); - - /* grant access */ - return 1; - -bad_option: - log("Bad options in %.100s file, line %lu: %.50s", - file, linenum, opts); - auth_debug_add("Bad options in %.100s file, line %lu: %.50s", - file, linenum, opts); - - auth_debug_send(); - - /* deny access */ - return 0; -} diff --git a/usr/src/cmd/ssh/sshd/auth-pam.c b/usr/src/cmd/ssh/sshd/auth-pam.c deleted file mode 100644 index c3686b4928..0000000000 --- a/usr/src/cmd/ssh/sshd/auth-pam.c +++ /dev/null @@ -1,617 +0,0 @@ -/* - * Copyright (c) 2000 Damien Miller. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -/* - * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. - */ - -#include "includes.h" - -#ifdef USE_PAM -#include "xmalloc.h" -#include "log.h" -#include "auth.h" -#include "auth-options.h" -#include "auth-pam.h" -#include "buffer.h" -#include "servconf.h" -#include "canohost.h" -#include "compat.h" -#include "misc.h" -#include "sshlogin.h" -#include "ssh-gss.h" - -#include <security/pam_appl.h> - -extern char *__progname; - -extern u_int utmp_len; -extern ServerOptions options; - -extern Authmethod method_kbdint; - -RCSID("$Id: auth-pam.c,v 1.54 2002/07/28 20:24:08 stevesk Exp $"); - -#define NEW_AUTHTOK_MSG \ - "Warning: Your password has expired, please change it now." - -/* PAM conversation for non-interactive userauth methods */ -static int do_pam_conversation(int num_msg, const struct pam_message **msg, - struct pam_response **resp, void *appdata_ptr); - -static void do_pam_cleanup_proc(void *context); - -static char *get_method_name(Authctxt *authctxt); - -/* PAM conversation for non-interactive userauth methods */ -static struct pam_conv conv = { - (int (*)())do_pam_conversation, - NULL -}; -static char *__pam_msg = NULL; - -static -char * -get_method_name(Authctxt *authctxt) -{ - if (!authctxt) - return "(unknown)"; - - if (!compat20) - return (authctxt->v1_auth_name) ? authctxt->v1_auth_name : - "(sshv1-unknown)"; - - if (!authctxt->method || !authctxt->method->name) - return "(sshv2-unknown)"; - - return authctxt->method->name; -} - -char * -derive_pam_service_name(Authmethod *method) -{ - char *svcname = xmalloc(BUFSIZ); - - /* - * If PamServiceName is set we use that for everything, including - * SSHv1 - */ - if (options.pam_service_name != NULL) { - (void) strlcpy(svcname, options.pam_service_name, BUFSIZ); - return (svcname); - } - - if (compat20 && method) { - char *method_name = method->name; - - if (!method_name) - fatal("Userauth method unknown while starting PAM"); - - /* - * For SSHv2 we use "sshd-<userauth name> - * The "sshd" prefix can be changed via the PAMServicePrefix - * sshd_config option. - */ - if (strcmp(method_name, "none") == 0) { - snprintf(svcname, BUFSIZ, "%s-none", - options.pam_service_prefix); - } - if (strcmp(method_name, "password") == 0) { - snprintf(svcname, BUFSIZ, "%s-password", - options.pam_service_prefix); - } - if (strcmp(method_name, "keyboard-interactive") == 0) { - /* "keyboard-interactive" is too long, shorten it */ - snprintf(svcname, BUFSIZ, "%s-kbdint", - options.pam_service_prefix); - } - if (strcmp(method_name, "publickey") == 0) { - /* "publickey" is too long, shorten it */ - snprintf(svcname, BUFSIZ, "%s-pubkey", - options.pam_service_prefix); - } - if (strcmp(method_name, "hostbased") == 0) { - /* "hostbased" can't really be shortened... */ - snprintf(svcname, BUFSIZ, "%s-hostbased", - options.pam_service_prefix); - } - if (strncmp(method_name, "gss", 3) == 0) { - /* "gss" is too short, elongate it */ - snprintf(svcname, BUFSIZ, "%s-gssapi", - options.pam_service_prefix); - } - return svcname; - } else { - /* SSHv1 doesn't get to be so cool */ - snprintf(svcname, BUFSIZ, "%s-v1", - options.pam_service_prefix); - } - return svcname; -} - -void -new_start_pam(Authctxt *authctxt, struct pam_conv *conv) -{ - int retval; - pam_handle_t *pamh; - const char *rhost; - char *svc; - char *user = NULL; - pam_stuff *pam; - - if (authctxt == NULL) - fatal("Internal error during userauth"); - - if (compat20 && authctxt->method == NULL) - fatal("Userauth method unknown while starting PAM"); - - /* PAM service selected here */ - svc = derive_pam_service_name(authctxt->method); - debug2("Starting PAM service %s for method %s", svc, - get_method_name(authctxt)); - - if (authctxt->user != NULL) - user = authctxt->user; - - /* Cleanup previous PAM state */ - if (authctxt->pam != NULL) { - fatal_remove_cleanup(&do_pam_cleanup_proc, authctxt->pam); - do_pam_cleanup_proc(authctxt->pam); - } - - pam = xmalloc(sizeof(pam_stuff)); - (void) memset(pam, 0, sizeof(pam_stuff)); - - /* - * pam->last_pam_retval has to be and is considered - * along with pam->state. - * - * pam->state = 0; -> no PAM auth, account, etc, work - * done yet. (Set by memset() above.) - * - * pam->last_pam_retval = PAM_SUCCESS; -> meaningless at - * this point. - * - * See finish_userauth_do_pam() below. - */ - pam->authctxt = authctxt; - pam->last_pam_retval = PAM_SUCCESS; - - authctxt->pam = pam; - - /* Free any previously stored text/error PAM prompts */ - if (__pam_msg) { - xfree(__pam_msg); - __pam_msg = NULL; - } - - if ((retval = pam_start(svc, user, conv, &pamh)) != PAM_SUCCESS) { - fatal("PAM initialization failed during %s userauth", - get_method_name(authctxt)); - } - - free(svc); - - fatal_add_cleanup((void (*)(void *)) &do_pam_cleanup_proc, - (void *) authctxt->pam); - - rhost = get_remote_name_or_ip(utmp_len, options.verify_reverse_mapping); - if ((retval = pam_set_item(pamh, PAM_RHOST, rhost)) != PAM_SUCCESS) { - (void) pam_end(pamh, retval); - fatal("Could not set PAM_RHOST item during %s userauth", - get_method_name(authctxt)); - } - - if ((retval = pam_set_item(pamh, PAM_TTY, "sshd")) != PAM_SUCCESS) { - (void) pam_end(pamh, retval); - fatal("Could not set PAM_TTY item during %s userauth", - get_method_name(authctxt)); - } - - if (authctxt->cuser != NULL) - if ((retval = pam_set_item(pamh, PAM_AUSER, authctxt->cuser)) != PAM_SUCCESS) { - (void) pam_end(pamh, retval); - fatal("Could not set PAM_AUSER item during %s userauth", - get_method_name(authctxt)); - } - - authctxt->pam->h = pamh; -} - -/* - * To be called from userauth methods, directly (as in keyboard-interactive) or - * indirectly (from auth_pam_password() or from do_pam_non_initial_userauth(). - * - * The caller is responsible for calling new_start_pam() first. - * - * PAM state is not cleaned up here on error. This is left to subsequent calls - * to new_start_pam() or to the cleanup function upon authentication error. - */ -int -finish_userauth_do_pam(Authctxt *authctxt) -{ - int retval; - char *user, *method; - - /* Various checks; fail gracefully */ - if (authctxt == NULL || authctxt->pam == NULL) - return PAM_SYSTEM_ERR; /* shouldn't happen */ - - if (compat20) { - if (authctxt->method == NULL || authctxt->method->name == NULL) - return PAM_SYSTEM_ERR; /* shouldn't happen */ - method = authctxt->method->name; - } else if ((method = authctxt->v1_auth_name) == NULL) - return PAM_SYSTEM_ERR; /* shouldn't happen */ - - if (AUTHPAM_DONE(authctxt)) - return PAM_SYSTEM_ERR; /* shouldn't happen */ - - if (!(authctxt->pam->state & PAM_S_DONE_ACCT_MGMT)) { - retval = pam_acct_mgmt(authctxt->pam->h, 0); - authctxt->pam->last_pam_retval = retval; - if (retval == PAM_NEW_AUTHTOK_REQD) { - userauth_force_kbdint(); - return retval; - } - if (retval != PAM_SUCCESS) - return retval; - authctxt->pam->state |= PAM_S_DONE_ACCT_MGMT; - } - - /* - * Handle PAM_USER change, if any. - * - * We do this before pam_open_session() because we need the PAM_USER's - * UID for: - * - * a) PermitRootLogin checking - * b) to get at the lastlog entry before pam_open_session() updates it. - */ - retval = pam_get_item(authctxt->pam->h, PAM_USER, (void **) &user); - if (retval != PAM_SUCCESS) { - fatal("PAM failure: pam_get_item(PAM_USER) " - "returned %d: %.200s", retval, - PAM_STRERROR(authctxt->pam->h, retval)); - } - - if (user == NULL || *user == '\0') { - debug("PAM set NULL PAM_USER"); - return PAM_PERM_DENIED; - } - - if (strcmp(user, authctxt->user) != 0) { - log("PAM changed the SSH username"); - pwfree(&authctxt->pw); - authctxt->pw = getpwnamallow(user); - authctxt->valid = (authctxt->pw != NULL); - xfree(authctxt->user); - authctxt->user = xstrdup(user); - } - - if (!authctxt->valid) { - debug2("PAM set PAM_USER to unknown user"); - /* - * Return success, userauth_finish() will catch - * this and send back a failure message. - */ - return PAM_SUCCESS; - } - - /* Check PermitRootLogin semantics */ - if (authctxt->pw->pw_uid == 0 && !auth_root_allowed(method)) - return PAM_PERM_DENIED; - - if (!(authctxt->pam->state & PAM_S_DONE_SETCRED)) { - retval = pam_setcred(authctxt->pam->h, - PAM_ESTABLISH_CRED); - authctxt->pam->last_pam_retval = retval; - if (retval != PAM_SUCCESS) - return retval; - authctxt->pam->state |= PAM_S_DONE_SETCRED; - -#ifdef GSSAPI - /* - * Store GSS-API delegated creds after pam_setcred(), which may - * have set the current credential store. - */ - ssh_gssapi_storecreds(NULL, authctxt); -#endif /* GSSAPI */ - } - - /* - * On Solaris pam_unix_session.so updates the lastlog, but does - * not converse a PAM_TEXT_INFO message about it. So we need to - * fetch the lastlog entry here and save it for use later. - */ - authctxt->last_login_time = - get_last_login_time(authctxt->pw->pw_uid, - authctxt->pw->pw_name, - authctxt->last_login_host, - sizeof(authctxt->last_login_host)); - - if (!(authctxt->pam->state & PAM_S_DONE_OPEN_SESSION)) { - retval = pam_open_session(authctxt->pam->h, 0); - authctxt->pam->last_pam_retval = retval; - if (retval != PAM_SUCCESS) - return retval; - authctxt->pam->state |= PAM_S_DONE_OPEN_SESSION; - } - - /* - * All PAM work done successfully. - * - * PAM handle stays around so we can call pam_close_session() on - * it later. - */ - return PAM_SUCCESS; -} - -/* - * PAM conversation function for non-interactive userauth methods that - * really cannot do any prompting. Password userauth and CHANGEREQ can - * always set the PAM_AUTHTOK and PAM_OLDAUTHTOK items to avoid - * conversation (and if they do and nonetheless some module tries to - * converse, then password userauth / CHANGEREQ MUST fail). - * - * Except, PAM_TEXT_INFO and PAM_ERROR_MSG prompts can be squirelled - * away and shown to the user later. - * - * Keyboard-interactive userauth has its own much more interesting - * conversation function. - * - */ -static int -do_pam_conversation(int num_msg, const struct pam_message **msg, - struct pam_response **resp, void *appdata_ptr) -{ - struct pam_response *reply; - int count; - - /* PAM will free this later */ - reply = xmalloc(num_msg * sizeof(*reply)); - - (void) memset(reply, 0, num_msg * sizeof(*reply)); - - for (count = 0; count < num_msg; count++) { - /* - * We can't use stdio yet, queue messages for - * printing later - */ - switch(PAM_MSG_MEMBER(msg, count, msg_style)) { - case PAM_PROMPT_ECHO_ON: - xfree(reply); - return PAM_CONV_ERR; - case PAM_PROMPT_ECHO_OFF: - xfree(reply); - return PAM_CONV_ERR; - break; - case PAM_ERROR_MSG: - case PAM_TEXT_INFO: - if (PAM_MSG_MEMBER(msg, count, msg) != NULL) { - message_cat(&__pam_msg, - PAM_MSG_MEMBER(msg, count, msg)); - } - reply[count].resp = xstrdup(""); - reply[count].resp_retcode = PAM_SUCCESS; - break; - default: - xfree(reply); - return PAM_CONV_ERR; - } - } - - *resp = reply; - - return PAM_SUCCESS; -} - -/* Called at exit to cleanly shutdown PAM */ -static void -do_pam_cleanup_proc(void *context) -{ - int pam_retval; - pam_stuff *pam = (pam_stuff *) context; - - if (pam == NULL) - return; - - if (pam->authctxt != NULL && pam->authctxt->pam == pam) { - pam->authctxt->pam_retval = pam->last_pam_retval; - pam->authctxt->pam = NULL; - pam->authctxt = NULL; - } - - if (pam->h == NULL) - return; - - /* - * We're in fatal_cleanup() or not in userauth or without a - * channel -- can't converse now, too bad. - */ - pam_retval = pam_set_item(pam->h, PAM_CONV, NULL); - if (pam_retval != PAM_SUCCESS) { - log("Cannot remove PAM conv, close session or delete creds[%d]: %.200s", - pam_retval, PAM_STRERROR(pam->h, pam_retval)); - goto cleanup; - } - - if (pam->state & PAM_S_DONE_OPEN_SESSION) { - pam_retval = pam_close_session(pam->h, 0); - if (pam_retval != PAM_SUCCESS) - log("Cannot close PAM session[%d]: %.200s", - pam_retval, PAM_STRERROR(pam->h, pam_retval)); - } - - if (pam->state & PAM_S_DONE_SETCRED) { - pam_retval = pam_setcred(pam->h, PAM_DELETE_CRED); - if (pam_retval != PAM_SUCCESS) - debug("Cannot delete credentials[%d]: %.200s", - pam_retval, PAM_STRERROR(pam->h, pam_retval)); - } - -cleanup: - - /* Use the previous PAM result, if not PAM_SUCCESS for pam_end() */ - if (pam->last_pam_retval != PAM_SUCCESS) - pam_retval = pam_end(pam->h, pam->last_pam_retval); - else if (pam_retval != PAM_SUCCESS) - pam_retval = pam_end(pam->h, pam_retval); - else - pam_retval = pam_end(pam->h, PAM_ABORT); - - if (pam_retval != PAM_SUCCESS) - log("Cannot release PAM authentication[%d]: %.200s", - pam_retval, PAM_STRERROR(pam->h, pam_retval)); - - xfree(pam); -} - -/* Attempt password authentation using PAM */ -int -auth_pam_password(Authctxt *authctxt, const char *password) -{ - int retval; - - /* Ensure we have a fresh PAM handle / state */ - new_start_pam(authctxt, &conv); - - retval = pam_set_item(authctxt->pam->h, PAM_AUTHTOK, password); - if (retval != PAM_SUCCESS) { - authctxt->pam->last_pam_retval = retval; - return 1; - } - - retval = pam_authenticate(authctxt->pam->h, - options.permit_empty_passwd ? 0 : - PAM_DISALLOW_NULL_AUTHTOK); - - if (retval != PAM_SUCCESS) { - authctxt->pam->last_pam_retval = retval; - return 0; - } - - if ((retval = finish_userauth_do_pam(authctxt)) != PAM_SUCCESS) - return 0; - - if (authctxt->method) - authctxt->method->authenticated = 1; /* SSHv2 */ - - return 1; -} - -int -do_pam_non_initial_userauth(Authctxt *authctxt) -{ - new_start_pam(authctxt, NULL); - return (finish_userauth_do_pam(authctxt) == PAM_SUCCESS); -} - -/* Cleanly shutdown PAM */ -void finish_pam(Authctxt *authctxt) -{ - fatal_remove_cleanup(&do_pam_cleanup_proc, authctxt->pam); - do_pam_cleanup_proc(authctxt->pam); -} - -static -char ** -find_env(char **env, char *var) -{ - char **p; - int len; - - if (strchr(var, '=') == NULL) - len = strlen(var); - else - len = (strchr(var, '=') - var) + 1; - - for ( p = env ; p != NULL && *p != NULL ; p++ ) { - if (strncmp(*p, var, len) == 0) - return (p); - } - - return (NULL); -} - -/* Return list of PAM environment strings */ -char ** -fetch_pam_environment(Authctxt *authctxt) -{ -#ifdef HAVE_PAM_GETENVLIST - char **penv; - - if (authctxt == NULL || authctxt->pam == NULL || - authctxt->pam->h == NULL) - return (NULL); - - penv = pam_getenvlist(authctxt->pam->h); - - return (penv); -#else /* HAVE_PAM_GETENVLIST */ - return(NULL); -#endif /* HAVE_PAM_GETENVLIST */ -} - -void free_pam_environment(char **env) -{ - int i; - - if (env != NULL) { - for (i = 0; env[i] != NULL; i++) - xfree(env[i]); - } - - xfree(env); -} - -/* Print any messages that have been generated during authentication */ -/* or account checking to stderr */ -void print_pam_messages(void) -{ - if (__pam_msg != NULL) - (void) fputs(__pam_msg, stderr); -} - -/* Append a message to buffer */ -void message_cat(char **p, const char *a) -{ - char *cp; - size_t new_len; - - new_len = strlen(a); - - if (*p) { - size_t len = strlen(*p); - - *p = xrealloc(*p, new_len + len + 2); - cp = *p + len; - } else - *p = cp = xmalloc(new_len + 2); - - (void) memcpy(cp, a, new_len); - cp[new_len] = '\n'; - cp[new_len + 1] = '\0'; -} - -#endif /* USE_PAM */ diff --git a/usr/src/cmd/ssh/sshd/auth-passwd.c b/usr/src/cmd/ssh/sshd/auth-passwd.c deleted file mode 100644 index 815231d4d4..0000000000 --- a/usr/src/cmd/ssh/sshd/auth-passwd.c +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Author: Tatu Ylonen <ylo@cs.hut.fi> - * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland - * All rights reserved - * Password authentication. This file contains the functions to check whether - * the password is valid for the user. - * - * As far as I am concerned, the code I have written for this software - * can be used freely for any purpose. Any derived versions of this - * software must be clearly marked as such, and if the derived work is - * incompatible with the protocol description in the RFC file, it must be - * called by a name other than "ssh" or "Secure Shell". - * - * Copyright (c) 1999 Dug Song. All rights reserved. - * Copyright (c) 2000 Markus Friedl. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "includes.h" -RCSID("$OpenBSD: auth-passwd.c,v 1.27 2002/05/24 16:45:16 stevesk Exp $"); - -#include "packet.h" -#include "log.h" -#include "servconf.h" -#include "auth.h" - -#if !defined(USE_PAM) && !defined(HAVE_OSF_SIA) -/* Don't need any of these headers for the PAM or SIA cases */ -# ifdef HAVE_CRYPT_H -# include <crypt.h> -# endif -# ifdef WITH_AIXAUTHENTICATE -# include <login.h> -# endif -# ifdef __hpux -# include <hpsecurity.h> -# include <prot.h> -# endif -# if defined(HAVE_SHADOW_H) && !defined(DISABLE_SHADOW) -# include <shadow.h> -# endif -# if defined(HAVE_GETPWANAM) && !defined(DISABLE_SHADOW) -# include <sys/label.h> -# include <sys/audit.h> -# include <pwdadj.h> -# endif -# if defined(HAVE_MD5_PASSWORDS) && !defined(HAVE_MD5_CRYPT) -# include "md5crypt.h" -# endif /* defined(HAVE_MD5_PASSWORDS) && !defined(HAVE_MD5_CRYPT) */ - -# ifdef HAVE_CYGWIN -# undef ERROR -# include <windows.h> -# include <sys/cygwin.h> -# define is_winnt (GetVersion() < 0x80000000) -# endif -#endif /* !USE_PAM && !HAVE_OSF_SIA */ - -extern ServerOptions options; -#ifdef WITH_AIXAUTHENTICATE -extern char *aixloginmsg; -#endif - -/* - * Tries to authenticate the user using password. Returns true if - * authentication succeeds. - */ -int -auth_password(Authctxt *authctxt, const char *password) -{ -#if defined(USE_PAM) - if (*password == '\0' && options.permit_empty_passwd == 0) - return 0; - return auth_pam_password(authctxt, password); -#elif defined(HAVE_OSF_SIA) - if (*password == '\0' && options.permit_empty_passwd == 0) - return 0; - return auth_sia_password(authctxt, password); -#else - struct passwd * pw = authctxt->pw; - char *encrypted_password; - char *pw_password; - char *salt; -#if defined(HAVE_SHADOW_H) && !defined(DISABLE_SHADOW) - struct spwd *spw; -#endif -#if defined(HAVE_GETPWANAM) && !defined(DISABLE_SHADOW) - struct passwd_adjunct *spw; -#endif -#ifdef WITH_AIXAUTHENTICATE - char *authmsg; - int authsuccess; - int reenter = 1; -#endif - - /* deny if no user. */ - if (pw == NULL) - return 0; -#ifndef HAVE_CYGWIN - if (pw->pw_uid == 0 && options.permit_root_login != PERMIT_YES) - return 0; -#endif - if (*password == '\0' && options.permit_empty_passwd == 0) - return 0; -#ifdef KRB5 - if (options.kerberos_authentication == 1) { - int ret = auth_krb5_password(authctxt, password); - if (ret == 1 || ret == 0) - return ret; - /* Fall back to ordinary passwd authentication. */ - } -#endif -#ifdef HAVE_CYGWIN - if (is_winnt) { - HANDLE hToken = cygwin_logon_user(pw, password); - - if (hToken == INVALID_HANDLE_VALUE) - return 0; - cygwin_set_impersonation_token(hToken); - return 1; - } -#endif -#ifdef WITH_AIXAUTHENTICATE - authsuccess = (authenticate(pw->pw_name,password,&reenter,&authmsg) == 0); - - if (authsuccess) - /* We don't have a pty yet, so just label the line as "ssh" */ - if (loginsuccess(authctxt->user, - get_canonical_hostname(options.verify_reverse_mapping), - "ssh", &aixloginmsg) < 0) - aixloginmsg = NULL; - - return(authsuccess); -#endif -#ifdef KRB4 - if (options.kerberos_authentication == 1) { - int ret = auth_krb4_password(authctxt, password); - if (ret == 1 || ret == 0) - return ret; - /* Fall back to ordinary passwd authentication. */ - } -#endif -#ifdef BSD_AUTH - if (auth_userokay(pw->pw_name, authctxt->style, "auth-ssh", - (char *)password) == 0) - return 0; - else - return 1; -#endif - pw_password = pw->pw_passwd; - - /* - * Various interfaces to shadow or protected password data - */ -#if defined(HAVE_SHADOW_H) && !defined(DISABLE_SHADOW) - spw = getspnam(pw->pw_name); - if (spw != NULL) - pw_password = spw->sp_pwdp; -#endif /* defined(HAVE_SHADOW_H) && !defined(DISABLE_SHADOW) */ - -#if defined(HAVE_GETPWANAM) && !defined(DISABLE_SHADOW) - if (issecure() && (spw = getpwanam(pw->pw_name)) != NULL) - pw_password = spw->pwa_passwd; -#endif /* defined(HAVE_GETPWANAM) && !defined(DISABLE_SHADOW) */ - - /* Check for users with no password. */ - if ((password[0] == '\0') && (pw_password[0] == '\0')) - return 1; - - if (pw_password[0] != '\0') - salt = pw_password; - else - salt = "xx"; - -#ifdef HAVE_MD5_PASSWORDS - if (is_md5_salt(salt)) - encrypted_password = md5_crypt(password, salt); - else - encrypted_password = crypt(password, salt); -#else /* HAVE_MD5_PASSWORDS */ - encrypted_password = crypt(password, salt); -#endif /* HAVE_MD5_PASSWORDS */ - - /* Authentication is accepted if the encrypted passwords are identical. */ - return (strcmp(encrypted_password, pw_password) == 0); -#endif /* !USE_PAM && !HAVE_OSF_SIA */ -} diff --git a/usr/src/cmd/ssh/sshd/auth-rh-rsa.c b/usr/src/cmd/ssh/sshd/auth-rh-rsa.c deleted file mode 100644 index ab10e1738a..0000000000 --- a/usr/src/cmd/ssh/sshd/auth-rh-rsa.c +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Author: Tatu Ylonen <ylo@cs.hut.fi> - * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland - * All rights reserved - * Rhosts or /etc/hosts.equiv authentication combined with RSA host - * authentication. - * - * As far as I am concerned, the code I have written for this software - * can be used freely for any purpose. Any derived versions of this - * software must be clearly marked as such, and if the derived work is - * incompatible with the protocol description in the RFC file, it must be - * called by a name other than "ssh" or "Secure Shell". - */ - -#include "includes.h" -RCSID("$OpenBSD: auth-rh-rsa.c,v 1.34 2002/03/25 09:25:06 markus Exp $"); - -#pragma ident "%Z%%M% %I% %E% SMI" - -#include "packet.h" -#include "uidswap.h" -#include "log.h" -#include "servconf.h" -#include "key.h" -#include "hostfile.h" -#include "pathnames.h" -#include "auth.h" -#include "canohost.h" - -/* import */ -extern ServerOptions options; - -int -auth_rhosts_rsa_key_allowed(struct passwd *pw, char *cuser, char *chost, - Key *client_host_key) -{ - HostStatus host_status; - - /* Check if we would accept it using rhosts authentication. */ - if (!auth_rhosts(pw, cuser)) - return 0; - - host_status = check_key_in_hostfiles(pw, client_host_key, - chost, _PATH_SSH_SYSTEM_HOSTFILE, - options.ignore_user_known_hosts ? NULL : _PATH_SSH_USER_HOSTFILE); - - return (host_status == HOST_OK); -} - -/* - * Tries to authenticate the user using the .rhosts file and the host using - * its host key. Returns true if authentication succeeds. - */ -int -auth_rhosts_rsa(struct passwd *pw, char *cuser, Key *client_host_key) -{ - char *chost; - - debug("Trying rhosts with RSA host authentication for client user %.100s", - cuser); - - if (pw == NULL || client_host_key == NULL || - client_host_key->rsa == NULL) - return 0; - - chost = (char *)get_canonical_hostname(options.verify_reverse_mapping); - debug("Rhosts RSA authentication: canonical host %.900s", chost); - - if (!auth_rhosts_rsa_key_allowed(pw, cuser, chost, client_host_key)) { - debug("Rhosts with RSA host authentication denied: unknown or invalid host key"); - packet_send_debug("Your host key cannot be verified: unknown or invalid host key."); - return 0; - } - /* A matching host key was found and is known. */ - - /* Perform the challenge-response dialog with the client for the host key. */ - if (!auth_rsa_challenge_dialog(client_host_key)) { - log("Client on %.800s failed to respond correctly to host authentication.", - chost); - return 0; - } - /* - * We have authenticated the user using .rhosts or /etc/hosts.equiv, - * and the host using RSA. We accept the authentication. - */ - - verbose("Rhosts with RSA host authentication accepted for %.100s, %.100s on %.700s.", - pw->pw_name, cuser, chost); - packet_send_debug("Rhosts with RSA host authentication accepted."); - return 1; -} diff --git a/usr/src/cmd/ssh/sshd/auth-rhosts.c b/usr/src/cmd/ssh/sshd/auth-rhosts.c deleted file mode 100644 index 2326eef8ae..0000000000 --- a/usr/src/cmd/ssh/sshd/auth-rhosts.c +++ /dev/null @@ -1,299 +0,0 @@ -/* - * Author: Tatu Ylonen <ylo@cs.hut.fi> - * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland - * All rights reserved - * Rhosts authentication. This file contains code to check whether to admit - * the login based on rhosts authentication. This file also processes - * /etc/hosts.equiv. - * - * As far as I am concerned, the code I have written for this software - * can be used freely for any purpose. Any derived versions of this - * software must be clearly marked as such, and if the derived work is - * incompatible with the protocol description in the RFC file, it must be - * called by a name other than "ssh" or "Secure Shell". - */ - -#include "includes.h" -RCSID("$OpenBSD: auth-rhosts.c,v 1.28 2002/05/13 21:26:49 markus Exp $"); - -#pragma ident "%Z%%M% %I% %E% SMI" - -#include "packet.h" -#include "uidswap.h" -#include "pathnames.h" -#include "log.h" -#include "servconf.h" -#include "canohost.h" -#include "auth.h" - -/* import */ -extern ServerOptions options; - -/* - * This function processes an rhosts-style file (.rhosts, .shosts, or - * /etc/hosts.equiv). This returns true if authentication can be granted - * based on the file, and returns zero otherwise. - */ - -static int -check_rhosts_file(const char *filename, const char *hostname, - const char *ipaddr, const char *client_user, - const char *server_user) -{ - FILE *f; - char buf[1024]; /* Must not be larger than host, user, dummy below. */ - - /* Open the .rhosts file, deny if unreadable */ - f = fopen(filename, "r"); - if (!f) - return 0; - - while (fgets(buf, sizeof(buf), f)) { - /* All three must be at least as big as buf to avoid overflows. */ - char hostbuf[1024], userbuf[1024], dummy[1024], *host, *user, *cp; - int negated; - - for (cp = buf; *cp == ' ' || *cp == '\t'; cp++) - ; - if (*cp == '#' || *cp == '\n' || !*cp) - continue; - - /* - * NO_PLUS is supported at least on OSF/1. We skip it (we - * don't ever support the plus syntax). - */ - if (strncmp(cp, "NO_PLUS", 7) == 0) - continue; - - /* - * This should be safe because each buffer is as big as the - * whole string, and thus cannot be overwritten. - */ - switch (sscanf(buf, "%1024s %1024s %1024s", - hostbuf, userbuf, dummy)) { - case 0: - auth_debug_add("Found empty line in %.100s.", filename); - continue; - case 1: - /* Host name only. */ - strlcpy(userbuf, server_user, sizeof(userbuf)); - break; - case 2: - /* Got both host and user name. */ - break; - case 3: - auth_debug_add("Found garbage in %.100s.", filename); - continue; - default: - /* Weird... */ - continue; - } - - host = hostbuf; - user = userbuf; - negated = 0; - - /* Process negated host names, or positive netgroups. */ - if (host[0] == '-') { - negated = 1; - host++; - } else if (host[0] == '+') - host++; - - if (user[0] == '-') { - negated = 1; - user++; - } else if (user[0] == '+') - user++; - - /* Check for empty host/user names (particularly '+'). */ - if (!host[0] || !user[0]) { - /* We come here if either was '+' or '-'. */ - auth_debug_add("Ignoring wild host/user names in %.100s.", - filename); - continue; - } - /* Verify that host name matches. */ - if (host[0] == '@') { - if (!innetgr(host + 1, hostname, NULL, NULL) && - !innetgr(host + 1, ipaddr, NULL, NULL)) - continue; - } else if (strcasecmp(host, hostname) && strcmp(host, ipaddr) != 0) - continue; /* Different hostname. */ - - /* Verify that user name matches. */ - if (user[0] == '@') { - if (!innetgr(user + 1, NULL, client_user, NULL)) - continue; - } else if (strcmp(user, client_user) != 0) - continue; /* Different username. */ - - /* Found the user and host. */ - fclose(f); - - /* If the entry was negated, deny access. */ - if (negated) { - auth_debug_add("Matched negative entry in %.100s.", - filename); - return 0; - } - /* Accept authentication. */ - return 1; - } - - /* Authentication using this file denied. */ - fclose(f); - return 0; -} - -/* - * Tries to authenticate the user using the .shosts or .rhosts file. Returns - * true if authentication succeeds. If ignore_rhosts is true, only - * /etc/hosts.equiv will be considered (.rhosts and .shosts are ignored). - */ - -int -auth_rhosts(struct passwd *pw, const char *client_user) -{ - const char *hostname, *ipaddr; - - hostname = get_canonical_hostname(options.verify_reverse_mapping); - ipaddr = get_remote_ipaddr(); - return auth_rhosts2(pw, client_user, hostname, ipaddr); -} - -static int -auth_rhosts2_raw(struct passwd *pw, const char *client_user, const char *hostname, - const char *ipaddr) -{ - char buf[1024]; - struct stat st; - static const char *rhosts_files[] = {".shosts", ".rhosts", NULL}; - u_int rhosts_file_index; - - debug2("auth_rhosts2: clientuser %s hostname %s ipaddr %s", - client_user, hostname, ipaddr); - - /* no user given */ - if (pw == NULL) - return 0; - - /* Switch to the user's uid. */ - temporarily_use_uid(pw); - /* - * Quick check: if the user has no .shosts or .rhosts files, return - * failure immediately without doing costly lookups from name - * servers. - */ - for (rhosts_file_index = 0; rhosts_files[rhosts_file_index]; - rhosts_file_index++) { - /* Check users .rhosts or .shosts. */ - snprintf(buf, sizeof buf, "%.500s/%.100s", - pw->pw_dir, rhosts_files[rhosts_file_index]); - if (stat(buf, &st) >= 0) - break; - } - /* Switch back to privileged uid. */ - restore_uid(); - - /* Deny if The user has no .shosts or .rhosts file and there are no system-wide files. */ - if (!rhosts_files[rhosts_file_index] && - stat(_PATH_RHOSTS_EQUIV, &st) < 0 && - stat(_PATH_SSH_HOSTS_EQUIV, &st) < 0) - return 0; - - /* If not logging in as superuser, try /etc/hosts.equiv and shosts.equiv. */ - if (pw->pw_uid != 0) { - if (check_rhosts_file(_PATH_RHOSTS_EQUIV, hostname, ipaddr, - client_user, pw->pw_name)) { - auth_debug_add("Accepted for %.100s [%.100s] by /etc/hosts.equiv.", - hostname, ipaddr); - return 1; - } - if (check_rhosts_file(_PATH_SSH_HOSTS_EQUIV, hostname, ipaddr, - client_user, pw->pw_name)) { - auth_debug_add("Accepted for %.100s [%.100s] by %.100s.", - hostname, ipaddr, _PATH_SSH_HOSTS_EQUIV); - return 1; - } - } - /* - * Check that the home directory is owned by root or the user, and is - * not group or world writable. - */ - if (stat(pw->pw_dir, &st) < 0) { - log("Rhosts authentication refused for %.100s: " - "no home directory %.200s", pw->pw_name, pw->pw_dir); - auth_debug_add("Rhosts authentication refused for %.100s: " - "no home directory %.200s", pw->pw_name, pw->pw_dir); - return 0; - } - if (options.strict_modes && - ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || - (st.st_mode & 022) != 0)) { - log("Rhosts authentication refused for %.100s: " - "bad ownership or modes for home directory.", pw->pw_name); - auth_debug_add("Rhosts authentication refused for %.100s: " - "bad ownership or modes for home directory.", pw->pw_name); - return 0; - } - /* Temporarily use the user's uid. */ - temporarily_use_uid(pw); - - /* Check all .rhosts files (currently .shosts and .rhosts). */ - for (rhosts_file_index = 0; rhosts_files[rhosts_file_index]; - rhosts_file_index++) { - /* Check users .rhosts or .shosts. */ - snprintf(buf, sizeof buf, "%.500s/%.100s", - pw->pw_dir, rhosts_files[rhosts_file_index]); - if (stat(buf, &st) < 0) - continue; - - /* - * Make sure that the file is either owned by the user or by - * root, and make sure it is not writable by anyone but the - * owner. This is to help avoid novices accidentally - * allowing access to their account by anyone. - */ - if (options.strict_modes && - ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || - (st.st_mode & 022) != 0)) { - log("Rhosts authentication refused for %.100s: bad modes for %.200s", - pw->pw_name, buf); - auth_debug_add("Bad file modes for %.200s", buf); - continue; - } - /* Check if we have been configured to ignore .rhosts and .shosts files. */ - if (options.ignore_rhosts) { - auth_debug_add("Server has been configured to ignore %.100s.", - rhosts_files[rhosts_file_index]); - continue; - } - /* Check if authentication is permitted by the file. */ - if (check_rhosts_file(buf, hostname, ipaddr, client_user, pw->pw_name)) { - auth_debug_add("Accepted by %.100s.", - rhosts_files[rhosts_file_index]); - /* Restore the privileged uid. */ - restore_uid(); - auth_debug_add("Accepted host %s ip %s client_user %s server_user %s", - hostname, ipaddr, client_user, pw->pw_name); - return 1; - } - } - - /* Restore the privileged uid. */ - restore_uid(); - return 0; -} - -int -auth_rhosts2(struct passwd *pw, const char *client_user, const char *hostname, - const char *ipaddr) -{ - int ret; - - auth_debug_reset(); - ret = auth_rhosts2_raw(pw, client_user, hostname, ipaddr); - auth_debug_send(); - return ret; -} diff --git a/usr/src/cmd/ssh/sshd/auth-rsa.c b/usr/src/cmd/ssh/sshd/auth-rsa.c deleted file mode 100644 index 3e0e6ea50d..0000000000 --- a/usr/src/cmd/ssh/sshd/auth-rsa.c +++ /dev/null @@ -1,328 +0,0 @@ -/* - * Author: Tatu Ylonen <ylo@cs.hut.fi> - * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland - * All rights reserved - * RSA-based authentication. This code determines whether to admit a login - * based on RSA authentication. This file also contains functions to check - * validity of the host key. - * - * As far as I am concerned, the code I have written for this software - * can be used freely for any purpose. Any derived versions of this - * software must be clearly marked as such, and if the derived work is - * incompatible with the protocol description in the RFC file, it must be - * called by a name other than "ssh" or "Secure Shell". - */ - -#include "includes.h" -RCSID("$OpenBSD: auth-rsa.c,v 1.56 2002/06/10 16:53:06 stevesk Exp $"); - -#pragma ident "%Z%%M% %I% %E% SMI" - -#include <openssl/rsa.h> -#include <openssl/md5.h> - -#include "rsa.h" -#include "packet.h" -#include "xmalloc.h" -#include "ssh1.h" -#include "mpaux.h" -#include "uidswap.h" -#include "match.h" -#include "auth-options.h" -#include "pathnames.h" -#include "log.h" -#include "servconf.h" -#include "auth.h" -#include "hostfile.h" -#include "ssh.h" - -/* import */ -extern ServerOptions options; - -/* - * Session identifier that is used to bind key exchange and authentication - * responses to a particular session. - */ -extern u_char session_id[16]; - -/* - * The .ssh/authorized_keys file contains public keys, one per line, in the - * following format: - * options bits e n comment - * where bits, e and n are decimal numbers, - * and comment is any string of characters up to newline. The maximum - * length of a line is 8000 characters. See the documentation for a - * description of the options. - */ - -BIGNUM * -auth_rsa_generate_challenge(Key *key) -{ - BIGNUM *challenge; - BN_CTX *ctx; - - if ((challenge = BN_new()) == NULL) - fatal("auth_rsa_generate_challenge: BN_new() failed"); - /* Generate a random challenge. */ - BN_rand(challenge, 256, 0, 0); - if ((ctx = BN_CTX_new()) == NULL) - fatal("auth_rsa_generate_challenge: BN_CTX_new() failed"); - BN_mod(challenge, challenge, key->rsa->n, ctx); - BN_CTX_free(ctx); - - return challenge; -} - -int -auth_rsa_verify_response(Key *key, BIGNUM *challenge, u_char response[16]) -{ - u_char buf[32], mdbuf[16]; - MD5_CTX md; - int len; - - /* don't allow short keys */ - if (BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) { - error("auth_rsa_verify_response: RSA modulus too small: %d < minimum %d bits", - BN_num_bits(key->rsa->n), SSH_RSA_MINIMUM_MODULUS_SIZE); - return (0); - } - - /* The response is MD5 of decrypted challenge plus session id. */ - len = BN_num_bytes(challenge); - if (len <= 0 || len > 32) - fatal("auth_rsa_verify_response: bad challenge length %d", len); - memset(buf, 0, 32); - BN_bn2bin(challenge, buf + 32 - len); - MD5_Init(&md); - MD5_Update(&md, buf, 32); - MD5_Update(&md, session_id, 16); - MD5_Final(mdbuf, &md); - - /* Verify that the response is the original challenge. */ - if (memcmp(response, mdbuf, 16) != 0) { - /* Wrong answer. */ - return (0); - } - /* Correct answer. */ - return (1); -} - -/* - * Performs the RSA authentication challenge-response dialog with the client, - * and returns true (non-zero) if the client gave the correct answer to - * our challenge; returns zero if the client gives a wrong answer. - */ - -int -auth_rsa_challenge_dialog(Key *key) -{ - BIGNUM *challenge, *encrypted_challenge; - u_char response[16]; - int i, success; - - if ((encrypted_challenge = BN_new()) == NULL) - fatal("auth_rsa_challenge_dialog: BN_new() failed"); - - challenge = auth_rsa_generate_challenge(key); - - /* Encrypt the challenge with the public key. */ - rsa_public_encrypt(encrypted_challenge, challenge, key->rsa); - - /* Send the encrypted challenge to the client. */ - packet_start(SSH_SMSG_AUTH_RSA_CHALLENGE); - packet_put_bignum(encrypted_challenge); - packet_send(); - BN_clear_free(encrypted_challenge); - packet_write_wait(); - - /* Wait for a response. */ - packet_read_expect(SSH_CMSG_AUTH_RSA_RESPONSE); - for (i = 0; i < 16; i++) - response[i] = packet_get_char(); - packet_check_eom(); - - success = auth_rsa_verify_response(key, challenge, response); - BN_clear_free(challenge); - return (success); -} - -/* - * check if there's user key matching client_n, - * return key if login is allowed, NULL otherwise - */ - -int -auth_rsa_key_allowed(struct passwd *pw, BIGNUM *client_n, Key **rkey) -{ - char line[8192], *file; - int allowed = 0; - u_int bits; - FILE *f; - u_long linenum = 0; - struct stat st; - Key *key; - - /* Temporarily use the user's uid. */ - temporarily_use_uid(pw); - - /* The authorized keys. */ - file = authorized_keys_file(pw); - debug("trying public RSA key file %s", file); - - /* Fail quietly if file does not exist */ - if (stat(file, &st) < 0) { - /* Restore the privileged uid. */ - restore_uid(); - xfree(file); - return (0); - } - /* Open the file containing the authorized keys. */ - f = fopen(file, "r"); - if (!f) { - /* Restore the privileged uid. */ - restore_uid(); - xfree(file); - return (0); - } - if (options.strict_modes && - secure_filename(f, file, pw, line, sizeof(line)) != 0) { - xfree(file); - fclose(f); - log("Authentication refused: %s", line); - restore_uid(); - return (0); - } - - /* Flag indicating whether the key is allowed. */ - allowed = 0; - - key = key_new(KEY_RSA1); - - /* - * Go though the accepted keys, looking for the current key. If - * found, perform a challenge-response dialog to verify that the - * user really has the corresponding private key. - */ - while (fgets(line, sizeof(line), f)) { - char *cp; - char *options; - - linenum++; - - /* Skip leading whitespace, empty and comment lines. */ - for (cp = line; *cp == ' ' || *cp == '\t'; cp++) - ; - if (!*cp || *cp == '\n' || *cp == '#') - continue; - - /* - * Check if there are options for this key, and if so, - * save their starting address and skip the option part - * for now. If there are no options, set the starting - * address to NULL. - */ - if (*cp < '0' || *cp > '9') { - int quoted = 0; - options = cp; - for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) { - if (*cp == '\\' && cp[1] == '"') - cp++; /* Skip both */ - else if (*cp == '"') - quoted = !quoted; - } - } else - options = NULL; - - /* Parse the key from the line. */ - if (hostfile_read_key(&cp, &bits, key) == 0) { - debug("%.100s, line %lu: non ssh1 key syntax", - file, linenum); - continue; - } - /* cp now points to the comment part. */ - - /* Check if the we have found the desired key (identified by its modulus). */ - if (BN_cmp(key->rsa->n, client_n) != 0) - continue; - - /* check the real bits */ - if (bits != BN_num_bits(key->rsa->n)) - log("Warning: %s, line %lu: keysize mismatch: " - "actual %d vs. announced %d.", - file, linenum, BN_num_bits(key->rsa->n), bits); - - /* We have found the desired key. */ - /* - * If our options do not allow this key to be used, - * do not send challenge. - */ - if (!auth_parse_options(pw, options, file, linenum)) - continue; - - /* break out, this key is allowed */ - allowed = 1; - break; - } - - /* Restore the privileged uid. */ - restore_uid(); - - /* Close the file. */ - xfree(file); - fclose(f); - - /* return key if allowed */ - if (allowed && rkey != NULL) - *rkey = key; - else - key_free(key); - return (allowed); -} - -/* - * Performs the RSA authentication dialog with the client. This returns - * 0 if the client could not be authenticated, and 1 if authentication was - * successful. This may exit if there is a serious protocol violation. - */ -int -auth_rsa(struct passwd *pw, BIGNUM *client_n) -{ - Key *key; - char *fp; - - /* no user given */ - if (pw == NULL) - return 0; - - if (!auth_rsa_key_allowed(pw, client_n, &key)) { - auth_clear_options(); - return (0); - } - - /* Perform the challenge-response dialog for this key. */ - if (!auth_rsa_challenge_dialog(key)) { - /* Wrong response. */ - verbose("Wrong response to RSA authentication challenge."); - packet_send_debug("Wrong response to RSA authentication challenge."); - /* - * Break out of the loop. Otherwise we might send - * another challenge and break the protocol. - */ - key_free(key); - return (0); - } - /* - * Correct response. The client has been successfully - * authenticated. Note that we have not yet processed the - * options; this will be reset if the options cause the - * authentication to be rejected. - */ - fp = key_fingerprint(key, SSH_FP_MD5, SSH_FP_HEX); - verbose("Found matching %s key: %s", - key_type(key), fp); - xfree(fp); - key_free(key); - - packet_send_debug("RSA authentication accepted."); - return (1); -} diff --git a/usr/src/cmd/ssh/sshd/auth-sia.c b/usr/src/cmd/ssh/sshd/auth-sia.c deleted file mode 100644 index 6afa02cf75..0000000000 --- a/usr/src/cmd/ssh/sshd/auth-sia.c +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (c) 2002 Chris Adams. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "includes.h" - -#ifdef HAVE_OSF_SIA -#include "ssh.h" -#include "auth.h" -#include "auth-sia.h" -#include "log.h" -#include "servconf.h" -#include "canohost.h" - -#include <sia.h> -#include <siad.h> -#include <pwd.h> -#include <signal.h> -#include <setjmp.h> -#include <sys/resource.h> -#include <unistd.h> -#include <string.h> - -extern ServerOptions options; -extern int saved_argc; -extern char **saved_argv; - -extern int errno; - -int -auth_sia_password(Authctxt *authctxt, char *pass) -{ - int ret; - SIAENTITY *ent = NULL; - const char *host; - char *user = authctxt->user; - - host = get_canonical_hostname(options.verify_reverse_mapping); - - if (!user || !pass || pass[0] == '\0') - return(0); - - if (sia_ses_init(&ent, saved_argc, saved_argv, host, user, NULL, 0, - NULL) != SIASUCCESS) - return(0); - - if ((ret = sia_ses_authent(NULL, pass, ent)) != SIASUCCESS) { - error("Couldn't authenticate %s from %s", user, host); - if (ret & SIASTOP) - sia_ses_release(&ent); - return(0); - } - - sia_ses_release(&ent); - - return(1); -} - -void -session_setup_sia(char *user, char *tty) -{ - struct passwd *pw; - SIAENTITY *ent = NULL; - const char *host; - - host = get_canonical_hostname (options.verify_reverse_mapping); - - if (sia_ses_init(&ent, saved_argc, saved_argv, host, user, tty, 0, - NULL) != SIASUCCESS) { - fatal("sia_ses_init failed"); - } - - if ((pw = getpwnam(user)) == NULL) { - sia_ses_release(&ent); - fatal("getpwnam: no user: %s", user); - } - if (sia_make_entity_pwd(pw, ent) != SIASUCCESS) { - sia_ses_release(&ent); - fatal("sia_make_entity_pwd failed"); - } - - ent->authtype = SIA_A_NONE; - if (sia_ses_estab(sia_collect_trm, ent) != SIASUCCESS) { - fatal("Couldn't establish session for %s from %s", user, - host); - } - - if (setpriority(PRIO_PROCESS, 0, 0) == -1) { - sia_ses_release(&ent); - fatal("setpriority: %s", strerror (errno)); - } - - if (sia_ses_launch(sia_collect_trm, ent) != SIASUCCESS) { - fatal("Couldn't launch session for %s from %s", user, host); - } - - sia_ses_release(&ent); - - if (setreuid(geteuid(), geteuid()) < 0) { - fatal("setreuid: %s", strerror(errno)); - } -} - -#endif /* HAVE_OSF_SIA */ - -#pragma ident "%Z%%M% %I% %E% SMI" diff --git a/usr/src/cmd/ssh/sshd/auth-skey.c b/usr/src/cmd/ssh/sshd/auth-skey.c deleted file mode 100644 index 436f66aed8..0000000000 --- a/usr/src/cmd/ssh/sshd/auth-skey.c +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (c) 2001 Markus Friedl. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#include "includes.h" -RCSID("$OpenBSD: auth-skey.c,v 1.20 2002/06/30 21:59:45 deraadt Exp $"); - -#pragma ident "%Z%%M% %I% %E% SMI" - -#ifdef SKEY - -#include <skey.h> - -#include "xmalloc.h" -#include "auth.h" - -static void * -skey_init_ctx(Authctxt *authctxt) -{ - return authctxt; -} - -int -skey_query(void *ctx, char **name, char **infotxt, - u_int* numprompts, char ***prompts, u_int **echo_on) -{ - Authctxt *authctxt = ctx; - char challenge[1024], *p; - int len; - struct skey skey; - - if (skeychallenge(&skey, authctxt->user, challenge) == -1) - return -1; - - *name = xstrdup(""); - *infotxt = xstrdup(""); - *numprompts = 1; - *prompts = xmalloc(*numprompts * sizeof(char *)); - *echo_on = xmalloc(*numprompts * sizeof(u_int)); - (*echo_on)[0] = 0; - - len = strlen(challenge) + strlen(SKEY_PROMPT) + 1; - p = xmalloc(len); - strlcpy(p, challenge, len); - strlcat(p, SKEY_PROMPT, len); - (*prompts)[0] = p; - - return 0; -} - -int -skey_respond(void *ctx, u_int numresponses, char **responses) -{ - Authctxt *authctxt = ctx; - - if (authctxt->valid && - numresponses == 1 && - skey_haskey(authctxt->pw->pw_name) == 0 && - skey_passcheck(authctxt->pw->pw_name, responses[0]) != -1) - return 0; - return -1; -} - -static void -skey_free_ctx(void *ctx) -{ - /* we don't have a special context */ -} - -KbdintDevice skey_device = { - "skey", - skey_init_ctx, - skey_query, - skey_respond, - skey_free_ctx -}; - -KbdintDevice mm_skey_device = { - "skey", - skey_init_ctx, - mm_skey_query, - mm_skey_respond, - skey_free_ctx -}; -#endif /* SKEY */ diff --git a/usr/src/cmd/ssh/sshd/auth.c b/usr/src/cmd/ssh/sshd/auth.c deleted file mode 100644 index 64e6959ecf..0000000000 --- a/usr/src/cmd/ssh/sshd/auth.c +++ /dev/null @@ -1,794 +0,0 @@ -/* - * Copyright (c) 2000 Markus Friedl. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -/* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - -#include "includes.h" -RCSID("$OpenBSD: auth.c,v 1.45 2002/09/20 18:41:29 stevesk Exp $"); - -#ifdef HAVE_LOGIN_H -#include <login.h> -#endif -#if defined(HAVE_SHADOW_H) && !defined(DISABLE_SHADOW) -#include <shadow.h> -#endif /* defined(HAVE_SHADOW_H) && !defined(DISABLE_SHADOW) */ - -#ifdef HAVE_LIBGEN_H -#include <libgen.h> -#endif - -#include "xmalloc.h" -#include "match.h" -#include "groupaccess.h" -#include "log.h" -#include "buffer.h" -#include "servconf.h" -#include "auth.h" -#include "auth-options.h" -#include "canohost.h" -#include "bufaux.h" -#include "uidswap.h" -#include "tildexpand.h" -#include "misc.h" -#include "bufaux.h" -#include "packet.h" -#include "channels.h" -#include "session.h" - -#ifdef HAVE_BSM -#include "bsmaudit.h" -#include <bsm/adt.h> -#endif /* HAVE_BSM */ - -/* import */ -extern ServerOptions options; - -/* Debugging messages */ -Buffer auth_debug; -int auth_debug_init; - -/* - * Check if the user is allowed to log in via ssh. If user is listed - * in DenyUsers or one of user's groups is listed in DenyGroups, false - * will be returned. If AllowUsers isn't empty and user isn't listed - * there, or if AllowGroups isn't empty and one of user's groups isn't - * listed there, false will be returned. - * If the user's shell is not executable, false will be returned. - * Otherwise true is returned. - */ -int -allowed_user(struct passwd * pw) -{ - struct stat st; - const char *hostname = NULL, *ipaddr = NULL; - char *shell; - int i; -#ifdef WITH_AIXAUTHENTICATE - char *loginmsg; -#endif /* WITH_AIXAUTHENTICATE */ -#if !defined(USE_PAM) && defined(HAVE_SHADOW_H) && \ - !defined(DISABLE_SHADOW) && defined(HAS_SHADOW_EXPIRE) - struct spwd *spw; - - /* Shouldn't be called if pw is NULL, but better safe than sorry... */ - if (!pw || !pw->pw_name) - return 0; - -#define DAY (24L * 60 * 60) /* 1 day in seconds */ - spw = getspnam(pw->pw_name); - if (spw != NULL) { - time_t today = time(NULL) / DAY; - debug3("allowed_user: today %d sp_expire %d sp_lstchg %d" - " sp_max %d", (int)today, (int)spw->sp_expire, - (int)spw->sp_lstchg, (int)spw->sp_max); - - /* - * We assume account and password expiration occurs the - * day after the day specified. - */ - if (spw->sp_expire != -1 && today > spw->sp_expire) { - log("Account %.100s has expired", pw->pw_name); - return 0; - } - - if (spw->sp_lstchg == 0) { - log("User %.100s password has expired (root forced)", - pw->pw_name); - return 0; - } - - if (spw->sp_max != -1 && - today > spw->sp_lstchg + spw->sp_max) { - log("User %.100s password has expired (password aged)", - pw->pw_name); - return 0; - } - } -#else - /* Shouldn't be called if pw is NULL, but better safe than sorry... */ - if (!pw || !pw->pw_name) - return 0; -#endif - - /* - * Get the shell from the password data. An empty shell field is - * legal, and means /bin/sh. - */ - shell = (pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell; - - /* deny if shell does not exists or is not executable */ - if (stat(shell, &st) != 0) { - log("User %.100s not allowed because shell %.100s does not exist", - pw->pw_name, shell); - return 0; - } - if (S_ISREG(st.st_mode) == 0 || - (st.st_mode & (S_IXOTH|S_IXUSR|S_IXGRP)) == 0) { - log("User %.100s not allowed because shell %.100s is not executable", - pw->pw_name, shell); - return 0; - } - - if (options.num_deny_users > 0 || options.num_allow_users > 0) { - hostname = get_canonical_hostname(options.verify_reverse_mapping); - ipaddr = get_remote_ipaddr(); - } - - /* Return false if user is listed in DenyUsers */ - if (options.num_deny_users > 0) { - for (i = 0; i < options.num_deny_users; i++) - if (match_user(pw->pw_name, hostname, ipaddr, - options.deny_users[i])) { - log("User %.100s not allowed because listed in DenyUsers", - pw->pw_name); - return 0; - } - } - /* Return false if AllowUsers isn't empty and user isn't listed there */ - if (options.num_allow_users > 0) { - for (i = 0; i < options.num_allow_users; i++) - if (match_user(pw->pw_name, hostname, ipaddr, - options.allow_users[i])) - break; - /* i < options.num_allow_users iff we break for loop */ - if (i >= options.num_allow_users) { - log("User %.100s not allowed because not listed in AllowUsers", - pw->pw_name); - return 0; - } - } - if (options.num_deny_groups > 0 || options.num_allow_groups > 0) { - /* Get the user's group access list (primary and supplementary) */ - if (ga_init(pw->pw_name, pw->pw_gid) == 0) { - log("User %.100s not allowed because not in any group", - pw->pw_name); - return 0; - } - - /* Return false if one of user's groups is listed in DenyGroups */ - if (options.num_deny_groups > 0) - if (ga_match(options.deny_groups, - options.num_deny_groups)) { - ga_free(); - log("User %.100s not allowed because a group is listed in DenyGroups", - pw->pw_name); - return 0; - } - /* - * Return false if AllowGroups isn't empty and one of user's groups - * isn't listed there - */ - if (options.num_allow_groups > 0) - if (!ga_match(options.allow_groups, - options.num_allow_groups)) { - ga_free(); - log("User %.100s not allowed because none of user's groups are listed in AllowGroups", - pw->pw_name); - return 0; - } - ga_free(); - } - -#ifdef WITH_AIXAUTHENTICATE - if (loginrestrictions(pw->pw_name, S_RLOGIN, NULL, &loginmsg) != 0) { - if (loginmsg && *loginmsg) { - /* Remove embedded newlines (if any) */ - char *p; - for (p = loginmsg; *p; p++) { - if (*p == '\n') - *p = ' '; - } - /* Remove trailing newline */ - *--p = '\0'; - log("Login restricted for %s: %.100s", pw->pw_name, loginmsg); - } - return 0; - } -#endif /* WITH_AIXAUTHENTICATE */ - - /* We found no reason not to let this user try to log on... */ - return 1; -} - -Authctxt * -authctxt_new(void) -{ - Authctxt *authctxt = xmalloc(sizeof(*authctxt)); - memset(authctxt, 0, sizeof(*authctxt)); - return authctxt; -} - -void -auth_log(Authctxt *authctxt, int authenticated, char *method, char *info) -{ - void (*authlog) (const char *fmt,...) = verbose; - char *authmsg, *user_str; - - if (authctxt == NULL) - fatal("%s: INTERNAL ERROR", __func__); - - /* Raise logging level */ - if (authenticated == 1 || !authctxt->valid) - authlog = log; - else if (authctxt->failures >= AUTH_FAIL_LOG || - authctxt->attempt >= options.max_auth_tries_log || - authctxt->init_attempt >= options.max_init_auth_tries_log) - authlog = notice; - - if (authctxt->method) { - authmsg = "Failed"; - if (authctxt->method->postponed) - authmsg = "Postponed"; /* shouldn't happen */ - if (authctxt->method->abandoned) - authmsg = "Abandoned"; - if (authctxt->method->authenticated) { - if (userauth_check_partial_failure(authctxt)) - authmsg = "Partially accepted"; - else - authmsg = "Accepted"; - } - else - authmsg = "Failed"; - } - else { - authmsg = authenticated ? "Accepted" : "Failed"; - } - - if (authctxt->user == NULL || *authctxt->user == '\0') - user_str = "<implicit>"; - else if (!authctxt->valid) - user_str = "<invalid username>"; - else - user_str = authctxt->user; - - authlog("%s %s for %s from %.200s port %d%s", - authmsg, - (method != NULL) ? method : "<unknown authentication method>", - user_str, - get_remote_ipaddr(), - get_remote_port(), - info); - -#ifdef WITH_AIXAUTHENTICATE - if (authenticated == 0 && strcmp(method, "password") == 0) - loginfailed(authctxt->user, - get_canonical_hostname(options.verify_reverse_mapping), - "ssh"); -#endif /* WITH_AIXAUTHENTICATE */ - -} - -#ifdef HAVE_BSM -void -audit_failed_login_cleanup(void *ctxt) -{ - Authctxt *authctxt = (Authctxt *)ctxt; - adt_session_data_t *ah; - - /* - * This table lists the different variable combinations evaluated and - * what the resulting PAM return value is. As the table shows - * authctxt and authctxt->valid need to be checked before either of - * the authctxt->pam* variables. - * - * authctxt-> authctxt-> - * authctxt valid authctxt->pam pam_retval PAM rval - * -------- ---------- ------------- ------------ -------- - * NULL ANY ANY ANY PAM_ABORT - * OK zero (0) ANY ANY PAM_USER_UNKNOWN - * OK one (1) NULL PAM_SUCCESS PAM_PERM_DENIED - * OK one (1) NULL !PAM_SUCCESS authctxt-> - * pam_retval - * OK one (1) VALID ANY authctxt-> - * pam_retval (+) - * (+) If not set then default to PAM_PERM_DENIED - */ - - if (authctxt == NULL) { - /* Internal error */ - audit_sshd_login_failure(&ah, PAM_ABORT, NULL); - return; - } - - if (authctxt->valid == 0) { - audit_sshd_login_failure(&ah, PAM_USER_UNKNOWN, NULL); - } else if (authctxt->pam == NULL) { - if (authctxt->pam_retval == PAM_SUCCESS) { - audit_sshd_login_failure(&ah, PAM_PERM_DENIED, - authctxt->user); - } else { - audit_sshd_login_failure(&ah, authctxt->pam_retval, - authctxt->user); - } - } else { - audit_sshd_login_failure(&ah, AUTHPAM_ERROR(authctxt, - PAM_PERM_DENIED), authctxt->user); - } -} -#endif /* HAVE_BSM */ - -/* - * Check whether root logins are disallowed. - */ -int -auth_root_allowed(char *method) -{ - switch (options.permit_root_login) { - case PERMIT_YES: - return 1; - break; - case PERMIT_NO_PASSWD: - if (strcmp(method, "password") != 0 && - strcmp(method, "keyboard-interactive") != 0) - return 1; - break; - case PERMIT_FORCED_ONLY: - if (forced_command) { - log("Root login accepted for forced command."); - return 1; - } - break; - } - log("ROOT LOGIN REFUSED FROM %.200s", get_remote_ipaddr()); - return 0; -} - - -/* - * Given a template and a passwd structure, build a filename - * by substituting % tokenised options. Currently, %% becomes '%', - * %h becomes the home directory and %u the username. - * - * This returns a buffer allocated by xmalloc. - */ -char * -expand_filename(const char *filename, struct passwd *pw) -{ - Buffer buffer; - char *file; - const char *cp; - - if (pw == 0) - return NULL; /* shouldn't happen */ - /* - * Build the filename string in the buffer by making the appropriate - * substitutions to the given file name. - */ - buffer_init(&buffer); - for (cp = filename; *cp; cp++) { - if (cp[0] == '%' && cp[1] == '%') { - buffer_append(&buffer, "%", 1); - cp++; - continue; - } - if (cp[0] == '%' && cp[1] == 'h') { - buffer_append(&buffer, pw->pw_dir, strlen(pw->pw_dir)); - cp++; - continue; - } - if (cp[0] == '%' && cp[1] == 'u') { - buffer_append(&buffer, pw->pw_name, - strlen(pw->pw_name)); - cp++; - continue; - } - buffer_append(&buffer, cp, 1); - } - buffer_append(&buffer, "\0", 1); - - /* - * Ensure that filename starts anchored. If not, be backward - * compatible and prepend the '%h/' - */ - file = xmalloc(MAXPATHLEN); - cp = buffer_ptr(&buffer); - if (*cp != '/') - snprintf(file, MAXPATHLEN, "%s/%s", pw->pw_dir, cp); - else - strlcpy(file, cp, MAXPATHLEN); - - buffer_free(&buffer); - return file; -} - -char * -authorized_keys_file(struct passwd *pw) -{ - return expand_filename(options.authorized_keys_file, pw); -} - -char * -authorized_keys_file2(struct passwd *pw) -{ - return expand_filename(options.authorized_keys_file2, pw); -} - -/* return ok if key exists in sysfile or userfile */ -HostStatus -check_key_in_hostfiles(struct passwd *pw, Key *key, const char *host, - const char *sysfile, const char *userfile) -{ - Key *found; - char *user_hostfile; - struct stat st; - HostStatus host_status; - - /* Check if we know the host and its host key. */ - found = key_new(key->type); - host_status = check_host_in_hostfile(sysfile, host, key, found, NULL); - - if (host_status != HOST_OK && userfile != NULL) { - user_hostfile = tilde_expand_filename(userfile, pw->pw_uid); - if (options.strict_modes && - (stat(user_hostfile, &st) == 0) && - ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || - (st.st_mode & 022) != 0)) { - log("Authentication refused for %.100s: " - "bad owner or modes for %.200s", - pw->pw_name, user_hostfile); - } else { - temporarily_use_uid(pw); - host_status = check_host_in_hostfile(user_hostfile, - host, key, found, NULL); - restore_uid(); - } - xfree(user_hostfile); - } - key_free(found); - - debug2("check_key_in_hostfiles: key %s for %s", host_status == HOST_OK ? - "ok" : "not found", host); - return host_status; -} - - -/* - * Check a given file for security. This is defined as all components - * of the path to the file must be owned by either the owner of - * of the file or root and no directories must be group or world writable. - * - * XXX Should any specific check be done for sym links ? - * - * Takes an open file descriptor, the file name, a uid and and - * error buffer plus max size as arguments. - * - * Returns 0 on success and -1 on failure - */ -int -secure_filename(FILE *f, const char *file, struct passwd *pw, - char *err, size_t errlen) -{ - uid_t uid; - char buf[MAXPATHLEN], homedir[MAXPATHLEN]; - char *cp; - int comparehome = 0; - struct stat st; - - if (pw == NULL) - return 0; - - uid = pw->pw_uid; - - if (realpath(file, buf) == NULL) { - snprintf(err, errlen, "realpath %s failed: %s", file, - strerror(errno)); - return -1; - } - - /* - * A user is not required to have all the files that are subject to - * the strict mode checking in his/her home directory. If the - * directory is not present at the moment, which might be the case if - * the directory is not mounted until the user is authenticated, do - * not perform the home directory check below. - */ - if (realpath(pw->pw_dir, homedir) != NULL) - comparehome = 1; - - /* check the open file to avoid races */ - if (fstat(fileno(f), &st) < 0 || - (st.st_uid != 0 && st.st_uid != uid) || - (st.st_mode & 022) != 0) { - snprintf(err, errlen, "bad ownership or modes for file %s", - buf); - return -1; - } - - /* for each component of the canonical path, walking upwards */ - for (;;) { - if ((cp = dirname(buf)) == NULL) { - snprintf(err, errlen, "dirname() failed"); - return -1; - } - strlcpy(buf, cp, sizeof(buf)); - - debug3("secure_filename: checking '%s'", buf); - if (stat(buf, &st) < 0 || - (st.st_uid != 0 && st.st_uid != uid) || - (st.st_mode & 022) != 0) { - snprintf(err, errlen, - "bad ownership or modes for directory %s", buf); - return -1; - } - - /* If we passed the homedir then we can stop. */ - if (comparehome && strcmp(homedir, buf) == 0) { - debug3("secure_filename: terminating check at '%s'", - buf); - break; - } - /* - * dirname should always complete with a "/" path, - * but we can be paranoid and check for "." too - */ - if ((strcmp("/", buf) == 0) || (strcmp(".", buf) == 0)) - break; - } - return 0; -} - -struct passwd * -getpwnamallow(const char *user) -{ -#ifdef HAVE_LOGIN_CAP - extern login_cap_t *lc; -#ifdef BSD_AUTH - auth_session_t *as; -#endif -#endif - struct passwd *pw; - - if (user == NULL || *user == '\0') - return (NULL); /* implicit user, will be set later */ - - parse_server_match_config(&options, user, - get_canonical_hostname(options.verify_reverse_mapping), get_remote_ipaddr()); - - pw = getpwnam(user); - if (pw == NULL) { - log("Illegal user %.100s from %.100s", - user, get_remote_ipaddr()); - return (NULL); - } - if (!allowed_user(pw)) - return (NULL); -#ifdef HAVE_LOGIN_CAP - if ((lc = login_getclass(pw->pw_class)) == NULL) { - debug("unable to get login class: %s", user); - return (NULL); - } -#ifdef BSD_AUTH - if ((as = auth_open()) == NULL || auth_setpwd(as, pw) != 0 || - auth_approval(as, lc, pw->pw_name, "ssh") <= 0) { - debug("Approval failure for %s", user); - pw = NULL; - } - if (as != NULL) - auth_close(as); -#endif -#endif - if (pw != NULL) - return (pwcopy(pw)); - return (NULL); -} - - -/* - * The fatal_cleanup method to kill the hook. Since hook has been put into - * new process group all descendants will be killed as well. - */ -static void -kill_hook(void *arg) -{ - pid_t pid; - - pid = *(pid_t*)arg; - debug("killing hook and all it's children, process group: %ld", pid); - xfree(arg); - (void)killpg(pid, SIGTERM); -} - -/* - * Runs the PreUserauthHook. - * Returns -1 on execution error or the exit code of the hook if execution is - * successful. - */ -int -run_auth_hook(const char *path, const char *user, const char *method) -{ - struct stat st; - int i, status, ret = 1; - u_int envsize, argsize; - char buf[256]; - char **env, **args; - pid_t pid, *ppid; - - if (path == NULL || user == NULL || method == NULL) { - return (-1); - } - - /* Initialize the environment/arguments for the hook. */ - envsize = 4; /* 3 env vars + EndOfList marker */ - argsize = 4; /* 2 args + exe name + EndOfList marker */ - env = xmalloc(envsize * sizeof (char *)); - args = xmalloc(argsize * sizeof (char *)); - env[0] = NULL; - - /* we use the SSH env handling scheme */ - child_set_env_silent(&env, &envsize, "PATH", "/usr/bin:/bin"); - child_set_env_silent(&env, &envsize, "IFS", " \t\n"); - - (void) snprintf(buf, sizeof (buf), "%.50s %d %.50s %d", - get_remote_ipaddr(), get_remote_port(), - get_local_ipaddr(packet_get_connection_in()), get_local_port()); - child_set_env_silent(&env, &envsize, "SSH_CONNECTION", buf); - - args[0] = xstrdup(path); - args[1] = xstrdup(method); - args[2] = xstrdup(user); - args[3] = NULL; - - /* - * sanity checks - * note: the checks do not make sure that the file checked is actually - * the same which is executed. However, in this case it shouldn't be a - * major issue since the hook is rather static and the worst case would - * be an uncorrect message in the log or a hook is run even though the - * permissions are not right. - */ - - /* check if script does exist */ - if (stat(path, &st) < 0) { - log("Error executing PreUserauthHook \"%s\": %s", path, - strerror(errno)); - goto cleanup; - } - - /* Check correct permissions for script (uid of SSHD, mode 500) */ - if (st.st_uid != getuid() || ((st.st_mode & 0777) != 0500)) { - log("PreUserauthHook has invalid permissions (should be 500, is" - " %o) or ownership (should be %d, is %d)", - (uint) st.st_mode & 0777, getuid(), st.st_uid); - goto cleanup; - } - - if ((pid = fork()) == 0) { - /* - * We put the hook and all its (possible) descendants into - * a new process group so that in case of a hanging hook - * we can wipe out the whole "family". - */ - if (setpgid(0, 0) != 0) { - log("setpgid: %s", strerror(errno)); - _exit(255); - } - (void) execve(path, args, env); - /* child is gone so we shouldn't get here */ - log("Error executing PreUserauthHook \"%s\": %s", path, - strerror(errno)); - _exit(255); - } else if (pid == -1) { - log("Error executing PreUserauthHook \"%s\": %s", path, - strerror(errno)); - goto cleanup; - } - - /* make preparations to kill hook if it is hanging */ - ppid = xmalloc(sizeof (pid_t)); - *ppid = pid; - fatal_add_cleanup((void (*)(void *))kill_hook, (void *) ppid); - - if (waitpid(pid, &status, 0) == -1) { - log("Error executing PreUserauthHook \"%s\": %s", path, - strerror(errno)); - goto cleanup; - } - - ret = WEXITSTATUS(status); - - if (ret == 255) { - ret = -1; /* execve() failed, error msg already logged */ - } else if (ret != 0) { - log("PreUserauthHook \"%s\" failed with exit code %d", - path, ret); - } else { - debug("PreUserauthHook \"%s\" finished successfully", path); - } - -cleanup: - for (i = 0; args[i] != NULL; i++) { - xfree(args[i]); - } - for (i = 0; env[i] != NULL; i++) { - xfree(env[i]); - } - xfree(args); - xfree(env); - - fatal_remove_cleanup((void (*)(void *))kill_hook, (void *) ppid); - - return (ret); -} - -void -auth_debug_add(const char *fmt,...) -{ - char buf[1024]; - va_list args; - - if (!auth_debug_init) - return; - - va_start(args, fmt); - vsnprintf(buf, sizeof(buf), fmt, args); - va_end(args); - buffer_put_cstring(&auth_debug, buf); -} - -void -auth_debug_send(void) -{ - char *msg; - - if (!auth_debug_init) - return; - while (buffer_len(&auth_debug)) { - msg = buffer_get_string(&auth_debug, NULL); - packet_send_debug("%s", msg); - xfree(msg); - } -} - -void -auth_debug_reset(void) -{ - if (auth_debug_init) - buffer_clear(&auth_debug); - else { - buffer_init(&auth_debug); - auth_debug_init = 1; - } -} diff --git a/usr/src/cmd/ssh/sshd/auth1.c b/usr/src/cmd/ssh/sshd/auth1.c deleted file mode 100644 index e77a021393..0000000000 --- a/usr/src/cmd/ssh/sshd/auth1.c +++ /dev/null @@ -1,466 +0,0 @@ -/* - * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland - * All rights reserved - * - * As far as I am concerned, the code I have written for this software - * can be used freely for any purpose. Any derived versions of this - * software must be clearly marked as such, and if the derived work is - * incompatible with the protocol description in the RFC file, it must be - * called by a name other than "ssh" or "Secure Shell". - */ -/* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - -#include "includes.h" -RCSID("$OpenBSD: auth1.c,v 1.44 2002/09/26 11:38:43 markus Exp $"); - -#include "xmalloc.h" -#include "rsa.h" -#include "ssh1.h" -#include "packet.h" -#include "buffer.h" -#include "mpaux.h" -#include "log.h" -#include "servconf.h" -#include "compat.h" -#include "auth.h" -#include "channels.h" -#include "session.h" -#include "uidswap.h" - -#ifdef HAVE_BSM -#include "bsmaudit.h" -extern adt_session_data_t *ah; -#endif /* HAVE_BSM */ - -/* import */ -extern ServerOptions options; - -/* - * convert ssh auth msg type into description - */ -static char * -get_authname(int type) -{ - static char buf[1024]; - switch (type) { - case SSH_CMSG_AUTH_PASSWORD: - return "password"; - case SSH_CMSG_AUTH_RSA: - return "rsa"; - case SSH_CMSG_AUTH_RHOSTS_RSA: - return "rhosts-rsa"; - case SSH_CMSG_AUTH_RHOSTS: - return "rhosts"; - case SSH_CMSG_AUTH_TIS: - case SSH_CMSG_AUTH_TIS_RESPONSE: - return "challenge-response"; -#if defined(KRB4) || defined(KRB5) - case SSH_CMSG_AUTH_KERBEROS: - return "kerberos"; -#endif - } - snprintf(buf, sizeof buf, "bad-auth-msg-%d", type); - return buf; -} - -/* - * read packets, try to authenticate the user and - * return only if authentication is successful - */ -static void -do_authloop(Authctxt *authctxt) -{ - int authenticated = 0; - u_int bits; - Key *client_host_key; - BIGNUM *n; - char *client_user, *password; - char info[1024]; - u_int dlen; - u_int ulen; - int type = 0; - struct passwd *pw = authctxt->pw; - - debug("Attempting authentication for %s%.100s.", - authctxt->valid ? "" : "illegal user ", authctxt->user); - - /* If the user has no password, accept authentication immediately. */ - if (options.password_authentication && -#if defined(KRB4) || defined(KRB5) - (!options.kerberos_authentication || options.kerberos_or_local_passwd) && -#endif - auth_password(authctxt, "")) { - auth_log(authctxt, 1, "without authentication", ""); - return; - } - - /* Indicate that authentication is needed. */ - packet_start(SSH_SMSG_FAILURE); - packet_send(); - packet_write_wait(); - - client_user = NULL; - - for ( ;; ) { - /* default to fail */ - authenticated = 0; - - info[0] = '\0'; - - /* Get a packet from the client. */ - authctxt->v1_auth_type = type = packet_read(); - authctxt->v1_auth_name = get_authname(type); - - authctxt->attempt++; - - /* Process the packet. */ - switch (type) { - -#if defined(KRB4) || defined(KRB5) - case SSH_CMSG_AUTH_KERBEROS: - if (!options.kerberos_authentication) { - verbose("Kerberos authentication disabled."); - } else { - char *kdata = packet_get_string(&dlen); - packet_check_eom(); - - if (kdata[0] == 4) { /* KRB_PROT_VERSION */ -#ifdef KRB4 - KTEXT_ST tkt, reply; - tkt.length = dlen; - if (tkt.length < MAX_KTXT_LEN) - memcpy(tkt.dat, kdata, tkt.length); - - if (auth_krb4(authctxt, &tkt, - &client_user, &reply)) { - authenticated = 1; - snprintf(info, sizeof(info), - " tktuser %.100s", - client_user); - - packet_start( - SSH_SMSG_AUTH_KERBEROS_RESPONSE); - packet_put_string((char *) - reply.dat, reply.length); - packet_send(); - packet_write_wait(); - } -#endif /* KRB4 */ - } else { -#ifdef KRB5 - krb5_data tkt, reply; - tkt.length = dlen; - tkt.data = kdata; - - if (auth_krb5(authctxt, &tkt, - &client_user, &reply)) { - authenticated = 1; - snprintf(info, sizeof(info), - " tktuser %.100s", - client_user); - - /* Send response to client */ - packet_start( - SSH_SMSG_AUTH_KERBEROS_RESPONSE); - packet_put_string((char *) - reply.data, reply.length); - packet_send(); - packet_write_wait(); - - if (reply.length) - xfree(reply.data); - } -#endif /* KRB5 */ - } - xfree(kdata); - } - break; -#endif /* KRB4 || KRB5 */ - -#if defined(AFS) || defined(KRB5) - /* XXX - punt on backward compatibility here. */ - case SSH_CMSG_HAVE_KERBEROS_TGT: - packet_send_debug("Kerberos TGT passing disabled before authentication."); - break; -#ifdef AFS - case SSH_CMSG_HAVE_AFS_TOKEN: - packet_send_debug("AFS token passing disabled before authentication."); - break; -#endif /* AFS */ -#endif /* AFS || KRB5 */ - - case SSH_CMSG_AUTH_RHOSTS: - if (!options.rhosts_authentication) { - verbose("Rhosts authentication disabled."); - break; - } - /* - * Get client user name. Note that we just have to - * trust the client; this is one reason why rhosts - * authentication is insecure. (Another is - * IP-spoofing on a local network.) - */ - client_user = packet_get_string(&ulen); - packet_check_eom(); - - /* Try to authenticate using /etc/hosts.equiv and .rhosts. */ - authenticated = auth_rhosts(pw, client_user); - - snprintf(info, sizeof info, " ruser %.100s", client_user); - break; - - case SSH_CMSG_AUTH_RHOSTS_RSA: - if (!options.rhosts_rsa_authentication) { - verbose("Rhosts with RSA authentication disabled."); - break; - } - /* - * Get client user name. Note that we just have to - * trust the client; root on the client machine can - * claim to be any user. - */ - client_user = packet_get_string(&ulen); - - /* Get the client host key. */ - client_host_key = key_new(KEY_RSA1); - bits = packet_get_int(); - packet_get_bignum(client_host_key->rsa->e); - packet_get_bignum(client_host_key->rsa->n); - - if (bits != BN_num_bits(client_host_key->rsa->n)) - verbose("Warning: keysize mismatch for client_host_key: " - "actual %d, announced %d", - BN_num_bits(client_host_key->rsa->n), bits); - packet_check_eom(); - - authenticated = auth_rhosts_rsa(pw, client_user, - client_host_key); - key_free(client_host_key); - - snprintf(info, sizeof info, " ruser %.100s", client_user); - break; - - case SSH_CMSG_AUTH_RSA: - if (!options.rsa_authentication) { - verbose("RSA authentication disabled."); - break; - } - /* RSA authentication requested. */ - if ((n = BN_new()) == NULL) - fatal("do_authloop: BN_new failed"); - packet_get_bignum(n); - packet_check_eom(); - authenticated = auth_rsa(pw, n); - BN_clear_free(n); - break; - - case SSH_CMSG_AUTH_PASSWORD: - authctxt->init_attempt++; - - if (!options.password_authentication) { - verbose("Password authentication disabled."); - break; - } - /* - * Read user password. It is in plain text, but was - * transmitted over the encrypted channel so it is - * not visible to an outside observer. - */ - password = packet_get_string(&dlen); - packet_check_eom(); - - /* Try authentication with the password. */ - if (authctxt->init_failures < - options.max_init_auth_tries) - authenticated = - auth_password(authctxt, password); - - memset(password, 0, strlen(password)); - xfree(password); - break; - - case SSH_CMSG_AUTH_TIS: - debug("rcvd SSH_CMSG_AUTH_TIS"); - if (options.challenge_response_authentication == 1) { - char *challenge = get_challenge(authctxt); - if (challenge != NULL) { - debug("sending challenge '%s'", challenge); - packet_start(SSH_SMSG_AUTH_TIS_CHALLENGE); - packet_put_cstring(challenge); - xfree(challenge); - packet_send(); - packet_write_wait(); - continue; - } - } - break; - case SSH_CMSG_AUTH_TIS_RESPONSE: - debug("rcvd SSH_CMSG_AUTH_TIS_RESPONSE"); - if (options.challenge_response_authentication == 1) { - char *response = packet_get_string(&dlen); - debug("got response '%s'", response); - packet_check_eom(); - authenticated = verify_response(authctxt, response); - memset(response, 'r', dlen); - xfree(response); - } - break; - - default: - /* - * Any unknown messages will be ignored (and failure - * returned) during authentication. - */ - log("Unknown message during authentication: type %d", type); - break; - } -#ifdef BSD_AUTH - if (authctxt->as) { - auth_close(authctxt->as); - authctxt->as = NULL; - } -#endif - if (!authctxt->valid && authenticated) { - authenticated = 0; - log("Ignoring authenticated invalid user %s", - authctxt->user); - } - -#ifdef _UNICOS - if (type == SSH_CMSG_AUTH_PASSWORD && !authenticated) - cray_login_failure(authctxt->user, IA_UDBERR); - if (authenticated && cray_access_denied(authctxt->user)) { - authenticated = 0; - fatal("Access denied for user %s.",authctxt->user); - } -#endif /* _UNICOS */ - -#ifdef HAVE_CYGWIN - if (authenticated && - !check_nt_auth(type == SSH_CMSG_AUTH_PASSWORD, pw)) { - packet_disconnect("Authentication rejected for uid %d.", - pw == NULL ? -1 : pw->pw_uid); - authenticated = 0; - } -#else - /* Special handling for root */ - if (authenticated && authctxt->pw->pw_uid == 0 && - !auth_root_allowed(get_authname(type))) - authenticated = 0; -#endif -#ifdef USE_PAM - if (authenticated && type != SSH_CMSG_AUTH_PASSWORD) - authenticated = do_pam_non_initial_userauth(authctxt); - else if (authenticated && !AUTHPAM_DONE(authctxt)) - authenticated = 0; - - if (!authenticated) - authctxt->pam_retval = AUTHPAM_ERROR(authctxt, - PAM_PERM_DENIED); -#endif /* USE_PAM */ - - /* Log before sending the reply */ - auth_log(authctxt, authenticated, get_authname(type), info); - - if (client_user != NULL) { - xfree(client_user); - client_user = NULL; - } - - if (authenticated) - return; - - if (type == SSH_CMSG_AUTH_PASSWORD) - authctxt->init_failures++; - - if (authctxt->failures++ > options.max_auth_tries) { -#ifdef HAVE_BSM - fatal_remove_cleanup(audit_failed_login_cleanup, - authctxt); - audit_sshd_login_failure(&ah, PAM_MAXTRIES, - authctxt->user); -#endif /* HAVE_BSM */ - packet_disconnect(AUTH_FAIL_MSG, authctxt->user); - } - - packet_start(SSH_SMSG_FAILURE); - packet_send(); - packet_write_wait(); - } -} - -/* - * Performs authentication of an incoming connection. Session key has already - * been exchanged and encryption is enabled. - */ -Authctxt * -do_authentication(void) -{ - Authctxt *authctxt; - u_int ulen; - char *user, *style = NULL; - - /* Get the name of the user that we wish to log in as. */ - packet_read_expect(SSH_CMSG_USER); - - /* Get the user name. */ - user = packet_get_string(&ulen); - packet_check_eom(); - - if ((style = strchr(user, ':')) != NULL) - *style++ = '\0'; - -#ifdef KRB5 - /* XXX - SSH.com Kerberos v5 braindeath. */ - if ((datafellows & SSH_BUG_K5USER) && - options.kerberos_authentication) { - char *p; - if ((p = strchr(user, '@')) != NULL) - *p = '\0'; - } -#endif - - authctxt = authctxt_new(); - authctxt->user = user; - authctxt->style = style; - -#ifdef HAVE_BSM - fatal_add_cleanup(audit_failed_login_cleanup, authctxt); -#endif /* HAVE_BSM */ - - /* Verify that the user is a valid user. */ - if ((authctxt->pw = getpwnamallow(user)) != NULL) { - authctxt->valid = 1; - } else { - authctxt->valid = 0; - debug("do_authentication: illegal user %s", user); - } - - setproctitle("%s", authctxt->pw ? user : "unknown"); - - /* - * If we are not running as root, the user must have the same uid as - * the server. (Unless you are running Windows) - */ -#ifndef HAVE_CYGWIN - if (getuid() != 0 && authctxt->pw && - authctxt->pw->pw_uid != getuid()) - packet_disconnect("Cannot change user when server not running as root."); -#endif - - /* - * Loop until the user has been authenticated or the connection is - * closed, do_authloop() returns only if authentication is successful - */ - do_authloop(authctxt); - - /* The user has been authenticated and accepted. */ - packet_start(SSH_SMSG_SUCCESS); - packet_send(); - packet_write_wait(); - - return (authctxt); -} diff --git a/usr/src/cmd/ssh/sshd/auth2-chall.c b/usr/src/cmd/ssh/sshd/auth2-chall.c deleted file mode 100644 index 72dcc6dc5b..0000000000 --- a/usr/src/cmd/ssh/sshd/auth2-chall.c +++ /dev/null @@ -1,358 +0,0 @@ -/* - * Copyright (c) 2001 Markus Friedl. All rights reserved. - * Copyright (c) 2001 Per Allansson. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -/* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - -#include "includes.h" -RCSID("$OpenBSD: auth2-chall.c,v 1.20 2002/06/30 21:59:45 deraadt Exp $"); - -#include "ssh2.h" -#include "auth.h" -#include "buffer.h" -#include "packet.h" -#include "xmalloc.h" -#include "dispatch.h" -#include "auth.h" -#include "log.h" - -#ifndef lint -static void auth2_challenge_start(Authctxt *); -static int send_userauth_info_request(Authctxt *); -static void input_userauth_info_response(int, u_int32_t, void *); - -#ifdef BSD_AUTH -extern KbdintDevice bsdauth_device; -#else -#ifdef SKEY -extern KbdintDevice skey_device; -#endif -#endif - -KbdintDevice *devices[] = { -#ifdef BSD_AUTH - &bsdauth_device, -#else -#ifdef SKEY - &skey_device, -#endif -#endif - NULL -}; - -typedef struct KbdintAuthctxt KbdintAuthctxt; -struct KbdintAuthctxt -{ - char *devices; - void *ctxt; - KbdintDevice *device; - u_int nreq; -}; - -static KbdintAuthctxt * -kbdint_alloc(const char *devs) -{ - KbdintAuthctxt *kbdintctxt; - Buffer b; - int i; - - kbdintctxt = xmalloc(sizeof(KbdintAuthctxt)); - if (strcmp(devs, "") == 0) { - buffer_init(&b); - for (i = 0; devices[i]; i++) { - if (buffer_len(&b) > 0) - buffer_append(&b, ",", 1); - buffer_append(&b, devices[i]->name, - strlen(devices[i]->name)); - } - buffer_append(&b, "\0", 1); - kbdintctxt->devices = xstrdup(buffer_ptr(&b)); - buffer_free(&b); - } else { - kbdintctxt->devices = xstrdup(devs); - } - debug("kbdint_alloc: devices '%s'", kbdintctxt->devices); - kbdintctxt->ctxt = NULL; - kbdintctxt->device = NULL; - kbdintctxt->nreq = 0; - - return kbdintctxt; -} -static void -kbdint_reset_device(KbdintAuthctxt *kbdintctxt) -{ - if (kbdintctxt->ctxt) { - kbdintctxt->device->free_ctx(kbdintctxt->ctxt); - kbdintctxt->ctxt = NULL; - } - kbdintctxt->device = NULL; -} -static void -kbdint_free(KbdintAuthctxt *kbdintctxt) -{ - if (kbdintctxt->device) - kbdint_reset_device(kbdintctxt); - if (kbdintctxt->devices) { - xfree(kbdintctxt->devices); - kbdintctxt->devices = NULL; - } - xfree(kbdintctxt); -} -/* get next device */ -static int -kbdint_next_device(KbdintAuthctxt *kbdintctxt) -{ - size_t len; - char *t; - int i; - - if (kbdintctxt->device) - kbdint_reset_device(kbdintctxt); - do { - len = kbdintctxt->devices ? - strcspn(kbdintctxt->devices, ",") : 0; - - if (len == 0) - break; - for (i = 0; devices[i]; i++) - if (strncmp(kbdintctxt->devices, devices[i]->name, len) == 0) - kbdintctxt->device = devices[i]; - t = kbdintctxt->devices; - kbdintctxt->devices = t[len] ? xstrdup(t+len+1) : NULL; - xfree(t); - debug2("kbdint_next_device: devices %s", kbdintctxt->devices ? - kbdintctxt->devices : "<empty>"); - } while (kbdintctxt->devices && !kbdintctxt->device); - - return kbdintctxt->device ? 1 : 0; -} - -/* - * try challenge-response, set authctxt->method->postponed if we have to - * wait for the response. - */ -void -auth2_challenge(Authctxt *authctxt, char *devs) -{ - debug("auth2_challenge: user=%s devs=%s", - authctxt->user ? authctxt->user : "<nouser>", - devs ? devs : "<no devs>"); - - if (authctxt->user == NULL || !devs) - return; - if (authctxt->method->method_data != NULL) { - auth2_challenge_abandon(authctxt); - authctxt->method->abandoned = 0; - } - authctxt->method->method_data = (void *) kbdint_alloc(devs); - auth2_challenge_start(authctxt); -} - -/* unregister kbd-int callbacks and context */ -static void -auth2_challenge_stop(Authctxt *authctxt) -{ - /* unregister callback */ - dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, NULL); - if (authctxt->method->method_data != NULL) { - kbdint_free((KbdintAuthctxt *) authctxt->method->method_data); - authctxt->method->method_data = NULL; - } -} - -void -auth2_challenge_abandon(Authctxt *authctxt) -{ - auth2_challenge_stop(authctxt); - authctxt->method->abandoned = 1; - authctxt->method->postponed = 0; - authctxt->method->authenticated = 0; - authctxt->method->abandons++; - authctxt->method->attempts++; -} - -/* side effect: sets authctxt->method->postponed if a reply was sent*/ -static void -auth2_challenge_start(Authctxt *authctxt) -{ - KbdintAuthctxt *kbdintctxt = (KbdintAuthctxt *) - authctxt->method->method_data; - - debug2("auth2_challenge_start: devices %s", - kbdintctxt->devices ? kbdintctxt->devices : "<empty>"); - - if (kbdint_next_device(kbdintctxt) == 0) { - auth2_challenge_stop(authctxt); - return; - } - debug("auth2_challenge_start: trying authentication method '%s'", - kbdintctxt->device->name); - - if ((kbdintctxt->ctxt = kbdintctxt->device->init_ctx(authctxt)) == NULL) { - auth2_challenge_stop(authctxt); - return; - } - if (send_userauth_info_request(authctxt) == 0) { - auth2_challenge_stop(authctxt); - return; - } - dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, - &input_userauth_info_response); - - authctxt->method->postponed = 1; -} - -static int -send_userauth_info_request(Authctxt *authctxt) -{ - KbdintAuthctxt *kbdintctxt; - char *name, *instr, **prompts; - int i; - u_int *echo_on; - - kbdintctxt = (KbdintAuthctxt *) authctxt->method->method_data; - if (kbdintctxt->device->query(kbdintctxt->ctxt, - &name, &instr, &kbdintctxt->nreq, &prompts, &echo_on)) - return 0; - - packet_start(SSH2_MSG_USERAUTH_INFO_REQUEST); - packet_put_cstring(name); - packet_put_utf8_cstring(instr); - packet_put_cstring(""); /* language not used */ - packet_put_int(kbdintctxt->nreq); - for (i = 0; i < kbdintctxt->nreq; i++) { - packet_put_utf8_cstring(prompts[i]); - packet_put_char(echo_on[i]); - } - packet_send(); - packet_write_wait(); - - for (i = 0; i < kbdintctxt->nreq; i++) - xfree(prompts[i]); - xfree(prompts); - xfree(echo_on); - xfree(name); - xfree(instr); - return 1; -} - -static void -input_userauth_info_response(int type, u_int32_t seq, void *ctxt) -{ - Authctxt *authctxt = ctxt; - KbdintAuthctxt *kbdintctxt; - int i, res, len; - u_int nresp; - char **response = NULL, *method; - - if (authctxt == NULL) - fatal("input_userauth_info_response: no authctxt"); - kbdintctxt = (KbdintAuthctxt *) authctxt->method->method_data; - if (kbdintctxt == NULL || kbdintctxt->ctxt == NULL) - fatal("input_userauth_info_response: no kbdintctxt"); - if (kbdintctxt->device == NULL) - fatal("input_userauth_info_response: no device"); - - nresp = packet_get_int(); - if (nresp != kbdintctxt->nreq) - fatal("input_userauth_info_response: wrong number of replies"); - if (nresp > 100) - fatal("input_userauth_info_response: too many replies"); - if (nresp > 0) { - response = xmalloc(nresp * sizeof(char *)); - for (i = 0; i < nresp; i++) - response[i] = packet_get_string(NULL); - } - packet_check_eom(); - - if (authctxt->valid) { - res = kbdintctxt->device->respond(kbdintctxt->ctxt, - nresp, response); - } else { - res = -1; - } - - for (i = 0; i < nresp; i++) { - memset(response[i], 'r', strlen(response[i])); - xfree(response[i]); - } - if (response) - xfree(response); - - authctxt->method->postponed = 0; /* reset */ - switch (res) { - case 0: - /* Success! */ - authctxt->method->authenticated = 1; - break; - case 1: - /* Authentication needs further interaction */ - if (send_userauth_info_request(authctxt) == 1) { - authctxt->method->postponed = 1; - } - break; - default: - /* Failure! */ - break; - } - - - len = strlen("keyboard-interactive") + 2 + - strlen(kbdintctxt->device->name); - method = xmalloc(len); - snprintf(method, len, "keyboard-interactive/%s", - kbdintctxt->device->name); - - if (authctxt->method->authenticated || authctxt->method->abandoned) { - auth2_challenge_stop(authctxt); - } else { - /* start next device */ - /* may set authctxt->method->postponed */ - auth2_challenge_start(authctxt); - } - userauth_finish(authctxt, method); - xfree(method); -} - -void -privsep_challenge_enable(void) -{ -#ifdef BSD_AUTH - extern KbdintDevice mm_bsdauth_device; -#endif -#ifdef SKEY - extern KbdintDevice mm_skey_device; -#endif - /* As long as SSHv1 has devices[0] hard coded this is fine */ -#ifdef BSD_AUTH - devices[0] = &mm_bsdauth_device; -#else -#ifdef SKEY - devices[0] = &mm_skey_device; -#endif -#endif -} -#endif /* lint */ diff --git a/usr/src/cmd/ssh/sshd/auth2-gss.c b/usr/src/cmd/ssh/sshd/auth2-gss.c deleted file mode 100644 index 8525707c1e..0000000000 --- a/usr/src/cmd/ssh/sshd/auth2-gss.c +++ /dev/null @@ -1,533 +0,0 @@ -/* - * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -/* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - -#include "includes.h" - -#ifdef GSSAPI -#include "auth.h" -#include "ssh2.h" -#include "xmalloc.h" -#include "log.h" -#include "dispatch.h" -#include "buffer.h" -#include "servconf.h" -#include "compat.h" -#include "bufaux.h" -#include "packet.h" - -#include <gssapi/gssapi.h> -#include "ssh-gss.h" - -extern ServerOptions options; -extern uchar_t *session_id2; -extern int session_id2_len; -extern Gssctxt *xxx_gssctxt; - -static void userauth_gssapi_finish(Authctxt *authctxt, Gssctxt *gssctxt); - -static void -userauth_gssapi_keyex(Authctxt *authctxt) -{ - gss_buffer_desc g_mic_data, mic_tok; - Buffer mic_data; - OM_uint32 maj_status, min_status; - - if (authctxt == NULL || authctxt->method == NULL) - fatal("No authentication context during gssapi-keyex userauth"); - - if (xxx_gssctxt == NULL || xxx_gssctxt->context == GSS_C_NO_CONTEXT) { - /* fatal()? or return? */ - debug("No GSS-API context during gssapi-keyex userauth"); - return; - } - - /* Make data buffer to verify MIC with */ - buffer_init(&mic_data); - buffer_put_string(&mic_data, session_id2, session_id2_len); - buffer_put_char(&mic_data, SSH2_MSG_USERAUTH_REQUEST); - buffer_put_cstring(&mic_data, authctxt->user); - buffer_put_cstring(&mic_data, authctxt->service); - buffer_put_cstring(&mic_data, authctxt->method->name); - - g_mic_data.value = buffer_ptr(&mic_data); - g_mic_data.length = buffer_len(&mic_data); - - mic_tok.value = packet_get_string(&mic_tok.length); - - maj_status = gss_verify_mic(&min_status, xxx_gssctxt->context, - &g_mic_data, &mic_tok, NULL); - - packet_check_eom(); - buffer_clear(&mic_data); - - if (maj_status != GSS_S_COMPLETE) - debug2("MIC verification failed, GSSAPI userauth failed"); - else - userauth_gssapi_finish(authctxt, xxx_gssctxt); - - /* Leave Gssctxt around for ssh_gssapi_cleanup/storecreds() */ - if (xxx_gssctxt->deleg_creds == GSS_C_NO_CREDENTIAL) - ssh_gssapi_delete_ctx(&xxx_gssctxt); -} - -static void ssh_gssapi_userauth_error(Gssctxt *ctxt); -static void input_gssapi_token(int type, u_int32_t plen, void *ctxt); -static void input_gssapi_mic(int type, u_int32_t plen, void *ctxt); -static void input_gssapi_errtok(int, u_int32_t, void *); -static void input_gssapi_exchange_complete(int type, u_int32_t plen, - void *ctxt); - -static void -userauth_gssapi_abandon(Authctxt *authctxt, Authmethod *method) -{ - ssh_gssapi_delete_ctx((Gssctxt **)&method->method_data); - xxx_gssctxt = NULL; - dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL); - dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); - dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); - dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL); -} - -static void -userauth_gssapi(Authctxt *authctxt) -{ - gss_OID_set supported_mechs; - int mechs, present = 0; - OM_uint32 min_status; - uint_t len; - char *doid = NULL; - gss_OID oid = GSS_C_NULL_OID; - - if (datafellows & SSH_OLD_GSSAPI) { - debug("Early drafts of GSSAPI userauth not supported"); - return; - } - - mechs = packet_get_int(); - if (mechs == 0) { - packet_check_eom(); - debug("Mechanism negotiation is not supported"); - return; - } - - ssh_gssapi_server_mechs(&supported_mechs); - - do { - mechs--; - - if (oid != GSS_C_NULL_OID) - ssh_gssapi_release_oid(&oid); - - doid = packet_get_string(&len); - - /* ick */ - if (doid[0] != 0x06 || (len > 2 && doid[1] != len - 2)) { - log("Mechanism OID received using the old " - "encoding form"); - oid = ssh_gssapi_make_oid(len, doid); - } else { - oid = ssh_gssapi_make_oid(len - 2, doid + 2); - } - - (void) gss_test_oid_set_member(&min_status, oid, - supported_mechs, &present); - - debug("Client offered gssapi userauth with %s (%s)", - ssh_gssapi_oid_to_str(oid), - present ? "supported" : "unsupported"); - } while (!present && (mechs > 0)); - - if (!present) { - /* userauth_finish() will send SSH2_MSG_USERAUTH_FAILURE */ - debug2("No mechanism offered by the client is available"); - ssh_gssapi_release_oid(&oid); - return; - } - - ssh_gssapi_build_ctx((Gssctxt **)&authctxt->method->method_data, - 0, oid); - ssh_gssapi_release_oid(&oid); - /* Send SSH_MSG_USERAUTH_GSSAPI_RESPONSE */ - - packet_start(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE); - - /* Just return whatever we found -- the matched mech does us no good */ - packet_put_string(doid, len); - xfree(doid); - - packet_send(); - packet_write_wait(); - - /* Setup rest of gssapi userauth conversation */ - dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, &input_gssapi_token); - dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, &input_gssapi_errtok); - authctxt->method->postponed = 1; -} - -static void -input_gssapi_token(int type, u_int32_t plen, void *ctxt) -{ - Authctxt *authctxt = ctxt; - Gssctxt *gssctxt; - gss_buffer_desc send_tok, recv_tok; - OM_uint32 maj_status, min_status; - uint_t len; - - if (authctxt == NULL || authctxt->method == NULL || - (authctxt->method->method_data == NULL)) { - fatal("No authentication or GSSAPI context during " - "gssapi-with-mic userauth"); - } - - gssctxt = authctxt->method->method_data; - recv_tok.value = packet_get_string(&len); - recv_tok.length = len; /* u_int vs. size_t */ - - maj_status = ssh_gssapi_accept_ctx(gssctxt, &recv_tok, &send_tok); - packet_check_eom(); - - if (GSS_ERROR(maj_status)) { - ssh_gssapi_userauth_error(gssctxt); - if (send_tok.length != 0) { - packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK); - packet_put_string(send_tok.value, send_tok.length); - packet_send(); - packet_write_wait(); - } - authctxt->method->postponed = 0; - dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); - userauth_finish(authctxt, authctxt->method->name); - } else { - if (send_tok.length != 0) { - packet_start(SSH2_MSG_USERAUTH_GSSAPI_TOKEN); - packet_put_string(send_tok.value, send_tok.length); - packet_send(); - packet_write_wait(); - } - if (maj_status == GSS_S_COMPLETE) { - dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); - dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, - &input_gssapi_mic); - dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, - &input_gssapi_exchange_complete); - } - } - - gss_release_buffer(&min_status, &send_tok); -} - -static void -input_gssapi_errtok(int type, u_int32_t plen, void *ctxt) -{ - Authctxt *authctxt = ctxt; - Gssctxt *gssctxt; - gss_buffer_desc send_tok, recv_tok; - - if (authctxt == NULL || authctxt->method == NULL || - (authctxt->method->method_data == NULL)) { - fatal("No authentication or GSSAPI context during " - "gssapi-with-mic userauth"); - } - - gssctxt = authctxt->method->method_data; - recv_tok.value = packet_get_string(&recv_tok.length); - packet_check_eom(); - - /* Push the error token into GSSAPI to see what it says */ - (void) ssh_gssapi_accept_ctx(gssctxt, &recv_tok, &send_tok); - - debug("Client sent GSS-API error token during GSS userauth-- %s", - ssh_gssapi_last_error(gssctxt, NULL, NULL)); - - /* We can't return anything to the client, even if we wanted to */ - dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); - dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL); - - - /* - * The client will have already moved on to the next auth and - * will send a new userauth request. The spec says that the - * server MUST NOT send a SSH_MSG_USERAUTH_FAILURE packet in - * response to this. - * - * We leave authctxt->method->postponed == 1 here so that a call - * to input_userauth_request() will detect this failure (as - * userauth abandonment) and act accordingly. - */ -} - -static void -input_gssapi_mic(int type, u_int32_t plen, void *ctxt) -{ - Authctxt *authctxt = ctxt; - Gssctxt *gssctxt; - gss_buffer_desc g_mic_data, mic_tok; - Buffer mic_data; - OM_uint32 maj_status, min_status; - - if (authctxt == NULL || authctxt->method == NULL || - (authctxt->method->method_data == NULL)) { - debug3("No authentication or GSSAPI context during " - "gssapi-with-mic userauth"); - return; - } - - gssctxt = authctxt->method->method_data; - - /* Make data buffer to verify MIC with */ - buffer_init(&mic_data); - buffer_put_string(&mic_data, session_id2, session_id2_len); - buffer_put_char(&mic_data, SSH2_MSG_USERAUTH_REQUEST); - buffer_put_cstring(&mic_data, authctxt->user); - buffer_put_cstring(&mic_data, authctxt->service); - buffer_put_cstring(&mic_data, authctxt->method->name); - - g_mic_data.value = buffer_ptr(&mic_data); - g_mic_data.length = buffer_len(&mic_data); - - mic_tok.value = packet_get_string(&mic_tok.length); - - maj_status = gss_verify_mic(&min_status, gssctxt->context, - &g_mic_data, &mic_tok, NULL); - - packet_check_eom(); - buffer_free(&mic_data); - - if (maj_status != GSS_S_COMPLETE) - debug2("MIC verification failed, GSSAPI userauth failed"); - else - userauth_gssapi_finish(authctxt, gssctxt); - - /* Delete context from keyex */ - if (xxx_gssctxt != gssctxt) - ssh_gssapi_delete_ctx(&xxx_gssctxt); - - /* Leave Gssctxt around for ssh_gssapi_cleanup/storecreds() */ - if (gssctxt->deleg_creds == GSS_C_NO_CREDENTIAL) - ssh_gssapi_delete_ctx(&gssctxt); - - xxx_gssctxt = gssctxt; - - authctxt->method->postponed = 0; - dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); - dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL); - dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL); - userauth_finish(authctxt, authctxt->method->name); -} - -/* - * This is called when the client thinks we've completed authentication. - * It should only be enabled in the dispatch handler by the function above, - * which only enables it once the GSSAPI exchange is complete. - */ -static void -input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt) -{ - Authctxt *authctxt = ctxt; - Gssctxt *gssctxt; - - packet_check_eom(); - - if (authctxt == NULL || authctxt->method == NULL || - (authctxt->method->method_data == NULL)) - fatal("No authentication or GSSAPI context"); - - gssctxt = authctxt->method->method_data; - - /* - * SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE -> gssapi userauth - * failure, the client should use SSH2_MSG_USERAUTH_GSSAPI_MIC - * instead. - * - * There's two reasons for this: - * - * 1) we don't have GSS mechs that don't support integrity - * protection, and even if we did we'd not want to use them with - * SSHv2, and, - * - * 2) we currently have no way to dynamically detect whether a - * given mechanism does or does not support integrity - * protection, so when a context's flags do not indicate - * integrity protection we can't know if the client simply - * didn't request it, so we assume it didn't and reject the - * userauth. - * - * We could fail partially (i.e., force the use of other - * userauth methods without counting this one as failed). But - * this will do for now. - */ -#if 0 - authctxt->method->authenticated = ssh_gssapi_userok(gssctxt, - authctxt->user); -#endif - - if (xxx_gssctxt != gssctxt) - ssh_gssapi_delete_ctx(&gssctxt); - ssh_gssapi_delete_ctx(&gssctxt); - authctxt->method->postponed = 0; - dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); - dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL); - dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL); - dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); - userauth_finish(authctxt, authctxt->method->name); -} - -static void -ssh_gssapi_userauth_error(Gssctxt *ctxt) -{ - char *errstr; - OM_uint32 maj, min; - - errstr = ssh_gssapi_last_error(ctxt, &maj, &min); - if (errstr) { - packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERROR); - packet_put_int(maj); - packet_put_int(min); - packet_put_cstring(errstr); - packet_put_cstring(""); - packet_send(); - packet_write_wait(); - xfree(errstr); - } -} - -/* - * Code common to gssapi-keyex and gssapi-with-mic userauth. - * - * Does authorization, figures out how to store delegated creds. - */ -static void -userauth_gssapi_finish(Authctxt *authctxt, Gssctxt *gssctxt) -{ - char *local_user = NULL; - gss_buffer_desc dispname; - OM_uint32 major; - - if (*authctxt->user != '\0' && - ssh_gssapi_userok(gssctxt, authctxt->user)) { - - /* - * If the client princ did not map to the requested - * username then we don't want to clobber existing creds - * for the user with the delegated creds. - */ - local_user = ssh_gssapi_localname(gssctxt); - if (local_user == NULL || - strcmp(local_user, authctxt->user) == 0) - gssctxt->default_creds = 1; /* store creds as default */ - - authctxt->method->authenticated = - do_pam_non_initial_userauth(authctxt); - - } else if (*authctxt->user == '\0') { - /* Requested username == ""; derive username from princ name */ - if ((local_user = ssh_gssapi_localname(gssctxt)) == NULL) - return; - - /* Changed username (from implicit, '') */ - userauth_user_svc_change(authctxt, local_user, NULL); - - gssctxt->default_creds = 1; /* store creds as default */ - - authctxt->method->authenticated = - do_pam_non_initial_userauth(authctxt); - } - - if (local_user != NULL) - xfree(local_user); - - if (*authctxt->user != '\0' && authctxt->method->authenticated != 0) { - major = gss_display_name(&gssctxt->minor, gssctxt->src_name, - &dispname, NULL); - if (major == GSS_S_COMPLETE) { - log("Authorized principal %.*s, authenticated with " - "GSS mechanism %s, to: %s", - dispname.length, (char *)dispname.value, - ssh_gssapi_oid_to_name(gssctxt->actual_mech), - authctxt->user); - } - (void) gss_release_buffer(&gssctxt->minor, &dispname); - } -} - -#if 0 -/* Deprecated userauths -- should not be enabled */ -Authmethod method_external = { - "external-keyx", - &options.gss_authentication, - userauth_gssapi_keyex, - NULL, /* no abandon function */ - NULL, - NULL, - /* State counters */ - 0, 0, 0, 0, - /* State flags */ - 0, 0, 0, 0, 0, 0 -}; - -Authmethod method_gssapi = { - "gssapi", - &options.gss_authentication, - userauth_gssapi, - userauth_gssapi_abandon, - NULL, - NULL, - /* State counters */ - 0, 0, 0, 0, - /* State flags */ - 0, 0, 0, 0, 0, 0 -}; -#endif - -Authmethod method_external = { - "gssapi-keyex", - &options.gss_authentication, - userauth_gssapi_keyex, - NULL, /* no abandon function */ - NULL, - NULL, - /* State counters */ - 0, 0, 0, 0, - /* State flags */ - 0, 0, 0, 0, 0, 0 -}; - -Authmethod method_gssapi = { - "gssapi-with-mic", - &options.gss_authentication, - userauth_gssapi, - userauth_gssapi_abandon, - NULL, - NULL, - /* State counters */ - 0, 0, 0, 0, - /* State flags */ - 0, 0, 0, 0, 0, 0 -}; - -#endif /* GSSAPI */ diff --git a/usr/src/cmd/ssh/sshd/auth2-hostbased.c b/usr/src/cmd/ssh/sshd/auth2-hostbased.c deleted file mode 100644 index c88e308100..0000000000 --- a/usr/src/cmd/ssh/sshd/auth2-hostbased.c +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright (c) 2000 Markus Friedl. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -/* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - -#include "includes.h" -RCSID("$OpenBSD: auth2-hostbased.c,v 1.2 2002/05/31 11:35:15 markus Exp $"); - -#pragma ident "%Z%%M% %I% %E% SMI" - -#include "ssh2.h" -#include "xmalloc.h" -#include "packet.h" -#include "buffer.h" -#include "log.h" -#include "servconf.h" -#include "compat.h" -#include "bufaux.h" -#include "auth.h" - -#ifdef USE_PAM -#include "auth-pam.h" -#endif /* USE_PAM */ - -#include "key.h" -#include "canohost.h" -#include "pathnames.h" - -/* import */ -extern ServerOptions options; -extern u_char *session_id2; -extern int session_id2_len; - -static void -userauth_hostbased(Authctxt *authctxt) -{ - Buffer b; - Key *key = NULL; - char *pkalg, *cuser, *chost, *service; - u_char *pkblob, *sig; - u_int alen, blen, slen; - int pktype; - int authenticated = 0; - - if (!authctxt || !authctxt->method) - fatal("%s: missing context", __func__); - - pkalg = packet_get_string(&alen); - pkblob = packet_get_string(&blen); - chost = packet_get_string(NULL); - cuser = packet_get_string(NULL); - sig = packet_get_string(&slen); - - debug("userauth_hostbased: cuser %s chost %s pkalg %s slen %d", - cuser, chost, pkalg, slen); -#ifdef DEBUG_PK - debug("signature:"); - buffer_init(&b); - buffer_append(&b, sig, slen); - buffer_dump(&b); - buffer_free(&b); -#endif - pktype = key_type_from_name(pkalg); - if (pktype == KEY_UNSPEC) { - /* this is perfectly legal */ - log("userauth_hostbased: unsupported " - "public key algorithm: %s", pkalg); - goto done; - } - key = key_from_blob(pkblob, blen); - if (key == NULL) { - error("userauth_hostbased: cannot decode key: %s", pkalg); - goto done; - } - if (key->type != pktype) { - error("userauth_hostbased: type mismatch for decoded key " - "(received %d, expected %d)", key->type, pktype); - goto done; - } - service = datafellows & SSH_BUG_HBSERVICE ? "ssh-userauth" : - authctxt->service; - buffer_init(&b); - buffer_put_string(&b, session_id2, session_id2_len); - /* reconstruct packet */ - buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); - buffer_put_cstring(&b, authctxt->user); - buffer_put_cstring(&b, service); - buffer_put_cstring(&b, "hostbased"); - buffer_put_string(&b, pkalg, alen); - buffer_put_string(&b, pkblob, blen); - buffer_put_cstring(&b, chost); - buffer_put_cstring(&b, cuser); -#ifdef DEBUG_PK - buffer_dump(&b); -#endif - /* test for allowed key and correct signature */ - authenticated = 0; - if (hostbased_key_allowed(authctxt->pw, cuser, chost, key) && - key_verify(key, sig, slen, buffer_ptr(&b), buffer_len(&b)) == 1) - authenticated = 1; - - buffer_clear(&b); -done: - /* - * XXX TODO: Add config options for specifying users for whom - * this userauth is insufficient and what userauths - * may continue. - * - * NOTE: do_pam_non_initial_userauth() does this for - * users with expired passwords. - */ -#ifdef USE_PAM - if (authenticated) { - authctxt->cuser = cuser; - if (!do_pam_non_initial_userauth(authctxt)) - authenticated = 0; - /* Make sure nobody else will use this pointer since we are - * going to free that string. */ - authctxt->cuser = NULL; - } -#endif /* USE_PAM */ - - if (authenticated) - authctxt->method->authenticated = 1; - - debug2("userauth_hostbased: authenticated %d", authenticated); - if (key != NULL) - key_free(key); - xfree(pkalg); - xfree(pkblob); - xfree(cuser); - xfree(chost); - xfree(sig); - return; -} - -/* return 1 if given hostkey is allowed */ -int -hostbased_key_allowed(struct passwd *pw, const char *cuser, char *chost, - Key *key) -{ - const char *resolvedname, *ipaddr, *lookup; - HostStatus host_status; - int len; - - resolvedname = get_canonical_hostname(options.verify_reverse_mapping); - ipaddr = get_remote_ipaddr(); - - debug2("userauth_hostbased: chost %s resolvedname %s ipaddr %s", - chost, resolvedname, ipaddr); - - if (pw == NULL) - return 0; - - if (options.hostbased_uses_name_from_packet_only) { - if (auth_rhosts2(pw, cuser, chost, chost) == 0) - return 0; - lookup = chost; - } else { - if (((len = strlen(chost)) > 0) && chost[len - 1] == '.') { - debug2("stripping trailing dot from chost %s", chost); - chost[len - 1] = '\0'; - } - if (strcasecmp(resolvedname, chost) != 0) - log("userauth_hostbased mismatch: " - "client sends %s, but we resolve %s to %s", - chost, ipaddr, resolvedname); - if (auth_rhosts2(pw, cuser, resolvedname, ipaddr) == 0) - return 0; - lookup = resolvedname; - } - debug2("userauth_hostbased: access allowed by auth_rhosts2"); - - host_status = check_key_in_hostfiles(pw, key, lookup, - _PATH_SSH_SYSTEM_HOSTFILE, - options.ignore_user_known_hosts ? NULL : _PATH_SSH_USER_HOSTFILE); - - /* backward compat if no key has been found. */ - if (host_status == HOST_NEW) - host_status = check_key_in_hostfiles(pw, key, lookup, - _PATH_SSH_SYSTEM_HOSTFILE2, - options.ignore_user_known_hosts ? NULL : - _PATH_SSH_USER_HOSTFILE2); - - return (host_status == HOST_OK); -} - -Authmethod method_hostbased = { - "hostbased", - &options.hostbased_authentication, - userauth_hostbased, - NULL, /* no abandon function */ - NULL, NULL, /* method data and hist data */ - 0, /* is not initial userauth */ - 0, 0, 0, /* counters */ - 0, 0, 0, 0, 0, 0 /* state */ -}; diff --git a/usr/src/cmd/ssh/sshd/auth2-kbdint.c b/usr/src/cmd/ssh/sshd/auth2-kbdint.c deleted file mode 100644 index 2ea8104182..0000000000 --- a/usr/src/cmd/ssh/sshd/auth2-kbdint.c +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2000 Markus Friedl. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -/* - * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. - */ - -#include "includes.h" -RCSID("$OpenBSD: auth2-kbdint.c,v 1.2 2002/05/31 11:35:15 markus Exp $"); - -#include "packet.h" -#include "auth.h" -#include "log.h" -#include "servconf.h" -#include "xmalloc.h" - -/* import */ -extern ServerOptions options; - -static void -userauth_kbdint(Authctxt *authctxt) -{ - char *lang, *devs; - - if (!authctxt || !authctxt->method) - fatal("%s: missing contex", __func__); - - lang = packet_get_string(NULL); - devs = packet_get_string(NULL); - packet_check_eom(); - - debug("keyboard-interactive devs %s", devs); - -#ifdef USE_PAM - if (options.kbd_interactive_authentication) - auth2_pam(authctxt); -#else - if (options.challenge_response_authentication) - auth2_challenge(authctxt, devs); -#endif /* USE_PAM */ - xfree(devs); - xfree(lang); -#ifdef HAVE_CYGWIN - if (check_nt_auth(0, authctxt->pw) == 0) { - authctxt->method->authenticated = 0; - return; - } -#endif -} - -static void -userauth_kbdint_abandon(Authctxt *authctxt, Authmethod *method) -{ -#ifdef USE_PAM - kbdint_pam_abandon(authctxt, method); -#else - auth2_challenge_abandon(authctxt); -#endif /* USE_PAM */ -} - -Authmethod method_kbdint = { - "keyboard-interactive", - &options.kbd_interactive_authentication, - userauth_kbdint, - userauth_kbdint_abandon, - NULL, NULL, /* method data and historical data */ - 1, /* initial userauth */ - 0, 0, 0, /* counters */ - 0, 0, 0, 0, 0, 0 /* state */ -}; diff --git a/usr/src/cmd/ssh/sshd/auth2-none.c b/usr/src/cmd/ssh/sshd/auth2-none.c deleted file mode 100644 index 5d49ee95e8..0000000000 --- a/usr/src/cmd/ssh/sshd/auth2-none.c +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (c) 2000 Markus Friedl. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -/* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - -#include "includes.h" -RCSID("$OpenBSD: auth2-none.c,v 1.4 2002/06/27 10:35:47 deraadt Exp $"); - -#include "auth.h" -#include "xmalloc.h" -#include "packet.h" -#include "log.h" -#include "servconf.h" -#include "atomicio.h" -#include "compat.h" -#include "ssh2.h" - -/* import */ -extern ServerOptions options; - -/* "none" is allowed only one time */ -static int none_enabled = 1; - -char * -auth2_read_banner(void) -{ - struct stat st; - char *banner, *ubanner, *errstr; - off_t len, n; - int fd; - uint_t ilen; - - if ((fd = open(options.banner, O_RDONLY)) == -1) - return (NULL); - if (fstat(fd, &st) == -1) { - close(fd); - return (NULL); - } - len = st.st_size; - banner = xmalloc(len + 1); - n = atomicio(read, fd, banner, len); - close(fd); - - if (n != len) { - xfree(banner); - return (NULL); - } - banner[n] = '\0'; - - if (datafellows & SSH_BUG_STRING_ENCODING) { - ubanner = banner; - } else { - ilen = (uint_t)n; - ubanner = g11n_convert_to_utf8(banner, &ilen, 1, &errstr); - if (ubanner == NULL) { - if (errstr != NULL) { - error("Can't convert banner contents " - "to UTF-8: %s\n", errstr); - } - ubanner = banner; - } else { - xfree(banner); - } - } - - return (ubanner); -} - -static void -userauth_banner(void) -{ - char *banner = NULL; - - if (options.banner == NULL || (datafellows & SSH_BUG_BANNER)) - return; - - if ((banner = auth2_read_banner()) == NULL) - goto done; - - packet_start(SSH2_MSG_USERAUTH_BANNER); - packet_put_cstring(banner); - packet_put_cstring(""); /* language, unused */ - packet_send(); - debug("userauth_banner: sent"); -done: - if (banner) - xfree(banner); -} - -static void -userauth_none(Authctxt *authctxt) -{ - none_enabled = 0; - - if (!authctxt || !authctxt->method) - fatal("%s: missing context", __func__); - - packet_check_eom(); - userauth_banner(); -#ifdef HAVE_CYGWIN - if (check_nt_auth(1, authctxt->pw) == 0) - return (0); -#endif - authctxt->method->authenticated = auth_password(authctxt, ""); -} - -Authmethod method_none = { - "none", - &none_enabled, - userauth_none, - NULL, /* no abandon function */ - NULL, NULL, /* method data and hist data */ - 0, /* not really initial userauth */ - 0, 0, 0, /* counters */ - 0, 0, 0, 0, 0, 0 /* state */ -}; diff --git a/usr/src/cmd/ssh/sshd/auth2-pam.c b/usr/src/cmd/ssh/sshd/auth2-pam.c deleted file mode 100644 index 1b0fa40f2b..0000000000 --- a/usr/src/cmd/ssh/sshd/auth2-pam.c +++ /dev/null @@ -1,458 +0,0 @@ -/* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - -#include "includes.h" - -RCSID("$Id: auth2-pam.c,v 1.14 2002/06/28 16:48:12 mouring Exp $"); - -#ifdef USE_PAM -#include <security/pam_appl.h> - -#include "ssh.h" -#include "ssh2.h" -#include "auth.h" -#include "auth-pam.h" -#include "auth-options.h" -#include "packet.h" -#include "xmalloc.h" -#include "dispatch.h" -#include "canohost.h" -#include "log.h" -#include "servconf.h" -#include "misc.h" - -#ifdef HAVE_BSM -#include "bsmaudit.h" -#endif /* HAVE_BSM */ - -extern u_int utmp_len; -extern ServerOptions options; - -extern Authmethod method_kbdint; -extern Authmethod method_passwd; - -#define SSHD_PAM_KBDINT_SVC "sshd-kbdint" -/* Maximum attempts for changing expired password */ -#define DEF_ATTEMPTS 3 - -static int do_pam_conv_kbd_int(int num_msg, - struct pam_message **msg, struct pam_response **resp, - void *appdata_ptr); -static void input_userauth_info_response_pam(int type, - u_int32_t seqnr, - void *ctxt); - -static struct pam_conv conv2 = { - do_pam_conv_kbd_int, - NULL, -}; - -static void do_pam_kbdint_cleanup(pam_handle_t *pamh); -static void do_pam_kbdint(Authctxt *authctxt); - -void -auth2_pam(Authctxt *authctxt) -{ - if (authctxt->user == NULL) - fatal("auth2_pam: internal error: no user"); - if (authctxt->method == NULL) - fatal("auth2_pam: internal error: no method"); - - conv2.appdata_ptr = authctxt; - new_start_pam(authctxt, &conv2); - - authctxt->method->method_data = NULL; /* freed in the conv func */ - dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, - &input_userauth_info_response_pam); - - /* - * Since password userauth and keyboard-interactive userauth - * both use PAM, and since keyboard-interactive is so much - * better than password userauth, we should not allow the user - * to try password userauth after trying keyboard-interactive. - */ - if (method_passwd.enabled) - *method_passwd.enabled = 0; - - do_pam_kbdint(authctxt); - - dispatch_set(SSH2_MSG_USERAUTH_INFO_RESPONSE, NULL); -} - -static void -do_pam_kbdint(Authctxt *authctxt) -{ - int retval, retval2; - pam_handle_t *pamh = authctxt->pam->h; - const char *where = "authenticating"; - char *text = NULL; - - debug2("Calling pam_authenticate()"); - retval = pam_authenticate(pamh, - options.permit_empty_passwd ? 0 : - PAM_DISALLOW_NULL_AUTHTOK); - - if (retval != PAM_SUCCESS) - goto cleanup; - - debug2("kbd-int: pam_authenticate() succeeded"); - where = "authorizing"; - retval = pam_acct_mgmt(pamh, 0); - - if (retval == PAM_NEW_AUTHTOK_REQD) { - if (authctxt->valid && authctxt->pw != NULL) { - /* send password expiration warning */ - message_cat(&text, - gettext("Warning: Your password has expired," - " please change it now.")); - packet_start(SSH2_MSG_USERAUTH_INFO_REQUEST); - packet_put_cstring(""); /* name */ - packet_put_utf8_cstring(text); /* instructions */ - packet_put_cstring(""); /* language, unused */ - packet_put_int(0); - packet_send(); - packet_write_wait(); - debug("expiration message sent"); - if (text) - xfree(text); - /* - * wait for the response so it does not mix - * with the upcoming PAM conversation - */ - packet_read_expect(SSH2_MSG_USERAUTH_INFO_RESPONSE); - /* - * Can't use temporarily_use_uid() and restore_uid() - * here because we need (euid == 0 && ruid == pw_uid) - * whereas temporarily_use_uid() arranges for - * (suid = 0 && euid == pw_uid && ruid == pw_uid). - */ - (void) setreuid(authctxt->pw->pw_uid, -1); - debug2("kbd-int: changing expired password"); - where = "changing authentication tokens (password)"; - /* - * Depending on error returned from pam_chauthtok, we - * need to try to change password a few times before - * we error out and return. - */ - int tries = 0; - while ((retval = pam_chauthtok(pamh, - PAM_CHANGE_EXPIRED_AUTHTOK)) != PAM_SUCCESS) { - if (tries++ < DEF_ATTEMPTS) { - if ((retval == PAM_AUTHTOK_ERR) || - (retval == PAM_TRY_AGAIN)) { - continue; - } - } - break; - } - audit_sshd_chauthtok(retval, authctxt->pw->pw_uid, - authctxt->pw->pw_gid); - (void) setreuid(0, -1); - } else { - retval = PAM_PERM_DENIED; - } - } - - if (retval != PAM_SUCCESS) - goto cleanup; - - authctxt->pam->state |= PAM_S_DONE_ACCT_MGMT; - - retval = finish_userauth_do_pam(authctxt); - - if (retval != PAM_SUCCESS) - goto cleanup; - - /* - * PAM handle stays around so we can call pam_close_session() - * on it later. - */ - authctxt->method->authenticated = 1; - debug2("kbd-int: success (pam->state == %x)", authctxt->pam->state); - return; - -cleanup: - /* - * Check for abandonment and cleanup. When kbdint is abandoned - * authctxt->pam->h is NULLed and by this point a new handle may - * be allocated. - */ - if (authctxt->pam->h != pamh) { - log("Keyboard-interactive (PAM) userauth abandoned " - "while %s", where); - if ((retval2 = pam_end(pamh, retval)) != PAM_SUCCESS) { - log("Cannot close PAM handle after " - "kbd-int userauth abandonment[%d]: %.200s", - retval2, PAM_STRERROR(pamh, retval2)); - } - authctxt->method->abandoned = 1; - - /* - * Avoid double counting; these are incremented in - * kbdint_pam_abandon() so that they reflect the correct - * count when userauth_finish() is called before - * unwinding the dispatch_run() loop, but they are - * incremented again in input_userauth_request() when - * the loop is unwound, right here. - */ - if (authctxt->method->abandons) - authctxt->method->abandons--; - if (authctxt->method->attempts) - authctxt->method->attempts--; - } - else { - /* Save error value for pam_end() */ - authctxt->pam->last_pam_retval = retval; - log("Keyboard-interactive (PAM) userauth failed[%d] " - "while %s: %.200s", retval, where, - PAM_STRERROR(pamh, retval)); - /* pam handle can be reused elsewhere, so no pam_end() here */ - } - - return; -} - -static int -do_pam_conv_kbd_int(int num_msg, struct pam_message **msg, - struct pam_response **resp, void *appdata_ptr) -{ - int i, j; - char *text; - Convctxt *conv_ctxt; - Authctxt *authctxt = (Authctxt *)appdata_ptr; - - if (!authctxt || !authctxt->method) { - debug("Missing state during PAM conversation"); - return PAM_CONV_ERR; - } - - conv_ctxt = xmalloc(sizeof(Convctxt)); - (void) memset(conv_ctxt, 0, sizeof(Convctxt)); - conv_ctxt->finished = 0; - conv_ctxt->num_received = 0; - conv_ctxt->num_expected = 0; - conv_ctxt->prompts = xmalloc(sizeof(int) * num_msg); - conv_ctxt->responses = xmalloc(sizeof(struct pam_response) * num_msg); - (void) memset(conv_ctxt->responses, 0, sizeof(struct pam_response) * num_msg); - - text = NULL; - for (i = 0, conv_ctxt->num_expected = 0; i < num_msg; i++) { - int style = PAM_MSG_MEMBER(msg, i, msg_style); - switch (style) { - case PAM_PROMPT_ECHO_ON: - debug2("PAM echo on prompt: %s", - PAM_MSG_MEMBER(msg, i, msg)); - conv_ctxt->num_expected++; - break; - case PAM_PROMPT_ECHO_OFF: - debug2("PAM echo off prompt: %s", - PAM_MSG_MEMBER(msg, i, msg)); - conv_ctxt->num_expected++; - break; - case PAM_TEXT_INFO: - debug2("PAM text info prompt: %s", - PAM_MSG_MEMBER(msg, i, msg)); - message_cat(&text, PAM_MSG_MEMBER(msg, i, msg)); - break; - case PAM_ERROR_MSG: - debug2("PAM error prompt: %s", - PAM_MSG_MEMBER(msg, i, msg)); - message_cat(&text, PAM_MSG_MEMBER(msg, i, msg)); - break; - default: - /* Capture all these messages to be sent at once */ - message_cat(&text, PAM_MSG_MEMBER(msg, i, msg)); - break; - } - } - - if (conv_ctxt->num_expected == 0 && text == NULL) { - xfree(conv_ctxt->prompts); - xfree(conv_ctxt->responses); - xfree(conv_ctxt); - return PAM_SUCCESS; - } - - authctxt->method->method_data = (void *) conv_ctxt; - - packet_start(SSH2_MSG_USERAUTH_INFO_REQUEST); - packet_put_cstring(""); /* Name */ - packet_put_utf8_cstring(text ? text : ""); /* Instructions */ - packet_put_cstring(""); /* Language */ - packet_put_int(conv_ctxt->num_expected); - - if (text) - xfree(text); - - for (i = 0, j = 0; i < num_msg; i++) { - int style = PAM_MSG_MEMBER(msg, i, msg_style); - - /* Skip messages which don't need a reply */ - if (style != PAM_PROMPT_ECHO_ON && style != PAM_PROMPT_ECHO_OFF) - continue; - - conv_ctxt->prompts[j++] = i; - packet_put_utf8_cstring(PAM_MSG_MEMBER(msg, i, msg)); - packet_put_char(style == PAM_PROMPT_ECHO_ON); - } - packet_send(); - packet_write_wait(); - - /* - * Here the dispatch_run() loop is nested. It should be unwound - * if keyboard-interactive userauth is abandoned (or restarted; - * same thing). - * - * The condition for breaking out of the nested dispatch_run() loop is - * ((got kbd-int info reponse) || (kbd-int abandoned)) - * - * conv_ctxt->finished is set in either of those cases. - * - * When abandonment is detected the conv_ctxt->finished is set as - * is conv_ctxt->abandoned, causing this function to signal - * userauth nested dispatch_run() loop unwinding and to return - * PAM_CONV_ERR; - */ - debug2("Nesting dispatch_run loop"); - dispatch_run(DISPATCH_BLOCK, &conv_ctxt->finished, appdata_ptr); - debug2("Nested dispatch_run loop exited"); - - if (conv_ctxt->abandoned) { - authctxt->unwind_dispatch_loop = 1; - xfree(conv_ctxt->prompts); - xfree(conv_ctxt->responses); - xfree(conv_ctxt); - debug("PAM conv function returns PAM_CONV_ERR"); - return PAM_CONV_ERR; - } - - if (conv_ctxt->num_received == conv_ctxt->num_expected) { - *resp = conv_ctxt->responses; - xfree(conv_ctxt->prompts); - xfree(conv_ctxt); - debug("PAM conv function returns PAM_SUCCESS"); - return PAM_SUCCESS; - } - - debug("PAM conv function returns PAM_CONV_ERR"); - xfree(conv_ctxt->prompts); - xfree(conv_ctxt->responses); - xfree(conv_ctxt); - return PAM_CONV_ERR; -} - -static void -input_userauth_info_response_pam(int type, u_int32_t seqnr, void *ctxt) -{ - Authctxt *authctxt = ctxt; - Convctxt *conv_ctxt; - unsigned int nresp = 0, rlen = 0, i = 0; - char *resp; - - if (authctxt == NULL) - fatal("input_userauth_info_response_pam: no authentication context"); - - /* Check for spurious/unexpected info response */ - if (method_kbdint.method_data == NULL) { - debug("input_userauth_info_response_pam: no method context"); - return; - } - - conv_ctxt = (Convctxt *) method_kbdint.method_data; - - nresp = packet_get_int(); /* Number of responses. */ - debug("got %d responses", nresp); - - -#if 0 - if (nresp != conv_ctxt->num_expected) - fatal("%s: Received incorrect number of responses " - "(expected %d, received %u)", __func__, - conv_ctxt->num_expected, nresp); -#endif - - if (nresp > 100) - fatal("%s: too many replies", __func__); - - for (i = 0; i < nresp && i < conv_ctxt->num_expected ; i++) { - int j = conv_ctxt->prompts[i]; - - /* - * We assume that ASCII charset is used for password - * although the protocol requires UTF-8 encoding for the - * password string. Therefore, we don't perform code - * conversion for the string. - */ - resp = packet_get_string(&rlen); - if (i < conv_ctxt->num_expected) { - conv_ctxt->responses[j].resp_retcode = PAM_SUCCESS; - conv_ctxt->responses[j].resp = xstrdup(resp); - conv_ctxt->num_received++; - } - xfree(resp); - } - - if (nresp < conv_ctxt->num_expected) - fatal("%s: too few replies (%d < %d)", __func__, - nresp, conv_ctxt->num_expected); - - /* XXX - This could make a covert channel... */ - if (nresp > conv_ctxt->num_expected) - debug("Ignoring additional PAM replies"); - - conv_ctxt->finished = 1; - - packet_check_eom(); -} - -#if 0 -int -kbdint_pam_abandon_chk(Authctxt *authctxt, Authmethod *method) -{ - if (!method) - return 0; /* fatal(), really; it'll happen somewhere else */ - - if (!method->method_data) - return 0; - - return 1; -} -#endif - -void -kbdint_pam_abandon(Authctxt *authctxt, Authmethod *method) -{ - Convctxt *conv_ctxt; - - /* - * But, if it ever becomes desirable and possible to support - * kbd-int userauth abandonment, here's what must be done. - */ - if (!method) - return; - - if (!method->method_data) - return; - - conv_ctxt = (Convctxt *) method->method_data; - - /* dispatch_run() loop will exit */ - conv_ctxt->abandoned = 1; - conv_ctxt->finished = 1; - - /* - * The method_data will be free in the corresponding, active - * conversation function - */ - method->method_data = NULL; - - /* update counts that can't be updated elsewhere */ - method->abandons++; - method->attempts++; - - /* Finally, we cannot re-use the current current PAM handle */ - authctxt->pam->h = NULL; /* Let the conv function cleanup */ -} -#endif diff --git a/usr/src/cmd/ssh/sshd/auth2-passwd.c b/usr/src/cmd/ssh/sshd/auth2-passwd.c deleted file mode 100644 index 9a1837fb05..0000000000 --- a/usr/src/cmd/ssh/sshd/auth2-passwd.c +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (c) 2000 Markus Friedl. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -/* - * Copyright 2007 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - -#include "includes.h" -RCSID("$OpenBSD: auth2-passwd.c,v 1.2 2002/05/31 11:35:15 markus Exp $"); - -#pragma ident "%Z%%M% %I% %E% SMI" - -#include "xmalloc.h" -#include "packet.h" -#include "log.h" -#include "auth.h" -#include "servconf.h" - -/* import */ -extern ServerOptions options; - -static void -userauth_passwd(Authctxt *authctxt) -{ - char *password; - int change; - u_int len; - - if (!authctxt || !authctxt->method) - fatal("%s: missing context", __func__); - - change = packet_get_char(); - if (change) - log("password change not supported"); - password = packet_get_string(&len); - packet_check_eom(); - if ( -#ifdef HAVE_CYGWIN - check_nt_auth(1, authctxt->pw) && -#endif - auth_password(authctxt, password) == 1) { - authctxt->method->authenticated = 1; - } - memset(password, 0, len); - xfree(password); -} - -Authmethod method_passwd = { - "password", - &options.password_authentication, - userauth_passwd, - NULL, /* no abandon function */ - NULL, NULL, /* method data and hist data */ - 1, /* initial userauth */ - 0, 0, 0, /* counters */ - 0, 0, 0, 0, 0, 0 /* state */ -}; diff --git a/usr/src/cmd/ssh/sshd/auth2-pubkey.c b/usr/src/cmd/ssh/sshd/auth2-pubkey.c deleted file mode 100644 index 658634c195..0000000000 --- a/usr/src/cmd/ssh/sshd/auth2-pubkey.c +++ /dev/null @@ -1,359 +0,0 @@ -/* - * Copyright (c) 2000 Markus Friedl. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -/* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - -#include "includes.h" -RCSID("$OpenBSD: auth2-pubkey.c,v 1.2 2002/05/31 11:35:15 markus Exp $"); - -#include "ssh2.h" -#include "xmalloc.h" -#include "packet.h" -#include "buffer.h" -#include "log.h" -#include "servconf.h" -#include "compat.h" -#include "bufaux.h" -#include "auth.h" -#include "key.h" -#include "pathnames.h" -#include "uidswap.h" -#include "auth-options.h" -#include "canohost.h" - -#ifdef USE_PAM -#include <security/pam_appl.h> -#include "auth-pam.h" -#endif /* USE_PAM */ - -/* import */ -extern ServerOptions options; -extern u_char *session_id2; -extern int session_id2_len; - -static void -userauth_pubkey(Authctxt *authctxt) -{ - Buffer b; - Key *key = NULL; - char *pkalg; - u_char *pkblob, *sig; - u_int alen, blen, slen; - int have_sig, pktype; - int authenticated = 0; - - if (!authctxt || !authctxt->method) - fatal("%s: missing context", __func__); - - have_sig = packet_get_char(); - if (datafellows & SSH_BUG_PKAUTH) { - debug2("userauth_pubkey: SSH_BUG_PKAUTH"); - /* no explicit pkalg given */ - pkblob = packet_get_string(&blen); - buffer_init(&b); - buffer_append(&b, pkblob, blen); - /* so we have to extract the pkalg from the pkblob */ - pkalg = buffer_get_string(&b, &alen); - buffer_free(&b); - } else { - pkalg = packet_get_string(&alen); - pkblob = packet_get_string(&blen); - } - pktype = key_type_from_name(pkalg); - if (pktype == KEY_UNSPEC) { - /* this is perfectly legal */ - log("userauth_pubkey: unsupported public key algorithm: %s", - pkalg); - goto done; - } - key = key_from_blob(pkblob, blen); - if (key == NULL) { - error("userauth_pubkey: cannot decode key: %s", pkalg); - goto done; - } - if (key->type != pktype) { - error("userauth_pubkey: type mismatch for decoded key " - "(received %d, expected %d)", key->type, pktype); - goto done; - } - - /* Detect and count abandonment */ - if (authctxt->method->method_data) { - Key *prev_key; - unsigned char *prev_pkblob; - int prev_blen; - - /* - * Check for earlier test of a key that was allowed but - * not followed up with a pubkey req for the same pubkey - * and with a signature. - */ - prev_key = authctxt->method->method_data; - if ((prev_blen = key_to_blob(prev_key, - &prev_pkblob, NULL))) { - if (prev_blen != blen || - memcmp(prev_pkblob, pkblob, blen) != 0) { - authctxt->method->abandons++; - authctxt->method->attempts++; - } - } - key_free(prev_key); - authctxt->method->method_data = NULL; - } - - if (have_sig) { - sig = packet_get_string(&slen); - packet_check_eom(); - buffer_init(&b); - if (datafellows & SSH_OLD_SESSIONID) { - buffer_append(&b, session_id2, session_id2_len); - } else { - buffer_put_string(&b, session_id2, session_id2_len); - } - /* reconstruct packet */ - buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); - buffer_put_cstring(&b, authctxt->user); - buffer_put_cstring(&b, - datafellows & SSH_BUG_PKSERVICE ? - "ssh-userauth" : - authctxt->service); - if (datafellows & SSH_BUG_PKAUTH) { - buffer_put_char(&b, have_sig); - } else { - buffer_put_cstring(&b, "publickey"); - buffer_put_char(&b, have_sig); - buffer_put_cstring(&b, pkalg); - } - buffer_put_string(&b, pkblob, blen); -#ifdef DEBUG_PK - buffer_dump(&b); -#endif - /* test for correct signature */ - if (user_key_allowed(authctxt->pw, key) && - key_verify(key, sig, slen, buffer_ptr(&b), - buffer_len(&b)) == 1) { - authenticated = 1; - } - authctxt->method->postponed = 0; - buffer_free(&b); - xfree(sig); - } else { - debug("test whether pkalg/pkblob are acceptable"); - packet_check_eom(); - - /* XXX fake reply and always send PK_OK ? */ - /* - * XXX this allows testing whether a user is allowed - * to login: if you happen to have a valid pubkey this - * message is sent. the message is NEVER sent at all - * if a user is not allowed to login. is this an - * issue? -markus - */ - if (user_key_allowed(authctxt->pw, key)) { - packet_start(SSH2_MSG_USERAUTH_PK_OK); - packet_put_string(pkalg, alen); - packet_put_string(pkblob, blen); - packet_send(); - packet_write_wait(); - authctxt->method->postponed = 1; - /* - * Remember key that was tried so we can - * correctly detect abandonment. See above. - */ - authctxt->method->method_data = (void *) key; - key = NULL; - } - } - if (authenticated != 1) - auth_clear_options(); - -done: - /* - * XXX TODO: add config options for specifying users for whom - * this userauth is insufficient and what userauths may - * continue. - */ -#ifdef USE_PAM - if (authenticated) { - if (!do_pam_non_initial_userauth(authctxt)) - authenticated = 0; - } -#endif /* USE_PAM */ - - debug2("userauth_pubkey: authenticated %d pkalg %s", authenticated, pkalg); - if (key != NULL) - key_free(key); - xfree(pkalg); - xfree(pkblob); -#ifdef HAVE_CYGWIN - if (check_nt_auth(0, authctxt->pw) == 0) - return; -#endif - if (authenticated) - authctxt->method->authenticated = 1; -} - -/* return 1 if user allows given key */ -static int -user_key_allowed2(struct passwd *pw, Key *key, char *file) -{ - char line[8192]; - int found_key = 0; - FILE *f; - u_long linenum = 0; - struct stat st; - Key *found; - char *fp; - - if (pw == NULL) - return 0; - - /* Temporarily use the user's uid. */ - temporarily_use_uid(pw); - - debug("trying public key file %s", file); - - /* Fail quietly if file does not exist */ - if (stat(file, &st) < 0) { - /* Restore the privileged uid. */ - restore_uid(); - return 0; - } - /* Open the file containing the authorized keys. */ - f = fopen(file, "r"); - if (!f) { - /* Restore the privileged uid. */ - restore_uid(); - return 0; - } - if (options.strict_modes && - secure_filename(f, file, pw, line, sizeof(line)) != 0) { - (void) fclose(f); - log("Authentication refused: %s", line); - restore_uid(); - return 0; - } - - found_key = 0; - found = key_new(key->type); - - while (fgets(line, sizeof(line), f)) { - char *cp, *options = NULL; - linenum++; - /* Skip leading whitespace, empty and comment lines. */ - for (cp = line; *cp == ' ' || *cp == '\t'; cp++) - ; - if (!*cp || *cp == '\n' || *cp == '#') - continue; - - if (key_read(found, &cp) != 1) { - /* no key? check if there are options for this key */ - int quoted = 0; - debug2("user_key_allowed: check options: '%s'", cp); - options = cp; - for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) { - if (*cp == '\\' && cp[1] == '"') - cp++; /* Skip both */ - else if (*cp == '"') - quoted = !quoted; - } - /* Skip remaining whitespace. */ - for (; *cp == ' ' || *cp == '\t'; cp++) - ; - if (key_read(found, &cp) != 1) { - debug2("user_key_allowed: advance: '%s'", cp); - /* still no key? advance to next line*/ - continue; - } - } - if (key_equal(found, key) && - auth_parse_options(pw, options, file, linenum) == 1) { - found_key = 1; - debug("matching key found: file %s, line %lu", - file, linenum); - fp = key_fingerprint(found, SSH_FP_MD5, SSH_FP_HEX); - verbose("Found matching %s key: %s", - key_type(found), fp); - xfree(fp); - break; - } - } - restore_uid(); - (void) fclose(f); - key_free(found); - if (!found_key) - debug2("key not found"); - return found_key; -} - -/* check whether given key is in .ssh/authorized_keys* */ -int -user_key_allowed(struct passwd *pw, Key *key) -{ - int success; - char *file; - - if (pw == NULL) - return 0; - - file = authorized_keys_file(pw); - success = user_key_allowed2(pw, key, file); - xfree(file); - if (success) - return success; - - /* try suffix "2" for backward compat, too */ - file = authorized_keys_file2(pw); - success = user_key_allowed2(pw, key, file); - xfree(file); - return success; -} - -static -void -userauth_pubkey_abandon(Authctxt *authctxt, Authmethod *method) -{ - if (!authctxt || !method) - return; - - if (method->method_data) { - method->abandons++; - method->attempts++; - key_free((Key *) method->method_data); - method->method_data = NULL; - } -} - -Authmethod method_pubkey = { - "publickey", - &options.pubkey_authentication, - userauth_pubkey, - userauth_pubkey_abandon, - NULL, NULL, /* method data and hist data */ - 0, /* not initial userauth */ - 0, 0, 0, /* counters */ - 0, 0, 0, 0, 0, 0 /* state */ -}; diff --git a/usr/src/cmd/ssh/sshd/auth2.c b/usr/src/cmd/ssh/sshd/auth2.c deleted file mode 100644 index bc3f4284f3..0000000000 --- a/usr/src/cmd/ssh/sshd/auth2.c +++ /dev/null @@ -1,695 +0,0 @@ -/* - * Copyright (c) 2000 Markus Friedl. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -/* - * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. - */ - -#include "includes.h" -RCSID("$OpenBSD: auth2.c,v 1.95 2002/08/22 21:33:58 markus Exp $"); - -#include "ssh2.h" -#include "xmalloc.h" -#include "packet.h" -#include "log.h" -#include "servconf.h" -#include "compat.h" -#include "misc.h" -#include "auth.h" -#include "dispatch.h" -#include "sshlogin.h" -#include "pathnames.h" - -#ifdef HAVE_BSM -#include "bsmaudit.h" -extern adt_session_data_t *ah; -#endif /* HAVE_BSM */ - -#ifdef GSSAPI -#include "ssh-gss.h" -#endif - -/* import */ -extern ServerOptions options; -extern u_char *session_id2; -extern int session_id2_len; - -Authctxt *x_authctxt = NULL; - -/* methods */ - -extern Authmethod method_none; -extern Authmethod method_pubkey; -extern Authmethod method_passwd; -extern Authmethod method_kbdint; -extern Authmethod method_hostbased; -extern Authmethod method_external; -extern Authmethod method_gssapi; - -static Authmethod *authmethods[] = { - &method_none, -#ifdef GSSAPI - &method_external, - &method_gssapi, -#endif - &method_pubkey, - &method_passwd, - &method_kbdint, - &method_hostbased, - NULL -}; - -/* protocol */ - -static void input_service_request(int, u_int32_t, void *); -static void input_userauth_request(int, u_int32_t, void *); - -/* helper */ -static Authmethod *authmethod_lookup(const char *); -static char *authmethods_get(void); -static char *authmethods_check_abandonment(Authctxt *authctxt, - Authmethod *method); -static void authmethod_count_attempt(Authmethod *method); -/*static char *authmethods_get_kbdint(void);*/ -int user_key_allowed(struct passwd *, Key *); -int hostbased_key_allowed(struct passwd *, const char *, char *, Key *); -static int userauth_method_can_run(Authmethod *method); -static void userauth_reset_methods(void); - -/* - * loop until authctxt->success == TRUE - */ - -Authctxt * -do_authentication2(void) -{ - Authctxt *authctxt = authctxt_new(); - - x_authctxt = authctxt; /*XXX*/ - -#ifdef HAVE_BSM - fatal_add_cleanup(audit_failed_login_cleanup, authctxt); -#endif /* HAVE_BSM */ - - dispatch_init(&dispatch_protocol_error); - dispatch_set(SSH2_MSG_SERVICE_REQUEST, &input_service_request); - dispatch_run(DISPATCH_BLOCK, &authctxt->success, authctxt); - - return (authctxt); -} - -static void -input_service_request(int type, u_int32_t seq, void *ctxt) -{ - Authctxt *authctxt = ctxt; - u_int len; - int acceptit = 0; - char *service = packet_get_string(&len); - packet_check_eom(); - - if (authctxt == NULL) - fatal("input_service_request: no authctxt"); - - if (strcmp(service, "ssh-userauth") == 0) { - if (!authctxt->success) { - acceptit = 1; - /* now we can handle user-auth requests */ - dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &input_userauth_request); - } - } - /* XXX all other service requests are denied */ - - if (acceptit) { - packet_start(SSH2_MSG_SERVICE_ACCEPT); - packet_put_cstring(service); - packet_send(); - packet_write_wait(); - } else { - debug("bad service request %s", service); - packet_disconnect("bad service request %s", service); - } - xfree(service); -} - -static void -input_userauth_request(int type, u_int32_t seq, void *ctxt) -{ - Authctxt *authctxt = ctxt; - Authmethod *m = NULL; - char *user, *service, *method, *style = NULL; - int valid_attempt; - - if (authctxt == NULL) - fatal("input_userauth_request: no authctxt"); - - user = packet_get_string(NULL); - service = packet_get_string(NULL); - method = packet_get_string(NULL); - debug("userauth-request for user %s service %s method %s", user, - service, method); - debug("attempt %d initial attempt %d failures %d initial failures %d", - authctxt->attempt, authctxt->init_attempt, - authctxt->failures, authctxt->init_failures); - - m = authmethod_lookup(method); - - if ((style = strchr(user, ':')) != NULL) - *style++ = 0; - - authctxt->attempt++; - if (m != NULL && m->is_initial) - authctxt->init_attempt++; - - if (options.pre_userauth_hook != NULL && - run_auth_hook(options.pre_userauth_hook, user, m->name) != 0) { - valid_attempt = 0; - } else { - valid_attempt = 1; - } - - if (authctxt->attempt == 1) { - /* setup auth context */ - authctxt->pw = getpwnamallow(user); - /* May want to abstract SSHv2 services someday */ - if (authctxt->pw && strcmp(service, "ssh-connection")==0) { - /* enforced in userauth_finish() below */ - if (valid_attempt) { - authctxt->valid = 1; - } - debug2("input_userauth_request: setting up authctxt for %s", user); - } else { - log("input_userauth_request: illegal user %s", user); - } - setproctitle("%s", authctxt->pw ? user : "unknown"); - authctxt->user = xstrdup(user); - authctxt->service = xstrdup(service); - authctxt->style = style ? xstrdup(style) : NULL; - userauth_reset_methods(); - } else { - char *abandoned; - - /* - * Check for abandoned [multi-round-trip] userauths - * methods (e.g., kbdint). Userauth method abandonment - * should be treated as userauth method failure and - * counted against max_auth_tries. - */ - abandoned = authmethods_check_abandonment(authctxt, m); - - if (abandoned != NULL && - authctxt->failures > options.max_auth_tries) { - /* userauth_finish() will now packet_disconnect() */ - userauth_finish(authctxt, abandoned); - /* NOTREACHED */ - } - - /* Handle user|service changes, possibly packet_disconnect() */ - userauth_user_svc_change(authctxt, user, service); - } - - authctxt->method = m; - - /* run userauth method, try to authenticate user */ - if (m != NULL && userauth_method_can_run(m)) { - debug2("input_userauth_request: try method %s", method); - - m->postponed = 0; - m->abandoned = 0; - m->authenticated = 0; - - if (!m->is_initial || - authctxt->init_failures < options.max_init_auth_tries) - m->userauth(authctxt); - - authmethod_count_attempt(m); - - if (authctxt->unwind_dispatch_loop) { - /* - * Method ran nested dispatch loop but was - * abandoned. Cleanup and return without doing - * anything else; we're just unwinding the stack. - */ - authctxt->unwind_dispatch_loop = 0; - goto done; - } - - if (m->postponed) - goto done; /* multi-round trip userauth not finished */ - - if (m->abandoned) { - /* multi-round trip userauth abandoned, log failure */ - auth_log(authctxt, 0, method, " ssh2"); - goto done; - } - } - - userauth_finish(authctxt, method); - -done: - xfree(service); - xfree(user); - xfree(method); -} - -void -userauth_finish(Authctxt *authctxt, char *method) -{ - int authenticated, partial; - - if (authctxt == NULL) - fatal("%s: missing context", __func__); - - /* unknown method handling -- must elicit userauth failure msg */ - if (authctxt->method == NULL) { - authenticated = 0; - partial = 0; - goto done_checking; - } - -#ifndef USE_PAM - /* Special handling for root (done elsewhere for PAM) */ - if (authctxt->method->authenticated && - authctxt->pw != NULL && authctxt->pw->pw_uid == 0 && - !auth_root_allowed(method)) - authctxt->method->authenticated = 0; -#endif /* USE_PAM */ - -#ifdef _UNICOS - if (authctxt->method->authenticated && - cray_access_denied(authctxt->user)) { - authctxt->method->authenticated = 0; - fatal("Access denied for user %s.",authctxt->user); - } -#endif /* _UNICOS */ - - partial = userauth_check_partial_failure(authctxt); - authenticated = authctxt->method->authenticated; - -#ifdef USE_PAM - /* - * If the userauth method failed to complete PAM work then force - * partial failure. - */ - if (authenticated && !AUTHPAM_DONE(authctxt)) - partial = 1; -#endif /* USE_PAM */ - - /* - * To properly support invalid userauth method names we set - * authenticated=0, partial=0 above and know that - * authctxt->method == NULL. - * - * No unguarded reference to authctxt->method allowed from here. - * Checking authenticated != 0 is a valid guard; authctxt->method - * MUST NOT be NULL if authenticated. - */ -done_checking: - if (!authctxt->valid && authenticated) { - /* - * We get here if the PreUserauthHook fails but the - * user is otherwise valid. - * An error in the PAM handling could also get us here - * but we need not panic, just treat as a failure. - */ - authctxt->method->authenticated = 0; - authenticated = 0; - log("Ignoring authenticated invalid user %s", - authctxt->user); - auth_log(authctxt, 0, method, " ssh2"); - } - - /* Log before sending the reply */ - auth_log(authctxt, authenticated, method, " ssh2"); - - if (authenticated && !partial) { - - /* turn off userauth */ - dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &dispatch_protocol_ignore); - packet_start(SSH2_MSG_USERAUTH_SUCCESS); - packet_send(); - packet_write_wait(); - /* now we can break out */ - authctxt->success = 1; - } else { - char *methods; - - if (authctxt->method && authctxt->method->is_initial) - authctxt->init_failures++; - - authctxt->method = NULL; - -#ifdef USE_PAM - /* - * Keep track of last PAM error (or PERM_DENIED) for BSM - * login failure auditing, which may run after the PAM - * state has been cleaned up. - */ - authctxt->pam_retval = AUTHPAM_ERROR(authctxt, PAM_PERM_DENIED); -#endif /* USE_PAM */ - - if (authctxt->failures++ > options.max_auth_tries) { -#ifdef HAVE_BSM - fatal_remove_cleanup(audit_failed_login_cleanup, - authctxt); - audit_sshd_login_failure(&ah, PAM_MAXTRIES, - authctxt->user); -#endif /* HAVE_BSM */ - packet_disconnect(AUTH_FAIL_MSG, authctxt->user); - } - -#ifdef _UNICOS - if (strcmp(method, "password") == 0) - cray_login_failure(authctxt->user, IA_UDBERR); -#endif /* _UNICOS */ - packet_start(SSH2_MSG_USERAUTH_FAILURE); - - /* - * If (partial) then authmethods_get() will return only - * required methods, likely only "keyboard-interactive;" - * (methods == NULL) implies failure, even if (partial == 1) - */ - methods = authmethods_get(); - packet_put_cstring(methods); - packet_put_char((authenticated && partial && methods) ? 1 : 0); - if (methods) - xfree(methods); - packet_send(); - packet_write_wait(); - } -} - -/* get current user */ - -struct passwd* -auth_get_user(void) -{ - return (x_authctxt != NULL && x_authctxt->valid) ? x_authctxt->pw : NULL; -} - -#define DELIM "," - -#if 0 -static char * -authmethods_get_kbdint(void) -{ - Buffer b; - int i; - - for (i = 0; authmethods[i] != NULL; i++) { - if (strcmp(authmethods[i]->name, "keyboard-interactive") != 0) - continue; - return xstrdup(authmethods[i]->name); - } - return NULL; -} -#endif - -void -userauth_user_svc_change(Authctxt *authctxt, char *user, char *service) -{ - /* - * NOTE: - * - * SSHv2 services should be abstracted and service changes during - * userauth should be supported as per the userauth draft. In the PAM - * case, support for multiple SSHv2 services means that we have to - * format the PAM service name according to the SSHv2 service *and* the - * SSHv2 userauth being attempted ("passwd", "kbdint" and "other"). - * - * We'll cross that bridge when we come to it. For now disallow service - * changes during userauth if using PAM, but allow username changes. - */ - - /* authctxt->service must == ssh-connection here */ - if (service != NULL && strcmp(service, authctxt->service) != 0) { - packet_disconnect("Change of service not " - "allowed: %s and %s", - authctxt->service, service); - } - if (user != NULL && authctxt->user != NULL && - strcmp(user, authctxt->user) == 0) - return; - - /* All good; update authctxt */ - xfree(authctxt->user); - authctxt->user = xstrdup(user); - pwfree(&authctxt->pw); - authctxt->pw = getpwnamallow(user); - authctxt->valid = (authctxt->pw != NULL); - - /* Forget method state; abandon postponed userauths */ - userauth_reset_methods(); -} - -int -userauth_check_partial_failure(Authctxt *authctxt) -{ - int i; - int required = 0; - int sufficient = 0; - - /* - * v1 does not set authctxt->method - * partial userauth failure is a v2 concept - */ - if (authctxt->method == NULL) - return 0; - - for (i = 0; authmethods[i] != NULL; i++) { - if (authmethods[i]->required) - required++; - if (authmethods[i]->sufficient) - sufficient++; - } - - if (required == 0 && sufficient == 0) - return !authctxt->method->authenticated; - - if (required == 1 && authctxt->method->required) - return !authctxt->method->authenticated; - - if (sufficient && authctxt->method->sufficient) - return !authctxt->method->authenticated; - - return 1; -} - -int -userauth_method_can_run(Authmethod *method) -{ - if (method->not_again) - return 0; - - return 1; -} - -static -void -userauth_reset_methods(void) -{ - int i; - - for (i = 0; authmethods[i] != NULL; i++) { - /* note: counters not reset */ - authmethods[i]->required = 0; - authmethods[i]->sufficient = 0; - authmethods[i]->authenticated = 0; - authmethods[i]->not_again = 0; - authmethods[i]->postponed = 0; - authmethods[i]->abandoned = 0; - } -} - -void -userauth_force_kbdint(void) -{ - int i; - - for (i = 0; authmethods[i] != NULL; i++) { - authmethods[i]->required = 0; - authmethods[i]->sufficient = 0; - } - method_kbdint.required = 1; -} - -/* - * Check to see if a previously run multi-round trip userauth method has - * been abandoned and call its cleanup function. - * - * Abandoned userauth method invocations are counted as userauth failures. - */ -static -char * -authmethods_check_abandonment(Authctxt *authctxt, Authmethod *method) -{ - int i; - - /* optimization: check current method first */ - if (method && method->postponed) { - method->postponed = 0; - if (method->abandon) - method->abandon(authctxt, method); - else - method->abandons++; - authctxt->failures++; /* abandonment -> failure */ - if (method->is_initial) - authctxt->init_failures++; - - /* - * Since we check for abandonment whenever a userauth is - * requested we know only one method could have been - * in postponed state, so we can return now. - */ - return (method->name); - } - for (i = 0; authmethods[i] != NULL; i++) { - if (!authmethods[i]->postponed) - continue; - - /* some method was postponed and a diff one is being started */ - if (method != authmethods[i]) { - authmethods[i]->postponed = 0; - if (authmethods[i]->abandon) - authmethods[i]->abandon(authctxt, - authmethods[i]); - else - authmethods[i]->abandons++; - authctxt->failures++; - if (authmethods[i]->is_initial) - authctxt->init_failures++; - return (authmethods[i]->name); /* see above */ - } - } - - return NULL; -} - -static char * -authmethods_get(void) -{ - Buffer b; - char *list; - int i; - int sufficient = 0; - int required = 0; - int authenticated = 0; - int partial = 0; - - /* - * If at least one method succeeded partially then at least one - * authmethod will be required and only required methods should - * continue. - */ - for (i = 0; authmethods[i] != NULL; i++) { - if (authmethods[i]->authenticated) - authenticated++; - if (authmethods[i]->required) - required++; - if (authmethods[i]->sufficient) - sufficient++; - } - - partial = (required + sufficient) > 0; - - buffer_init(&b); - for (i = 0; authmethods[i] != NULL; i++) { - if (strcmp(authmethods[i]->name, "none") == 0) - continue; - if (required && !authmethods[i]->required) - continue; - if (sufficient && !required && !authmethods[i]->sufficient) - continue; - if (authmethods[i]->not_again) - continue; - - if (authmethods[i]->required) { - if (buffer_len(&b) > 0) - buffer_append(&b, ",", 1); - buffer_append(&b, authmethods[i]->name, - strlen(authmethods[i]->name)); - continue; - } - - /* - * A method can be enabled (marked sufficient) - * dynamically provided that at least one other method - * has succeeded partially. - */ - if ((partial && authmethods[i]->sufficient) || - (authmethods[i]->enabled != NULL && - *(authmethods[i]->enabled) != 0)) { - if (buffer_len(&b) > 0) - buffer_append(&b, ",", 1); - buffer_append(&b, authmethods[i]->name, - strlen(authmethods[i]->name)); - } - } - buffer_append(&b, "\0", 1); - list = xstrdup(buffer_ptr(&b)); - buffer_free(&b); - return list; -} - -static Authmethod * -authmethod_lookup(const char *name) -{ - int i; - - /* - * Method must be sufficient, required or enabled and must not - * be marked as not able to run again - */ - if (name != NULL) - for (i = 0; authmethods[i] != NULL; i++) - if (((authmethods[i]->sufficient || - authmethods[i]->required) || - (authmethods[i]->enabled != NULL && - *(authmethods[i]->enabled) != 0)) && - !authmethods[i]->not_again && - strcmp(name, authmethods[i]->name) == 0) - return authmethods[i]; - debug2("Unrecognized authentication method name: %s", - name ? name : "NULL"); - return NULL; -} - -static void -authmethod_count_attempt(Authmethod *method) -{ - if (!method) - fatal("Internal error in authmethod_count_attempt()"); - - if (method->postponed) - return; - - method->attempts++; - - if (method->abandoned) - method->abandons++; - else if (method->authenticated) - method->successes++; - else - method->failures++; - - return; -} diff --git a/usr/src/cmd/ssh/sshd/bsmaudit.c b/usr/src/cmd/ssh/sshd/bsmaudit.c deleted file mode 100644 index c46d295972..0000000000 --- a/usr/src/cmd/ssh/sshd/bsmaudit.c +++ /dev/null @@ -1,316 +0,0 @@ -/* - * CDDL HEADER START - * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License (the "License"). - * You may not use this file except in compliance with the License. - * - * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE - * or http://www.opensolaris.org/os/licensing. - * See the License for the specific language governing permissions - * and limitations under the License. - * - * When distributing Covered Code, include this CDDL HEADER in each - * file and include the License file at usr/src/OPENSOLARIS.LICENSE. - * If applicable, add the following below this CDDL HEADER, with the - * fields enclosed by brackets "[]" replaced with your own identifying - * information: Portions Copyright [yyyy] [name of copyright owner] - * - * CDDL HEADER END - * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - * - * usr/src/cmd/ssh/sshd/bsmaudit.c - * - * Taken from the on81 usr/src/lib/libbsm/common/audit_login.c - */ -#include "includes.h" - -#include <sys/systeminfo.h> -#include <sys/param.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/systeminfo.h> -#include <sys/stat.h> -#include <sys/wait.h> -#include <netinet/in.h> -#include <netdb.h> -#include <signal.h> - -#include <stdarg.h> -#include <pwd.h> -#include <shadow.h> -#include <utmpx.h> -#include <unistd.h> -#include <string.h> - -#include <locale.h> - -#include "log.h" -#include "packet.h" -#include "canohost.h" -#include "servconf.h" -#include "xmalloc.h" -#include <errno.h> -#include <bsm/adt.h> -#include <bsm/adt_event.h> - -extern uint_t utmp_len; /* XXX - Yuck; we'll keep this for now */ -extern ServerOptions options; - /* - * XXX - Yuck; we should have a - * get_client_name_or_ip that does the - * right thing wrt reverse lookups - */ - -void -audit_sshd_chauthtok(int pam_retval, uid_t uid, gid_t gid) -{ - adt_session_data_t *ah = NULL; - adt_event_data_t *event = NULL; - const char *how = "couldn't start adt session"; - int saved_errno = 0; - - if (adt_start_session(&ah, NULL, 0) != 0) { - saved_errno = errno; - goto fail; - } - if (adt_set_user(ah, uid, gid, uid, gid, NULL, ADT_NEW) != 0) { - saved_errno = errno; - how = "couldn't set adt user"; - goto fail; - } - - if ((event = adt_alloc_event(ah, ADT_passwd)) == NULL) { - saved_errno = errno; - how = "couldn't allocate adt event"; - goto fail; - } - - if (pam_retval == PAM_SUCCESS) { - if (adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS) != 0) { - saved_errno = errno; - how = "couldn't put adt event"; - goto fail; - } - } else if (adt_put_event(event, ADT_FAILURE, - ADT_FAIL_PAM + pam_retval) != 0) { - saved_errno = errno; - how = "couldn't put adt event"; - goto fail; - } - - adt_free_event(event); - (void) adt_end_session(ah); - return; - -fail: - adt_free_event(event); - (void) adt_end_session(ah); - - fatal("Auditing of password change failed: %s (%s)", - strerror(saved_errno), how); -} - -void -audit_sshd_login(adt_session_data_t **ah, pid_t pid) -{ - adt_event_data_t *event = NULL; - const char *how; - int saved_errno = 0; - ucred_t *ucred = NULL; - - if (ah == NULL) { - how = "programmer error"; - saved_errno = EINVAL; - goto fail; - } - - if (adt_start_session(ah, NULL, 0) != 0) { - saved_errno = errno; - how = "couldn't start adt session"; - goto fail; - } - - if ((ucred = ucred_get(pid)) == NULL) { - saved_errno = errno; - how = "ucred_get() failed to obtain user credential"; - goto fail; - } - - if (adt_set_from_ucred(*ah, ucred, ADT_NEW)) { - saved_errno = errno; - how = "adt_set_from_ucred() failed to set user credential"; - goto fail; - } - - if ((event = adt_alloc_event(*ah, ADT_ssh)) == NULL) { - saved_errno = errno; - how = "couldn't allocate adt event"; - goto fail; - } - - if (adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS) != 0) { - saved_errno = errno; - how = "couldn't put adt event"; - goto fail; - } - - adt_free_event(event); - ucred_free(ucred); - /* Don't end adt session - leave for when logging out */ - return; - -fail: - if (ucred != NULL) - ucred_free(ucred); - adt_free_event(event); - (void) adt_end_session(*ah); - - fatal("Auditing of login failed: %s (%s)", - strerror(saved_errno), how); -} - -void -audit_sshd_login_failure(adt_session_data_t **ah, int pam_retval, char *user) -{ - adt_event_data_t *event = NULL; - const char *how; - int saved_errno = 0; - struct passwd pwd; - char *pwdbuf = NULL; - size_t pwdbuf_len; - long pwdbuf_len_max; - uid_t uid = ADT_NO_ATTRIB; - gid_t gid = ADT_NO_ATTRIB; - - if (ah == NULL) { - how = "programmer error"; - saved_errno = EINVAL; - goto fail; - } - - if ((pwdbuf_len_max = sysconf(_SC_GETPW_R_SIZE_MAX)) == -1) { - saved_errno = errno; - how = "couldn't determine maximum size of password buffer"; - goto fail; - } - - pwdbuf_len = (size_t)pwdbuf_len_max; - pwdbuf = xmalloc(pwdbuf_len); - - if (adt_start_session(ah, NULL, ADT_USE_PROC_DATA) != 0) { - saved_errno = errno; - how = "couldn't start adt session"; - goto fail; - } - - /* - * Its possible to reach this point with user being invalid so - * we check here to make sure that the user in question has a valid - * password entry. - */ - if ((user != NULL) && - (getpwnam_r(user, &pwd, pwdbuf, pwdbuf_len) != NULL)) { - uid = pwd.pw_uid; - gid = pwd.pw_gid; - } - - if (adt_set_user(*ah, uid, gid, uid, gid, NULL, ADT_NEW) != 0) { - saved_errno = errno; - how = "couldn't set adt user"; - goto fail; - } - - if ((event = adt_alloc_event(*ah, ADT_ssh)) == NULL) { - saved_errno = errno; - how = "couldn't allocate adt event"; - goto fail; - } - - if (adt_put_event(event, ADT_FAILURE, ADT_FAIL_PAM + pam_retval) != 0) { - saved_errno = errno; - how = "couldn't put adt event"; - goto fail; - } - - xfree(pwdbuf); - adt_free_event(event); - (void) adt_end_session(*ah); - *ah = NULL; - return; - -fail: - if (pwdbuf != NULL) - xfree(pwdbuf); - adt_free_event(event); - (void) adt_end_session(*ah); - - fatal("Auditing of login failed: %s (%s)", - strerror(saved_errno), how); -} - -void -audit_sshd_logout(adt_session_data_t **ah) -{ - adt_event_data_t *event = NULL; - const char *how = "programmer error"; - int saved_errno = 0; - - if (!ah) { - saved_errno = EINVAL; - goto fail; - } - - if ((event = adt_alloc_event(*ah, ADT_logout)) == NULL) { - saved_errno = errno; - how = "couldn't allocate adt event"; - goto fail; - } - - if (adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS) != 0) { - saved_errno = errno; - how = "couldn't put adt event"; - goto fail; - } - - adt_free_event(event); - (void) adt_end_session(*ah); - *ah = NULL; - return; - -fail: - adt_free_event(event); - (void) adt_end_session(*ah); - - fatal("Auditing of logout failed: %s (%s)", - how, strerror(saved_errno)); -} - -/* - * audit_sshd_settid stores the terminal id while it is still - * available. - * - * The failure cases are lack of resources or incorrect permissions. - * libbsm generates syslog messages, so there's no value doing more - * here. ADT_NO_AUDIT leaves the auid at AU_NOAUDITID and will be - * replaced when one of the above functions is called. - */ -void -audit_sshd_settid(int sock) -{ - adt_session_data_t *ah; - adt_termid_t *termid; - - if (adt_start_session(&ah, NULL, 0) == 0) { - if (adt_load_termid(sock, &termid) == 0) { - if (adt_set_user(ah, ADT_NO_AUDIT, - ADT_NO_AUDIT, 0, ADT_NO_AUDIT, - termid, ADT_SETTID) == 0) - (void) adt_set_proc(ah); - free(termid); - } - (void) adt_end_session(ah); - } -} diff --git a/usr/src/cmd/ssh/sshd/bsmaudit.h b/usr/src/cmd/ssh/sshd/bsmaudit.h deleted file mode 100644 index 72f599a240..0000000000 --- a/usr/src/cmd/ssh/sshd/bsmaudit.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * CDDL HEADER START - * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License (the "License"). - * You may not use this file except in compliance with the License. - * - * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE - * or http://www.opensolaris.org/os/licensing. - * See the License for the specific language governing permissions - * and limitations under the License. - * - * When distributing Covered Code, include this CDDL HEADER in each - * file and include the License file at usr/src/OPENSOLARIS.LICENSE. - * If applicable, add the following below this CDDL HEADER, with the - * fields enclosed by brackets "[]" replaced with your own identifying - * information: Portions Copyright [yyyy] [name of copyright owner] - * - * CDDL HEADER END - * - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - - -#ifndef _BSMAUDIT_H -#define _BSMAUDIT_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include <bsm/adt.h> -#include <bsm/adt_event.h> -#include <pwd.h> - -void audit_sshd_chauthtok(int pam_retval, uid_t uid, gid_t gid); -void audit_sshd_login(adt_session_data_t **ah, pid_t pid); -void audit_sshd_login_failure(adt_session_data_t **ah, int pam_retval, - char *user); -void audit_sshd_logout(adt_session_data_t **ah); -void audit_sshd_settid(int); - -#ifdef __cplusplus -} -#endif - -#endif /* _BSMAUDIT_H */ diff --git a/usr/src/cmd/ssh/sshd/groupaccess.c b/usr/src/cmd/ssh/sshd/groupaccess.c deleted file mode 100644 index 2239832e1b..0000000000 --- a/usr/src/cmd/ssh/sshd/groupaccess.c +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ -/* - * Copyright (c) 2001 Kevin Steves. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "includes.h" -RCSID("$OpenBSD: groupaccess.c,v 1.5 2002/03/04 17:27:39 stevesk Exp $"); - -#include "groupaccess.h" -#include "xmalloc.h" -#include "match.h" -#include "log.h" -#include <alloca.h> - -static int ngroups, ngroups_lim; -static char **groups_byname; - -/* - * Initialize group access list for user with primary (base) and - * supplementary groups. Return the number of groups in the list. - */ -int -ga_init(const char *user, gid_t base) -{ - gid_t *groups_bygid; - int i, j; - struct group *gr; - - if (ngroups_lim == 0) { - /* Add one for the base gid */ - ngroups_lim = sysconf(_SC_NGROUPS_MAX) + 1; - groups_byname = malloc(sizeof (char *) * ngroups_lim); - } else if (ngroups > 0) - ga_free(); - - groups_bygid = alloca(ngroups_lim * sizeof (gid_t)); - - ngroups = ngroups_lim; - if (getgrouplist(user, base, groups_bygid, &ngroups) == -1) - log("getgrouplist: groups list too small"); - for (i = 0, j = 0; i < ngroups; i++) - if ((gr = getgrgid(groups_bygid[i])) != NULL) - groups_byname[j++] = xstrdup(gr->gr_name); - return (ngroups = j); -} - -/* - * Return 1 if one of user's groups is contained in groups. - * Return 0 otherwise. Use match_pattern() for string comparison. - */ -int -ga_match(char * const *groups, int n) -{ - int i, j; - - for (i = 0; i < ngroups; i++) - for (j = 0; j < n; j++) - if (match_pattern(groups_byname[i], groups[j])) - return (1); - return (0); -} - -/* - * Return 1 if one of user's groups matches group_pattern list. - * Return 0 on negated or no match. - */ -int -ga_match_pattern_list(const char *group_pattern) -{ - int i, found = 0; - size_t len = strlen(group_pattern); - - for (i = 0; i < ngroups; i++) { - switch (match_pattern_list(groups_byname[i], - group_pattern, len, 0)) { - case -1: - return (0); /* Negated match wins */ - case 0: - continue; - case 1: - found = 1; - } - } - return (found); -} - -/* - * Free memory allocated for group access list. - */ -void -ga_free(void) -{ - int i; - - if (ngroups > 0) { - for (i = 0; i < ngroups; i++) - xfree(groups_byname[i]); - ngroups = 0; - } -} diff --git a/usr/src/cmd/ssh/sshd/gss-serv.c b/usr/src/cmd/ssh/sshd/gss-serv.c deleted file mode 100644 index 7ff525c306..0000000000 --- a/usr/src/cmd/ssh/sshd/gss-serv.c +++ /dev/null @@ -1,516 +0,0 @@ -/* - * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -/* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - -#include "includes.h" - -#ifdef GSSAPI - -#pragma ident "%Z%%M% %I% %E% SMI" - -#include "includes.h" -#include "ssh.h" -#include "ssh2.h" -#include "xmalloc.h" -#include "buffer.h" -#include "bufaux.h" -#include "packet.h" -#include "compat.h" -#include <openssl/evp.h> -#include "cipher.h" -#include "kex.h" -#include "auth.h" -#include "log.h" -#include "channels.h" -#include "session.h" -#include "dispatch.h" -#include "servconf.h" -#include "uidswap.h" -#include "compat.h" -#include <pwd.h> - -#include "ssh-gss.h" - -extern char **environ; - -extern ServerOptions options; -extern uchar_t *session_id2; -extern int session_id2_len; - -Gssctxt *xxx_gssctxt; - -void -ssh_gssapi_server_kex_hook(Kex *kex, char **proposal) -{ - gss_OID_set mechs = GSS_C_NULL_OID_SET; - - if (kex == NULL || !kex->server) - fatal("INTERNAL ERROR (%s)", __func__); - - ssh_gssapi_server_mechs(&mechs); - ssh_gssapi_modify_kex(kex, mechs, proposal); -} - -void -ssh_gssapi_server_mechs(gss_OID_set *mechs) -{ - static gss_OID_set supported = GSS_C_NULL_OID_SET; - gss_OID_set s, acquired, indicated = GSS_C_NULL_OID_SET; - gss_cred_id_t creds; - OM_uint32 maj, min; - int i; - - if (!mechs) { - (void) gss_release_oid_set(&min, &supported); - return; - } - - if (supported != GSS_C_NULL_OID_SET) { - *mechs = supported; - return; - } - - *mechs = GSS_C_NULL_OID_SET; - - maj = gss_create_empty_oid_set(&min, &s); - if (GSS_ERROR(maj)) { - debug("Could not allocate GSS-API resources (%s)", - ssh_gssapi_last_error(NULL, &maj, &min)); - return; - } - - maj = gss_indicate_mechs(&min, &indicated); - if (GSS_ERROR(maj)) { - debug("No GSS-API mechanisms are installed"); - return; - } - - maj = gss_acquire_cred(&min, GSS_C_NO_NAME, 0, indicated, - GSS_C_ACCEPT, &creds, &acquired, NULL); - - if (GSS_ERROR(maj)) - debug("Failed to acquire GSS-API credentials for any " - "mechanisms (%s)", ssh_gssapi_last_error(NULL, &maj, &min)); - - (void) gss_release_oid_set(&min, &indicated); - (void) gss_release_cred(&min, &creds); - - if (acquired == GSS_C_NULL_OID_SET || acquired->count == 0) - return; - - for (i = 0; i < acquired->count; i++) { - if (ssh_gssapi_is_spnego(&acquired->elements[i])) - continue; - - maj = gss_add_oid_set_member(&min, &acquired->elements[i], &s); - if (GSS_ERROR(maj)) { - debug("Could not allocate GSS-API resources (%s)", - ssh_gssapi_last_error(NULL, &maj, &min)); - return; - } - } - (void) gss_release_oid_set(&min, &acquired); - - if (s->count) { - supported = s; - *mechs = s; - } -} - -/* - * Wrapper around accept_sec_context. Requires that the context contains: - * - * oid - * credentials (from ssh_gssapi_acquire_cred) - */ -/* Priviledged */ -OM_uint32 -ssh_gssapi_accept_ctx(Gssctxt *ctx, gss_buffer_t recv_tok, - gss_buffer_t send_tok) -{ - /* - * Acquiring a cred for the ctx->desired_mech for GSS_C_NO_NAME - * may well be probably better than using GSS_C_NO_CREDENTIAL - * and then checking that ctx->desired_mech agrees with - * ctx->actual_mech... - */ - ctx->major = gss_accept_sec_context(&ctx->minor, &ctx->context, - GSS_C_NO_CREDENTIAL, recv_tok, GSS_C_NO_CHANNEL_BINDINGS, - &ctx->src_name, &ctx->actual_mech, send_tok, &ctx->flags, - NULL, &ctx->deleg_creds); - - if (GSS_ERROR(ctx->major)) - ssh_gssapi_error(ctx, "accepting security context"); - - if (ctx->major == GSS_S_CONTINUE_NEEDED && send_tok->length == 0) - fatal("Zero length GSS context token output when " - "continue needed"); - else if (GSS_ERROR(ctx->major) && send_tok->length == 0) - debug2("Zero length GSS context error token output"); - - if (ctx->major == GSS_S_COMPLETE && - ctx->desired_mech != GSS_C_NULL_OID && - (ctx->desired_mech->length != ctx->actual_mech->length || - memcmp(ctx->desired_mech->elements, ctx->actual_mech->elements, - ctx->desired_mech->length) != 0)) { - - gss_OID_set supported; - OM_uint32 min; - int present = 0; - - debug("The client did not use the GSS-API mechanism it " - "asked for"); - - /* Let it slide as long as the mech is supported */ - ssh_gssapi_server_mechs(&supported); - if (supported != GSS_C_NULL_OID_SET) { - (void) gss_test_oid_set_member(&min, ctx->actual_mech, - supported, &present); - } - if (!present) - ctx->major = GSS_S_BAD_MECH; - } - - if (ctx->deleg_creds) - debug("Received delegated GSS credentials"); - - if (ctx->major == GSS_S_COMPLETE) { - ctx->major = gss_inquire_context(&ctx->minor, ctx->context, - NULL, &ctx->dst_name, NULL, NULL, NULL, NULL, - &ctx->established); - - if (GSS_ERROR(ctx->major)) { - ssh_gssapi_error(ctx, - "inquiring established sec context"); - return (ctx->major); - } - - xxx_gssctxt = ctx; - } - - return (ctx->major); -} - - -/* As user - called through fatal cleanup hook */ -void -ssh_gssapi_cleanup_creds(Gssctxt *ctx) -{ -#ifdef HAVE_GSS_STORE_CRED - /* pam_setcred() will take care of this */ - return; -#else - return; -/* #error "Portability broken in cleanup of stored creds" */ -#endif /* HAVE_GSS_STORE_CRED */ -} - -void -ssh_gssapi_storecreds(Gssctxt *ctx, Authctxt *authctxt) -{ -#ifdef USE_PAM - char **penv, **tmp_env; -#endif /* USE_PAM */ - - if (authctxt == NULL) { - error("Missing context while storing GSS-API credentials"); - return; - } - - if (ctx == NULL && xxx_gssctxt == NULL) - return; - - if (ctx == NULL) - ctx = xxx_gssctxt; - - if (!options.gss_cleanup_creds || - ctx->deleg_creds == GSS_C_NO_CREDENTIAL) { - debug3("Not storing delegated GSS credentials" - " (none delegated)"); - return; - } - - if (!authctxt->valid || authctxt->pw == NULL) { - debug3("Not storing delegated GSS credentials" - " for invalid user"); - return; - } - - debug("Storing delegated GSS-API credentials"); - - /* - * The GSS-API has a flaw in that it does not provide a - * mechanism by which delegated credentials can be made - * available for acquisition by GSS_Acquire_cred() et. al.; - * gss_store_cred() is the proposed GSS-API extension for - * generically storing delegated credentials. - * - * gss_store_cred() does not speak to how credential stores are - * referenced. Generically this may be done by switching to the - * user context of the user in whose default credential store we - * wish to place delegated credentials. But environment - * variables could conceivably affect the choice of credential - * store as well, and perhaps in a mechanism-specific manner. - * - * SUNW -- On Solaris the euid selects the current credential - * store, but PAM modules could select alternate stores by - * setting, for example, KRB5CCNAME, so we also use the PAM - * environment temporarily. - */ - -#ifdef HAVE_GSS_STORE_CRED -#ifdef USE_PAM - /* - * PAM may have set mechanism-specific variables (e.g., - * KRB5CCNAME). fetch_pam_environment() protects against LD_* - * and other environment variables. - */ - penv = fetch_pam_environment(authctxt); - tmp_env = environ; - environ = penv; -#endif /* USE_PAM */ - if (authctxt->pw->pw_uid != geteuid()) { - temporarily_use_uid(authctxt->pw); - ctx->major = gss_store_cred(&ctx->minor, ctx->deleg_creds, - GSS_C_INITIATE, GSS_C_NULL_OID, 0, ctx->default_creds, - NULL, NULL); - restore_uid(); - } else { - /* only when logging in as the privileged user used by sshd */ - ctx->major = gss_store_cred(&ctx->minor, ctx->deleg_creds, - GSS_C_INITIATE, GSS_C_NULL_OID, 0, ctx->default_creds, - NULL, NULL); - } -#ifdef USE_PAM - environ = tmp_env; - free_pam_environment(penv); -#endif /* USE_PAM */ - if (GSS_ERROR(ctx->major)) - ssh_gssapi_error(ctx, "storing delegated credentials"); - -#else -#ifdef KRB5_GSS -#error "MIT/Heimdal krb5-specific code missing in ssh_gssapi_storecreds()" - if (ssh_gssapi_is_krb5(ctx->mech)) - ssh_gssapi_krb5_storecreds(ctx); -#endif /* KRB5_GSS */ -#ifdef GSI_GSS -#error "GSI krb5-specific code missing in ssh_gssapi_storecreds()" - if (ssh_gssapi_is_gsi(ctx->mech)) - ssh_gssapi_krb5_storecreds(ctx); -#endif /* GSI_GSS */ -/* #error "Mechanism-specific code missing in ssh_gssapi_storecreds()" */ - return; -#endif /* HAVE_GSS_STORE_CRED */ -} - -void -ssh_gssapi_do_child(Gssctxt *ctx, char ***envp, uint_t *envsizep) -{ - /* - * MIT/Heimdal/GSI specific code goes here. - * - * On Solaris there's nothing to do here as the GSS store and - * related environment variables are to be set by PAM, if at all - * (no environment variables are needed to address the default - * credential store -- the euid does that). - */ -#ifdef KRB5_GSS -#error "MIT/Heimdal krb5-specific code missing in ssh_gssapi_storecreds()" -#endif /* KRB5_GSS */ -#ifdef GSI_GSS -#error "GSI krb5-specific code missing in ssh_gssapi_storecreds()" -#endif /* GSI_GSS */ -} - -int -ssh_gssapi_userok(Gssctxt *ctx, char *user) -{ - if (ctx == NULL) { - debug3("INTERNAL ERROR: %s", __func__); - return (0); - } - - if (user == NULL || *user == '\0') - return (0); - -#ifdef HAVE___GSS_USEROK - { - int user_ok = 0; - - ctx->major = __gss_userok(&ctx->minor, ctx->src_name, user, - &user_ok); - if (GSS_ERROR(ctx->major)) { - debug2("__GSS_userok() failed"); - return (0); - } - - if (user_ok) - return (1); - - /* fall through */ - } -#else -#ifdef GSSAPI_SIMPLE_USEROK - { - /* Mechanism-generic */ - OM_uint32 min; - gss_buffer_desc buf, ename1, ename2; - gss_name_t iname, cname; - int eql; - - buf.value = user; - buf.length = strlen(user); - ctx->major = gss_import_name(&ctx->minor, &buf, - GSS_C_NULL_OID, &iname); - if (GSS_ERROR(ctx->major)) { - ssh_gssapi_error(ctx, - "importing name for authorizing initiator"); - goto failed_simple_userok; - } - - ctx->major = gss_canonicalize_name(&ctx->minor, iname, - ctx->actual_mech, &cname); - (void) gss_release_name(&min, &iname); - if (GSS_ERROR(ctx->major)) { - ssh_gssapi_error(ctx, "canonicalizing name"); - goto failed_simple_userok; - } - - ctx->major = gss_export_name(&ctx->minor, cname, &ename1); - (void) gss_release_name(&min, &cname); - if (GSS_ERROR(ctx->major)) { - ssh_gssapi_error(ctx, "exporting name"); - goto failed_simple_userok; - } - - ctx->major = gss_export_name(&ctx->minor, ctx->src_name, - &ename2); - if (GSS_ERROR(ctx->major)) { - ssh_gssapi_error(ctx, - "exporting client principal name"); - (void) gss_release_buffer(&min, &ename1); - goto failed_simple_userok; - } - - eql = (ename1.length == ename2.length && - memcmp(ename1.value, ename2.value, ename1.length) == 0); - - (void) gss_release_buffer(&min, &ename1); - (void) gss_release_buffer(&min, &ename2); - - if (eql) - return (1); - /* fall through */ - } -failed_simple_userok: -#endif /* GSSAPI_SIMPLE_USEROK */ -#ifdef HAVE_GSSCRED_API - { - /* Mechanism-generic, Solaris-specific */ - OM_uint32 maj; - uid_t uid; - struct passwd *pw; - - maj = gsscred_name_to_unix_cred(ctx->src_name, - ctx->actual_mech, &uid, NULL, NULL, NULL); - - if (GSS_ERROR(maj)) - goto failed_simple_gsscred_userok; - - if ((pw = getpwnam(user)) == NULL) - goto failed_simple_gsscred_userok; - - if (pw->pw_uid == uid) - return (1); - /* fall through */ - } - -failed_simple_gsscred_userok: -#endif /* HAVE_GSSCRED_API */ -#ifdef KRB5_GSS - if (ssh_gssapi_is_krb5(ctx->mech)) - if (ssh_gssapi_krb5_userok(ctx->src_name, user)) - return (1); -#endif /* KRB5_GSS */ -#ifdef GSI_GSS - if (ssh_gssapi_is_gsi(ctx->mech)) - if (ssh_gssapi_gsi_userok(ctx->src_name, user)) - return (1); -#endif /* GSI_GSS */ -#endif /* HAVE___GSS_USEROK */ - - /* default to not authorized */ - return (0); -} - -char * -ssh_gssapi_localname(Gssctxt *ctx) -{ - if (ctx == NULL) { - debug3("INTERNAL ERROR: %s", __func__); - return (NULL); - } - - debug2("Mapping initiator GSS-API principal to local username"); -#ifdef HAVE_GSSCRED_API - { - /* Mechanism-generic, Solaris-specific */ - OM_uint32 maj; - uid_t uid; - struct passwd *pw; - - if (ctx->src_name == GSS_C_NO_NAME) - goto failed_gsscred_localname; - - maj = gsscred_name_to_unix_cred(ctx->src_name, - ctx->actual_mech, &uid, NULL, NULL, NULL); - - if (GSS_ERROR(maj)) - goto failed_gsscred_localname; - - if ((pw = getpwuid(uid)) == NULL) - goto failed_gsscred_localname; - - debug2("Mapped the initiator to: %s", pw->pw_name); - return (xstrdup(pw->pw_name)); - } -failed_gsscred_localname: -#endif /* HAVE_GSSCRED_API */ -#ifdef KRB5_GSS -#error "ssh_gssapi_krb5_localname() not implemented" - if (ssh_gssapi_is_krb5(ctx->mech)) - return (ssh_gssapi_krb5_localname(ctx->src_name)); -#endif /* KRB5_GSS */ -#ifdef GSI_GSS -#error "ssh_gssapi_gsi_localname() not implemented" - if (ssh_gssapi_is_gsi(ctx->mech)) - return (ssh_gssapi_gsi_localname(ctx->src_name)); -#endif /* GSI_GSS */ - return (NULL); -} -#endif /* GSSAPI */ diff --git a/usr/src/cmd/ssh/sshd/loginrec.c b/usr/src/cmd/ssh/sshd/loginrec.c deleted file mode 100644 index 33998b02b9..0000000000 --- a/usr/src/cmd/ssh/sshd/loginrec.c +++ /dev/null @@ -1,1533 +0,0 @@ -/* - * Copyright (c) 2000 Andre Lucas. All rights reserved. - * Portions copyright (c) 1998 Todd C. Miller - * Portions copyright (c) 1996 Jason Downs - * Portions copyright (c) 1996 Theo de Raadt - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Markus Friedl. - * 4. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -/* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - -/** - ** loginrec.c: platform-independent login recording and lastlog retrieval - **/ - -/* - The new login code explained - ============================ - - This code attempts to provide a common interface to login recording - (utmp and friends) and last login time retrieval. - - Its primary means of achieving this is to use 'struct logininfo', a - union of all the useful fields in the various different types of - system login record structures one finds on UNIX variants. - - We depend on autoconf to define which recording methods are to be - used, and which fields are contained in the relevant data structures - on the local system. Many C preprocessor symbols affect which code - gets compiled here. - - The code is designed to make it easy to modify a particular - recording method, without affecting other methods nor requiring so - many nested conditional compilation blocks as were commonplace in - the old code. - - For login recording, we try to use the local system's libraries as - these are clearly most likely to work correctly. For utmp systems - this usually means login() and logout() or setutent() etc., probably - in libutil, along with logwtmp() etc. On these systems, we fall back - to writing the files directly if we have to, though this method - requires very thorough testing so we do not corrupt local auditing - information. These files and their access methods are very system - specific indeed. - - For utmpx systems, the corresponding library functions are - setutxent() etc. To the author's knowledge, all utmpx systems have - these library functions and so no direct write is attempted. If such - a system exists and needs support, direct analogues of the [uw]tmp - code should suffice. - - Retrieving the time of last login ('lastlog') is in some ways even - more problemmatic than login recording. Some systems provide a - simple table of all users which we seek based on uid and retrieve a - relatively standard structure. Others record the same information in - a directory with a separate file, and others don't record the - information separately at all. For systems in the latter category, - we look backwards in the wtmp or wtmpx file for the last login entry - for our user. Naturally this is slower and on busy systems could - incur a significant performance penalty. - - Calling the new code - -------------------- - - In OpenSSH all login recording and retrieval is performed in - login.c. Here you'll find working examples. Also, in the logintest.c - program there are more examples. - - Internal handler calling method - ------------------------------- - - When a call is made to login_login() or login_logout(), both - routines set a struct logininfo flag defining which action (log in, - or log out) is to be taken. They both then call login_write(), which - calls whichever of the many structure-specific handlers autoconf - selects for the local system. - - The handlers themselves handle system data structure specifics. Both - struct utmp and struct utmpx have utility functions (see - construct_utmp*()) to try to make it simpler to add extra systems - that introduce new features to either structure. - - While it may seem terribly wasteful to replicate so much similar - code for each method, experience has shown that maintaining code to - write both struct utmp and utmpx in one function, whilst maintaining - support for all systems whether they have library support or not, is - a difficult and time-consuming task. - - Lastlog support proceeds similarly. Functions login_get_lastlog() - (and its OpenSSH-tuned friend login_get_lastlog_time()) call - getlast_entry(), which tries one of three methods to find the last - login time. It uses local system lastlog support if it can, - otherwise it tries wtmp or wtmpx before giving up and returning 0, - meaning "tilt". - - Maintenance - ----------- - - In many cases it's possible to tweak autoconf to select the correct - methods for a particular platform, either by improving the detection - code (best), or by presetting DISABLE_<method> or CONF_<method>_FILE - symbols for the platform. - - Use logintest to check which symbols are defined before modifying - configure.ac and loginrec.c. (You have to build logintest yourself - with 'make logintest' as it's not built by default.) - - Otherwise, patches to the specific method(s) are very helpful! - -*/ - -/** - ** TODO: - ** homegrown ttyslot() - ** test, test, test - ** - ** Platform status: - ** ---------------- - ** - ** Known good: - ** Linux (Redhat 6.2, Debian) - ** Solaris - ** HP-UX 10.20 (gcc only) - ** IRIX - ** NeXT - M68k/HPPA/Sparc (4.2/3.3) - ** - ** Testing required: Please send reports! - ** NetBSD - ** HP-UX 11 - ** AIX - ** - ** Platforms with known problems: - ** Some variants of Slackware Linux - ** - **/ - -#include "includes.h" - -#include "ssh.h" -#include "xmalloc.h" -#include "loginrec.h" -#include "log.h" -#include "atomicio.h" - -RCSID("$Id: loginrec.c,v 1.44 2002/09/26 00:38:49 tim Exp $"); - -#ifdef HAVE_UTIL_H -# include <util.h> -#endif - -#ifdef HAVE_LIBUTIL_H -# include <libutil.h> -#endif - -/** - ** prototypes for helper functions in this file - **/ - -#if HAVE_UTMP_H -void set_utmp_time(struct logininfo *li, struct utmp *ut); -void construct_utmp(struct logininfo *li, struct utmp *ut); -#endif - -#ifdef HAVE_UTMPX_H -void set_utmpx_time(struct logininfo *li, struct utmpx *ut); -void construct_utmpx(struct logininfo *li, struct utmpx *ut); -#endif - -int utmp_write_entry(struct logininfo *li); -int utmpx_write_entry(struct logininfo *li); -int wtmp_write_entry(struct logininfo *li); -int wtmpx_write_entry(struct logininfo *li); -int lastlog_write_entry(struct logininfo *li); -int syslogin_write_entry(struct logininfo *li); - -int getlast_entry(struct logininfo *li); -int lastlog_get_entry(struct logininfo *li); -int wtmp_get_entry(struct logininfo *li); -int wtmpx_get_entry(struct logininfo *li); - -/* pick the shortest string */ -#define MIN_SIZEOF(s1,s2) ( sizeof(s1) < sizeof(s2) ? sizeof(s1) : sizeof(s2) ) - -/** - ** platform-independent login functions - **/ - -/* login_login(struct logininfo *) -Record a login - * - * Call with a pointer to a struct logininfo initialised with - * login_init_entry() or login_alloc_entry() - * - * Returns: - * >0 if successful - * 0 on failure (will use OpenSSH's logging facilities for diagnostics) - */ -int -login_login (struct logininfo *li) -{ - li->type = LTYPE_LOGIN; - return login_write(li); -} - - -/* login_logout(struct logininfo *) - Record a logout - * - * Call as with login_login() - * - * Returns: - * >0 if successful - * 0 on failure (will use OpenSSH's logging facilities for diagnostics) - */ -int -login_logout(struct logininfo *li) -{ - li->type = LTYPE_LOGOUT; - return login_write(li); -} - -/* login_get_lastlog_time(int) - Retrieve the last login time - * - * Retrieve the last login time for the given uid. Will try to use the - * system lastlog facilities if they are available, but will fall back - * to looking in wtmp/wtmpx if necessary - * - * Returns: - * 0 on failure, or if user has never logged in - * Time in seconds from the epoch if successful - * - * Useful preprocessor symbols: - * DISABLE_LASTLOG: If set, *never* even try to retrieve lastlog - * info - * USE_LASTLOG: If set, indicates the presence of system lastlog - * facilities. If this and DISABLE_LASTLOG are not set, - * try to retrieve lastlog information from wtmp/wtmpx. - */ -#if 0 -unsigned int -login_get_lastlog_time(const int uid) -{ - struct logininfo li; - - if (login_get_lastlog(&li, uid)) - return li.tv_sec; - else - return 0; -} -#endif - -/* login_get_lastlog(struct logininfo *, int) - Retrieve a lastlog entry - * - * Retrieve a logininfo structure populated (only partially) with - * information from the system lastlog data, or from wtmp/wtmpx if no - * system lastlog information exists. - * - * Note this routine must be given a pre-allocated logininfo. - * - * Returns: - * >0: A pointer to your struct logininfo if successful - * 0 on failure (will use OpenSSH's logging facilities for diagnostics) - * - */ -struct logininfo * -login_get_lastlog(struct logininfo *li, const int uid) -{ - struct passwd *pw; - - (void) memset(li, '\0', sizeof(*li)); - li->uid = uid; - - /* - * If we don't have a 'real' lastlog, we need the username to - * reliably search wtmp(x) for the last login (see - * wtmp_get_entry().) - */ - pw = getpwuid(uid); - if (pw == NULL) - fatal("login_get_lastlog: Cannot find account for uid %i", uid); - - /* No MIN_SIZEOF here - we absolutely *must not* truncate the - * username */ - (void) strlcpy(li->username, pw->pw_name, sizeof(li->username)); - - if (getlast_entry(li)) - return li; - else - return NULL; -} - - -/* login_alloc_entry() - Allocate and initialise a logininfo - * structure - * - * This function creates a new struct logininfo, a data structure - * meant to carry the information required to portably record login info. - * - * Returns a pointer to a newly created struct logininfo. If memory - * allocation fails, the program halts. - */ -struct -logininfo *login_alloc_entry(int pid, const char *username, - const char *hostname, const char *line, - const char *progname) -{ - struct logininfo *newli; - - newli = (struct logininfo *) xmalloc (sizeof(*newli)); - (void)login_init_entry(newli, pid, username, hostname, line, progname); - return newli; -} - - -/* login_free_entry(struct logininfo *) - free struct memory */ -void -login_free_entry(struct logininfo *li) -{ - xfree(li); -} - - -/* login_init_entry() - * - initialise a struct logininfo - * - * Populates a new struct logininfo, a data structure meant to carry - * the information required to portably record login info. - * - * Returns: 1 - */ -int -login_init_entry(struct logininfo *li, int pid, const char *username, - const char *hostname, const char *line, const char *progname) -{ - struct passwd *pw; - - (void) memset(li, 0, sizeof(*li)); - - li->pid = pid; - - /* set the line information */ - if (line) - (void) line_fullname(li->line, line, sizeof(li->line)); - else - li->line_null = 1; - - if (progname) - (void) strlcpy(li->progname, progname, sizeof(li->progname)); - else - li->progname_null = 1; - - if (username) { - (void) strlcpy(li->username, username, sizeof(li->username)); - pw = getpwnam(li->username); - if (pw == NULL) - fatal("login_init_entry: Cannot find user \"%s\"", li->username); - li->uid = pw->pw_uid; - } - - if (hostname) - (void) strlcpy(li->hostname, hostname, sizeof(li->hostname)); - - return 1; -} - -/* login_set_current_time(struct logininfo *) - set the current time - * - * Set the current time in a logininfo structure. This function is - * meant to eliminate the need to deal with system dependencies for - * time handling. - */ -void -login_set_current_time(struct logininfo *li) -{ - struct timeval tv; - - (void) gettimeofday(&tv, NULL); - - li->tv_sec = tv.tv_sec; - li->tv_usec = tv.tv_usec; -} - -/* copy a sockaddr_* into our logininfo */ -void -login_set_addr(struct logininfo *li, const struct sockaddr *sa, - const unsigned int sa_size) -{ - unsigned int bufsize = sa_size; - - /* make sure we don't overrun our union */ - if (sizeof(li->hostaddr) < sa_size) - bufsize = sizeof(li->hostaddr); - - (void) memcpy((void *)&(li->hostaddr.sa), (const void *)sa, bufsize); -} - - -/** - ** login_write: Call low-level recording functions based on autoconf - ** results - **/ -int -login_write (struct logininfo *li) -{ -#ifndef HAVE_CYGWIN - if ((int)geteuid() != 0) { - log("Attempt to write login records by non-root user (aborting)"); - return 1; - } -#endif - - /* set the timestamp */ - login_set_current_time(li); -#ifdef USE_LOGIN - syslogin_write_entry(li); -#endif -#ifdef USE_LASTLOG - if (li->type == LTYPE_LOGIN) { - (void) lastlog_write_entry(li); - } -#endif -#ifdef USE_UTMP - utmp_write_entry(li); -#endif -#ifdef USE_WTMP - wtmp_write_entry(li); -#endif -#ifdef USE_UTMPX - (void) utmpx_write_entry(li); -#endif -#ifdef USE_WTMPX - (void) wtmpx_write_entry(li); -#endif - return 0; -} - -/** - ** getlast_entry: Call low-level functions to retrieve the last login - ** time. - **/ - -/* take the uid in li and return the last login time */ -int -getlast_entry(struct logininfo *li) -{ -#ifdef USE_LASTLOG - return(lastlog_get_entry(li)); -#else /* !USE_LASTLOG */ - -#ifdef DISABLE_LASTLOG - /* On some systems we shouldn't even try to obtain last login - * time, e.g. AIX */ - return 0; -# else /* DISABLE_LASTLOG */ - /* Try to retrieve the last login time from wtmp */ -# if defined(USE_WTMP) && (defined(HAVE_TIME_IN_UTMP) || defined(HAVE_TV_IN_UTMP)) - /* retrieve last login time from utmp */ - return (wtmp_get_entry(li)); -# else /* defined(USE_WTMP) && (defined(HAVE_TIME_IN_UTMP) || defined(HAVE_TV_IN_UTMP)) */ - /* If wtmp isn't available, try wtmpx */ -# if defined(USE_WTMPX) && (defined(HAVE_TIME_IN_UTMPX) || defined(HAVE_TV_IN_UTMPX)) - /* retrieve last login time from utmpx */ - return (wtmpx_get_entry(li)); -# else - /* Give up: No means of retrieving last login time */ - return 0; -# endif /* USE_WTMPX && (HAVE_TIME_IN_UTMPX || HAVE_TV_IN_UTMPX) */ -# endif /* USE_WTMP && (HAVE_TIME_IN_UTMP || HAVE_TV_IN_UTMP) */ -# endif /* DISABLE_LASTLOG */ -#endif /* USE_LASTLOG */ -} - - - -/* - * 'line' string utility functions - * - * These functions process the 'line' string into one of three forms: - * - * 1. The full filename (including '/dev') - * 2. The stripped name (excluding '/dev') - * 3. The abbreviated name (e.g. /dev/ttyp00 -> yp00 - * /dev/pts/1 -> ts/1 ) - * - * Form 3 is used on some systems to identify a .tmp.? entry when - * attempting to remove it. Typically both addition and removal is - * performed by one application - say, sshd - so as long as the choice - * uniquely identifies a terminal it's ok. - */ - - -/* line_fullname(): add the leading '/dev/' if it doesn't exist make - * sure dst has enough space, if not just copy src (ugh) */ -char * -line_fullname(char *dst, const char *src, int dstsize) -{ - (void) memset(dst, '\0', dstsize); - /* "sshd" is special, like "ftp" */ - if (strcmp(src, "sshd") || - ((strncmp(src, "/dev/", 5) == 0) || (dstsize < (strlen(src) + 5)))) { - (void) strlcpy(dst, src, dstsize); - } else { - (void) strlcpy(dst, "/dev/", dstsize); - (void) strlcat(dst, src, dstsize); - } - return dst; -} - -/* line_stripname(): strip the leading '/dev' if it exists, return dst */ -char * -line_stripname(char *dst, const char *src, int dstsize) -{ - (void) memset(dst, '\0', dstsize); - if (strncmp(src, "/dev/", 5) == 0) - (void) strlcpy(dst, src + 5, dstsize); - else - (void) strlcpy(dst, src, dstsize); - return dst; -} - -/* line_abbrevname(): Return the abbreviated (usually four-character) - * form of the line (Just use the last <dstsize> characters of the - * full name.) - * - * NOTE: use strncpy because we do NOT necessarily want zero - * termination */ -char * -line_abbrevname(char *dst, const char *src, int dstsize) -{ - size_t len; - - (void) memset(dst, '\0', dstsize); - - /* Always skip prefix if present */ - if (strncmp(src, "/dev/", 5) == 0) - src += 5; - -#ifdef WITH_ABBREV_NO_TTY - if (strncmp(src, "tty", 3) == 0) - src += 3; -#endif - - len = strlen(src); - - if (len > 0) { - if (((int)len - dstsize) > 0) - src += ((int)len - dstsize); - - /* note: _don't_ change this to strlcpy */ - (void) strncpy(dst, src, (size_t)dstsize); - } - - return dst; -} - -/** - ** utmp utility functions - ** - ** These functions manipulate struct utmp, taking system differences - ** into account. - **/ - -#if defined(USE_UTMP) || defined (USE_WTMP) || defined (USE_LOGIN) - -/* build the utmp structure */ -void -set_utmp_time(struct logininfo *li, struct utmp *ut) -{ -# ifdef HAVE_TV_IN_UTMP - ut->ut_tv.tv_sec = li->tv_sec; - ut->ut_tv.tv_usec = li->tv_usec; -# else -# ifdef HAVE_TIME_IN_UTMP - ut->ut_time = li->tv_sec; -# endif -# endif -} - -void -construct_utmp(struct logininfo *li, - struct utmp *ut) -{ - (void) memset(ut, '\0', sizeof(*ut)); - - /* First fill out fields used for both logins and logouts */ - -# ifdef HAVE_ID_IN_UTMP - (void) line_abbrevname(ut->ut_id, li->line, sizeof(ut->ut_id)); -# endif - -# ifdef HAVE_TYPE_IN_UTMP - /* This is done here to keep utmp constants out of struct logininfo */ - switch (li->type) { - case LTYPE_LOGIN: - ut->ut_type = USER_PROCESS; -#ifdef _UNICOS - cray_set_tmpdir(ut); -#endif - break; - case LTYPE_LOGOUT: - ut->ut_type = DEAD_PROCESS; -#ifdef _UNICOS - cray_retain_utmp(ut, li->pid); -#endif - break; - } -# endif - set_utmp_time(li, ut); - - (void) line_stripname(ut->ut_line, li->line, sizeof(ut->ut_line)); - -# ifdef HAVE_PID_IN_UTMP - ut->ut_pid = li->pid; -# endif - - /* If we're logging out, leave all other fields blank */ - if (li->type == LTYPE_LOGOUT) - return; - - /* - * These fields are only used when logging in, and are blank - * for logouts. - */ - - /* Use strncpy because we don't necessarily want null termination */ - (void) strncpy(ut->ut_name, li->username, MIN_SIZEOF(ut->ut_name, li->username)); -# ifdef HAVE_HOST_IN_UTMP - (void) strncpy(ut->ut_host, li->hostname, MIN_SIZEOF(ut->ut_host, li->hostname)); -# endif -# ifdef HAVE_ADDR_IN_UTMP - /* this is just a 32-bit IP address */ - if (li->hostaddr.sa.sa_family == AF_INET) - ut->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr; -# endif -} -#endif /* USE_UTMP || USE_WTMP || USE_LOGIN */ - -/** - ** utmpx utility functions - ** - ** These functions manipulate struct utmpx, accounting for system - ** variations. - **/ - -#if defined(USE_UTMPX) || defined (USE_WTMPX) -/* build the utmpx structure */ -void -set_utmpx_time(struct logininfo *li, struct utmpx *utx) -{ -# ifdef HAVE_TV_IN_UTMPX - utx->ut_tv.tv_sec = li->tv_sec; - utx->ut_tv.tv_usec = li->tv_usec; -# else /* HAVE_TV_IN_UTMPX */ -# ifdef HAVE_TIME_IN_UTMPX - utx->ut_time = li->tv_sec; -# endif /* HAVE_TIME_IN_UTMPX */ -# endif /* HAVE_TV_IN_UTMPX */ -} - -void -construct_utmpx(struct logininfo *li, struct utmpx *utx) -{ - (void) memset(utx, '\0', sizeof(*utx)); -# ifdef HAVE_ID_IN_UTMPX - (void) line_abbrevname(utx->ut_id, li->line, sizeof(utx->ut_id)); -# endif - - /* this is done here to keep utmp constants out of loginrec.h */ - switch (li->type) { - case LTYPE_LOGIN: - utx->ut_type = USER_PROCESS; - break; - case LTYPE_LOGOUT: - utx->ut_type = DEAD_PROCESS; - break; - } - if (!li->line_null) - (void) line_stripname(utx->ut_line, li->line, sizeof(utx->ut_line)); - else if (!li->progname_null) - (void) line_stripname(utx->ut_line, li->progname, sizeof(utx->ut_line)); - - set_utmpx_time(li, utx); - utx->ut_pid = li->pid; - /* strncpy(): Don't necessarily want null termination */ - (void) strncpy(utx->ut_name, li->username, MIN_SIZEOF(utx->ut_name, li->username)); - - if (li->type == LTYPE_LOGOUT) - return; - - /* - * These fields are only used when logging in, and are blank - * for logouts. - */ - -# ifdef HAVE_HOST_IN_UTMPX - (void) strncpy(utx->ut_host, li->hostname, MIN_SIZEOF(utx->ut_host, li->hostname)); -# endif -# ifdef HAVE_ADDR_IN_UTMPX - /* this is just a 32-bit IP address */ - if (li->hostaddr.sa.sa_family == AF_INET) - utx->ut_addr = li->hostaddr.sa_in.sin_addr.s_addr; -# endif -# ifdef HAVE_SYSLEN_IN_UTMPX - /* ut_syslen is the length of the utx_host string */ - utx->ut_syslen = MIN(strlen(li->hostname), sizeof(utx->ut_host)); -# endif -} -#endif /* USE_UTMPX || USE_WTMPX */ - -/** - ** Low-level utmp functions - **/ - -/* FIXME: (ATL) utmp_write_direct needs testing */ -#ifdef USE_UTMP - -/* if we can, use pututline() etc. */ -# if !defined(DISABLE_PUTUTLINE) && defined(HAVE_SETUTENT) && \ - defined(HAVE_PUTUTLINE) -# define UTMP_USE_LIBRARY -# endif - - -/* write a utmp entry with the system's help (pututline() and pals) */ -# ifdef UTMP_USE_LIBRARY -static int -utmp_write_library(struct logininfo *li, struct utmp *ut) -{ - setutent(); - pututline(ut); - -# ifdef HAVE_ENDUTENT - endutent(); -# endif - return 1; -} -# else /* UTMP_USE_LIBRARY */ - -/* write a utmp entry direct to the file */ -/* This is a slightly modification of code in OpenBSD's login.c */ -static int -utmp_write_direct(struct logininfo *li, struct utmp *ut) -{ - struct utmp old_ut; - register int fd; - int tty; - - /* FIXME: (ATL) ttyslot() needs local implementation */ - -#if defined(HAVE_GETTTYENT) - register struct ttyent *ty; - - tty=0; - - setttyent(); - while ((struct ttyent *)0 != (ty = getttyent())) { - tty++; - if (!strncmp(ty->ty_name, ut->ut_line, sizeof(ut->ut_line))) - break; - } - endttyent(); - - if((struct ttyent *)0 == ty) { - log("utmp_write_entry: tty not found"); - return(1); - } -#else /* FIXME */ - - tty = ttyslot(); /* seems only to work for /dev/ttyp? style names */ - -#endif /* HAVE_GETTTYENT */ - - if (tty > 0 && (fd = open(UTMP_FILE, O_RDWR|O_CREAT, 0644)) >= 0) { - (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET); - /* - * Prevent luser from zero'ing out ut_host. - * If the new ut_line is empty but the old one is not - * and ut_line and ut_name match, preserve the old ut_line. - */ - if (atomicio(read, fd, &old_ut, sizeof(old_ut)) == sizeof(old_ut) && - (ut->ut_host[0] == '\0') && (old_ut.ut_host[0] != '\0') && - (strncmp(old_ut.ut_line, ut->ut_line, sizeof(ut->ut_line)) == 0) && - (strncmp(old_ut.ut_name, ut->ut_name, sizeof(ut->ut_name)) == 0)) { - (void)memcpy(ut->ut_host, old_ut.ut_host, sizeof(ut->ut_host)); - } - - (void)lseek(fd, (off_t)(tty * sizeof(struct utmp)), SEEK_SET); - if (atomicio(write, fd, ut, sizeof(*ut)) != sizeof(*ut)) - log("utmp_write_direct: error writing %s: %s", - UTMP_FILE, strerror(errno)); - - (void)close(fd); - return 1; - } else { - return 0; - } -} -# endif /* UTMP_USE_LIBRARY */ - -static int -utmp_perform_login(struct logininfo *li) -{ - struct utmp ut; - - construct_utmp(li, &ut); -# ifdef UTMP_USE_LIBRARY - if (!utmp_write_library(li, &ut)) { - log("utmp_perform_login: utmp_write_library() failed"); - return 0; - } -# else - if (!utmp_write_direct(li, &ut)) { - log("utmp_perform_login: utmp_write_direct() failed"); - return 0; - } -# endif - return 1; -} - - -static int -utmp_perform_logout(struct logininfo *li) -{ - struct utmp ut; - - construct_utmp(li, &ut); -# ifdef UTMP_USE_LIBRARY - if (!utmp_write_library(li, &ut)) { - log("utmp_perform_logout: utmp_write_library() failed"); - return 0; - } -# else - if (!utmp_write_direct(li, &ut)) { - log("utmp_perform_logout: utmp_write_direct() failed"); - return 0; - } -# endif - return 1; -} - - -int -utmp_write_entry(struct logininfo *li) -{ - if (li->line_null) { - debug3("not writing utmp entry"); - return 1; - } - debug3("writing utmp entry"); - - switch(li->type) { - case LTYPE_LOGIN: - return utmp_perform_login(li); - - case LTYPE_LOGOUT: - return utmp_perform_logout(li); - - default: - log("utmp_write_entry: invalid type field"); - return 0; - } -} -#endif /* USE_UTMP */ - - -/** - ** Low-level utmpx functions - **/ - -/* not much point if we don't want utmpx entries */ -#ifdef USE_UTMPX - -/* if we have the wherewithall, use pututxline etc. */ -# if !defined(DISABLE_PUTUTXLINE) && defined(HAVE_SETUTXENT) && \ - defined(HAVE_PUTUTXLINE) -# define UTMPX_USE_LIBRARY -# endif - - -/* write a utmpx entry with the system's help (pututxline() and pals) */ -# ifdef UTMPX_USE_LIBRARY -static int -utmpx_write_library(struct logininfo *li, struct utmpx *utx) -{ - setutxent(); - (void) pututxline(utx); - -# ifdef HAVE_ENDUTXENT - endutxent(); -# endif - return 1; -} - -# else /* UTMPX_USE_LIBRARY */ - -/* write a utmp entry direct to the file */ -static int -utmpx_write_direct(struct logininfo *li, struct utmpx *utx) -{ - log("utmpx_write_direct: not implemented!"); - return 0; -} -# endif /* UTMPX_USE_LIBRARY */ - -static int -utmpx_perform_login(struct logininfo *li) -{ - struct utmpx utx; - - construct_utmpx(li, &utx); -# ifdef UTMPX_USE_LIBRARY - if (!utmpx_write_library(li, &utx)) { - log("tmpx_perform_login: utmp_write_library() failed"); - return 0; - } -# else - if (!utmpx_write_direct(li, &ut)) { - log("utmpx_perform_login: utmp_write_direct() failed"); - return 0; - } -# endif - return 1; -} - - -static int -utmpx_perform_logout(struct logininfo *li) -{ - struct utmpx utx; - - construct_utmpx(li, &utx); -# ifdef HAVE_ID_IN_UTMPX - (void) line_abbrevname(utx.ut_id, li->line, sizeof(utx.ut_id)); -# endif -# ifdef HAVE_TYPE_IN_UTMPX - utx.ut_type = DEAD_PROCESS; -# endif - -# ifdef UTMPX_USE_LIBRARY - (void) utmpx_write_library(li, &utx); -# else - utmpx_write_direct(li, &utx); -# endif - return 1; -} - -int -utmpx_write_entry(struct logininfo *li) -{ - if (li->line_null) { - debug3("not writing utmpx entry"); - return 1; - } - debug3("writing utmpx entry"); - - switch(li->type) { - case LTYPE_LOGIN: - return utmpx_perform_login(li); - case LTYPE_LOGOUT: - return utmpx_perform_logout(li); - default: - log("utmpx_write_entry: invalid type field"); - return 0; - } -} -#endif /* USE_UTMPX */ - - -/** - ** Low-level wtmp functions - **/ - -#ifdef USE_WTMP - -/* write a wtmp entry direct to the end of the file */ -/* This is a slight modification of code in OpenBSD's logwtmp.c */ -static int -wtmp_write(struct logininfo *li, struct utmp *ut) -{ - struct stat buf; - int fd, ret = 1; - - if ((fd = open(WTMP_FILE, O_WRONLY|O_APPEND, 0)) < 0) { - log("wtmp_write: problem writing %s: %s", - WTMP_FILE, strerror(errno)); - return 0; - } - if (fstat(fd, &buf) == 0) - if (atomicio(write, fd, ut, sizeof(*ut)) != sizeof(*ut)) { - (void) ftruncate(fd, buf.st_size); - log("wtmp_write: problem writing %s: %s", - WTMP_FILE, strerror(errno)); - ret = 0; - } - (void)close(fd); - return ret; -} - -static int -wtmp_perform_login(struct logininfo *li) -{ - struct utmp ut; - - construct_utmp(li, &ut); - return wtmp_write(li, &ut); -} - - -static int -wtmp_perform_logout(struct logininfo *li) -{ - struct utmp ut; - - construct_utmp(li, &ut); - return wtmp_write(li, &ut); -} - - -int -wtmp_write_entry(struct logininfo *li) -{ - switch(li->type) { - case LTYPE_LOGIN: - return wtmp_perform_login(li); - case LTYPE_LOGOUT: - return wtmp_perform_logout(li); - default: - log("wtmp_write_entry: invalid type field"); - return 0; - } -} - - -/* Notes on fetching login data from wtmp/wtmpx - * - * Logouts are usually recorded with (amongst other things) a blank - * username on a given tty line. However, some systems (HP-UX is one) - * leave all fields set, but change the ut_type field to DEAD_PROCESS. - * - * Since we're only looking for logins here, we know that the username - * must be set correctly. On systems that leave it in, we check for - * ut_type==USER_PROCESS (indicating a login.) - * - * Portability: Some systems may set something other than USER_PROCESS - * to indicate a login process. I don't know of any as I write. Also, - * it's possible that some systems may both leave the username in - * place and not have ut_type. - */ - -/* return true if this wtmp entry indicates a login */ -static int -wtmp_islogin(struct logininfo *li, struct utmp *ut) -{ - if (strncmp(li->username, ut->ut_name, - MIN_SIZEOF(li->username, ut->ut_name)) == 0) { -# ifdef HAVE_TYPE_IN_UTMP - if (ut->ut_type & USER_PROCESS) - return 1; -# else - return 1; -# endif - } - return 0; -} - -int -wtmp_get_entry(struct logininfo *li) -{ - struct stat st; - struct utmp ut; - int fd, found=0; - - /* Clear the time entries in our logininfo */ - li->tv_sec = li->tv_usec = 0; - - if ((fd = open(WTMP_FILE, O_RDONLY)) < 0) { - log("wtmp_get_entry: problem opening %s: %s", - WTMP_FILE, strerror(errno)); - return 0; - } - if (fstat(fd, &st) != 0) { - log("wtmp_get_entry: couldn't stat %s: %s", - WTMP_FILE, strerror(errno)); - (void) close(fd); - return 0; - } - - /* Seek to the start of the last struct utmp */ - if (lseek(fd, -(off_t)sizeof(struct utmp), SEEK_END) == -1) { - /* Looks like we've got a fresh wtmp file */ - (void) close(fd); - return 0; - } - - while (!found) { - if (atomicio(read, fd, &ut, sizeof(ut)) != sizeof(ut)) { - log("wtmp_get_entry: read of %s failed: %s", - WTMP_FILE, strerror(errno)); - (void) close (fd); - return 0; - } - if ( wtmp_islogin(li, &ut) ) { - found = 1; - /* We've already checked for a time in struct - * utmp, in login_getlast(). */ -# ifdef HAVE_TIME_IN_UTMP - li->tv_sec = ut.ut_time; -# else -# if HAVE_TV_IN_UTMP - li->tv_sec = ut.ut_tv.tv_sec; -# endif -# endif - (void) line_fullname(li->line, ut.ut_line, - MIN_SIZEOF(li->line, ut.ut_line)); -# ifdef HAVE_HOST_IN_UTMP - (void) strlcpy(li->hostname, ut.ut_host, - MIN_SIZEOF(li->hostname, ut.ut_host)); -# endif - continue; - } - /* Seek back 2 x struct utmp */ - if (lseek(fd, -(off_t)(2 * sizeof(struct utmp)), SEEK_CUR) == -1) { - /* We've found the start of the file, so quit */ - (void) close (fd); - return 0; - } - } - - /* We found an entry. Tidy up and return */ - (void) close(fd); - return 1; -} -# endif /* USE_WTMP */ - - -/** - ** Low-level wtmpx functions - **/ - -#ifdef USE_WTMPX -/* write a wtmpx entry direct to the end of the file */ -/* This is a slight modification of code in OpenBSD's logwtmp.c */ -static int -wtmpx_write(struct logininfo *li, struct utmpx *utx) -{ - struct stat buf; - int fd, ret = 1; - - if ((fd = open(WTMPX_FILE, O_WRONLY|O_APPEND, 0)) < 0) { - log("wtmpx_write: problem opening %s: %s", - WTMPX_FILE, strerror(errno)); - return 0; - } - - if (fstat(fd, &buf) == 0) - if (atomicio(write, fd, utx, sizeof(*utx)) != sizeof(*utx)) { - (void) ftruncate(fd, buf.st_size); - log("wtmpx_write: problem writing %s: %s", - WTMPX_FILE, strerror(errno)); - ret = 0; - } - (void)close(fd); - - return ret; -} - - -static int -wtmpx_perform_login(struct logininfo *li) -{ - struct utmpx utx; - - construct_utmpx(li, &utx); - return wtmpx_write(li, &utx); -} - - -static int -wtmpx_perform_logout(struct logininfo *li) -{ - struct utmpx utx; - - construct_utmpx(li, &utx); - return wtmpx_write(li, &utx); -} - - -int -wtmpx_write_entry(struct logininfo *li) -{ - switch(li->type) { - case LTYPE_LOGIN: - return wtmpx_perform_login(li); - case LTYPE_LOGOUT: - return wtmpx_perform_logout(li); - default: - log("wtmpx_write_entry: invalid type field"); - return 0; - } -} - -/* Please see the notes above wtmp_islogin() for information about the - next two functions */ - -/* Return true if this wtmpx entry indicates a login */ -static int -wtmpx_islogin(struct logininfo *li, struct utmpx *utx) -{ - if ( strncmp(li->username, utx->ut_name, - MIN_SIZEOF(li->username, utx->ut_name)) == 0 ) { -# ifdef HAVE_TYPE_IN_UTMPX - if (utx->ut_type == USER_PROCESS) - return 1; -# else - return 1; -# endif - } - return 0; -} - - -#if 0 -int -wtmpx_get_entry(struct logininfo *li) -{ - struct stat st; - struct utmpx utx; - int fd, found=0; - - /* Clear the time entries */ - li->tv_sec = li->tv_usec = 0; - - if ((fd = open(WTMPX_FILE, O_RDONLY)) < 0) { - log("wtmpx_get_entry: problem opening %s: %s", - WTMPX_FILE, strerror(errno)); - return 0; - } - if (fstat(fd, &st) != 0) { - log("wtmpx_get_entry: couldn't stat %s: %s", - WTMPX_FILE, strerror(errno)); - (void) close(fd); - return 0; - } - - /* Seek to the start of the last struct utmpx */ - if (lseek(fd, -(off_t)sizeof(struct utmpx), SEEK_END) == -1 ) { - /* probably a newly rotated wtmpx file */ - (void) close(fd); - return 0; - } - - while (!found) { - if (atomicio(read, fd, &utx, sizeof(utx)) != sizeof(utx)) { - log("wtmpx_get_entry: read of %s failed: %s", - WTMPX_FILE, strerror(errno)); - (void) close (fd); - return 0; - } - /* Logouts are recorded as a blank username on a particular line. - * So, we just need to find the username in struct utmpx */ - if ( wtmpx_islogin(li, &utx) ) { - found = 1; -# ifdef HAVE_TV_IN_UTMPX - li->tv_sec = utx.ut_tv.tv_sec; -# else -# ifdef HAVE_TIME_IN_UTMPX - li->tv_sec = utx.ut_time; -# endif -# endif - (void) line_fullname(li->line, utx.ut_line, sizeof(li->line)); -# ifdef HAVE_HOST_IN_UTMPX - (void) strlcpy(li->hostname, utx.ut_host, - MIN_SIZEOF(li->hostname, utx.ut_host)); -# endif - continue; - } - if (lseek(fd, -(off_t)(2 * sizeof(struct utmpx)), SEEK_CUR) == -1) { - (void) close (fd); - return 0; - } - } - - (void) close(fd); - return 1; -} -#endif -#endif /* USE_WTMPX */ - -/** - ** Low-level libutil login() functions - **/ - -#ifdef USE_LOGIN -static int -syslogin_perform_login(struct logininfo *li) -{ - struct utmp *ut; - - if (! (ut = (struct utmp *)malloc(sizeof(*ut)))) { - log("syslogin_perform_login: couldn't malloc()"); - return 0; - } - construct_utmp(li, ut); - login(ut); - - return 1; -} - -static int -syslogin_perform_logout(struct logininfo *li) -{ -# ifdef HAVE_LOGOUT - char line[8]; - - (void)line_stripname(line, li->line, sizeof(line)); - - if (!logout(line)) { - log("syslogin_perform_logout: logout() returned an error"); -# ifdef HAVE_LOGWTMP - } else { - logwtmp(line, "", ""); -# endif - } - /* FIXME: (ATL - if the need arises) What to do if we have - * login, but no logout? what if logout but no logwtmp? All - * routines are in libutil so they should all be there, - * but... */ -# endif - return 1; -} - -int -syslogin_write_entry(struct logininfo *li) -{ - switch (li->type) { - case LTYPE_LOGIN: - return syslogin_perform_login(li); - case LTYPE_LOGOUT: - return syslogin_perform_logout(li); - default: - log("syslogin_write_entry: Invalid type field"); - return 0; - } -} -#endif /* USE_LOGIN */ - -/* end of file log-syslogin.c */ - -/** - ** Low-level lastlog functions - **/ - -#ifdef USE_LASTLOG -#define LL_FILE 1 -#define LL_DIR 2 -#define LL_OTHER 3 - -static void -lastlog_construct(struct logininfo *li, struct lastlog *last) -{ - /* clear the structure */ - (void) memset(last, '\0', sizeof(*last)); - - (void)line_stripname(last->ll_line, li->line, sizeof(last->ll_line)); - (void) strlcpy(last->ll_host, li->hostname, - MIN_SIZEOF(last->ll_host, li->hostname)); - last->ll_time = li->tv_sec; -} - -static int -lastlog_filetype(char *filename) -{ - struct stat st; - - if (stat(LASTLOG_FILE, &st) != 0) { - log("lastlog_perform_login: Couldn't stat %s: %s", LASTLOG_FILE, - strerror(errno)); - return 0; - } - if (S_ISDIR(st.st_mode)) - return LL_DIR; - else if (S_ISREG(st.st_mode)) - return LL_FILE; - else - return LL_OTHER; -} - - -/* open the file (using filemode) and seek to the login entry */ -static int -lastlog_openseek(struct logininfo *li, int *fd, int filemode) -{ - off_t offset; - int type; - char lastlog_file[1024]; - - type = lastlog_filetype(LASTLOG_FILE); - switch (type) { - case LL_FILE: - (void) strlcpy(lastlog_file, LASTLOG_FILE, sizeof(lastlog_file)); - break; - case LL_DIR: - (void) snprintf(lastlog_file, sizeof(lastlog_file), "%s/%s", - LASTLOG_FILE, li->username); - break; - default: - log("lastlog_openseek: %.100s is not a file or directory!", - LASTLOG_FILE); - return 0; - } - - *fd = open(lastlog_file, filemode); - if ( *fd < 0) { - debug("lastlog_openseek: Couldn't open %s: %s", - lastlog_file, strerror(errno)); - return 0; - } - - if (type == LL_FILE) { - /* find this uid's offset in the lastlog file */ - offset = (off_t) ((long)li->uid * sizeof(struct lastlog)); - - if ( lseek(*fd, offset, SEEK_SET) != offset ) { - log("lastlog_openseek: %s->lseek(): %s", - lastlog_file, strerror(errno)); - return 0; - } - } - - return 1; -} - -static int -lastlog_perform_login(struct logininfo *li) -{ - struct lastlog last; - int fd; - - /* create our struct lastlog */ - lastlog_construct(li, &last); - - if (!lastlog_openseek(li, &fd, O_RDWR|O_CREAT)) - return(0); - - /* write the entry */ - if (atomicio(write, fd, &last, sizeof(last)) != sizeof(last)) { - (void) close(fd); - log("lastlog_write_filemode: Error writing to %s: %s", - LASTLOG_FILE, strerror(errno)); - return 0; - } - - (void) close(fd); - return 1; -} - -int -lastlog_write_entry(struct logininfo *li) -{ - switch(li->type) { - case LTYPE_LOGIN: - return lastlog_perform_login(li); - default: - log("lastlog_write_entry: Invalid type field"); - return 0; - } -} - -static void -lastlog_populate_entry(struct logininfo *li, struct lastlog *last) -{ - (void) line_fullname(li->line, last->ll_line, sizeof(li->line)); - (void) strlcpy(li->hostname, last->ll_host, - MIN_SIZEOF(li->hostname, last->ll_host)); - li->tv_sec = last->ll_time; -} - -int -lastlog_get_entry(struct logininfo *li) -{ - struct lastlog last; - int fd, ret; - - if (!lastlog_openseek(li, &fd, O_RDONLY)) - return (0); - - ret = atomicio(read, fd, &last, sizeof(last)); - close(fd); - - switch (ret) { - case 0: - memset(&last, '\0', sizeof(last)); - /* FALLTHRU */ - case sizeof(last): - lastlog_populate_entry(li, &last); - return (1); - case -1: - error("%s: Error reading from %s: %s", __func__, - LASTLOG_FILE, strerror(errno)); - return (0); - default: - error("%s: Error reading from %s: Expecting %d, got %d", - __func__, LASTLOG_FILE, (int)sizeof(last), ret); - return (0); - } - - /* NOTREACHED */ - return (0); -} -#endif /* USE_LASTLOG */ diff --git a/usr/src/cmd/ssh/sshd/mapfile-intf b/usr/src/cmd/ssh/sshd/mapfile-intf deleted file mode 100644 index 6981212730..0000000000 --- a/usr/src/cmd/ssh/sshd/mapfile-intf +++ /dev/null @@ -1,49 +0,0 @@ -# -# CDDL HEADER START -# -# The contents of this file are subject to the terms of the -# Common Development and Distribution License (the "License"). -# You may not use this file except in compliance with the License. -# -# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE -# or http://www.opensolaris.org/os/licensing. -# See the License for the specific language governing permissions -# and limitations under the License. -# -# When distributing Covered Code, include this CDDL HEADER in each -# file and include the License file at usr/src/OPENSOLARIS.LICENSE. -# If applicable, add the following below this CDDL HEADER, with the -# fields enclosed by brackets "[]" replaced with your own identifying -# information: Portions Copyright [yyyy] [name of copyright owner] -# -# CDDL HEADER END -# - -# -# Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. -# -# sshd defines its own log(), as do various other ssh utilities, thus their -# symbols are reduced to locals via MAPFILE.NGB in Makefile-ssh.common. sshd -# must export some symbols too, thus this mapfile augments the former. - -# -# MAPFILE HEADER START -# -# WARNING: STOP NOW. DO NOT MODIFY THIS FILE. -# Object versioning must comply with the rules detailed in -# -# usr/src/lib/README.mapfiles -# -# You should not be making modifications here until you've read the most current -# copy of that file. If you need help, contact a gatekeeper for guidance. -# -# MAPFILE HEADER END -# - -$mapfile_version 2 - -SYMBOL_SCOPE { - global: - allow_severity; # required by libwrap - deny_severity; # required by libwrap -}; diff --git a/usr/src/cmd/ssh/sshd/servconf.c b/usr/src/cmd/ssh/sshd/servconf.c deleted file mode 100644 index 516466bbc1..0000000000 --- a/usr/src/cmd/ssh/sshd/servconf.c +++ /dev/null @@ -1,1494 +0,0 @@ -/* - * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland - * All rights reserved - * - * As far as I am concerned, the code I have written for this software - * can be used freely for any purpose. Any derived versions of this - * software must be clearly marked as such, and if the derived work is - * incompatible with the protocol description in the RFC file, it must be - * called by a name other than "ssh" or "Secure Shell". - */ -/* - * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. - * Copyright 2013 Joyent, Inc. All rights reserved. - */ - -#include "includes.h" -RCSID("$OpenBSD: servconf.c,v 1.115 2002/09/04 18:52:42 stevesk Exp $"); - -#ifdef HAVE_DEFOPEN -#include <deflt.h> -#endif /* HAVE_DEFOPEN */ - -#if defined(KRB4) -#include <krb.h> -#endif -#if defined(KRB5) -#ifdef HEIMDAL -#include <krb.h> -#else -/* Bodge - but then, so is using the kerberos IV KEYFILE to get a Kerberos V - * keytab */ -#define KEYFILE "/etc/krb5.keytab" -#endif -#endif -#ifdef AFS -#include <kafs.h> -#endif - -#include "ssh.h" -#include "log.h" -#include "buffer.h" -#include "servconf.h" -#include "xmalloc.h" -#include "compat.h" -#include "pathnames.h" -#include "tildexpand.h" -#include "misc.h" -#include "cipher.h" -#include "kex.h" -#include "mac.h" -#include "auth.h" -#include "match.h" -#include "groupaccess.h" - -static void add_listen_addr(ServerOptions *, char *, u_short); -static void add_one_listen_addr(ServerOptions *, char *, u_short); - -extern Buffer cfg; - -/* AF_UNSPEC or AF_INET or AF_INET6 */ -extern int IPv4or6; - -/* - * Initializes the server options to their initial (unset) values. Some of those - * that stay unset after the command line options and configuration files are - * read are set to their default values in fill_default_server_options(). - */ -void -initialize_server_options(ServerOptions *options) -{ - (void) memset(options, 0, sizeof(*options)); - - /* Standard Options */ - options->num_ports = 0; - options->ports_from_cmdline = 0; - options->listen_addrs = NULL; - options->num_host_key_files = 0; - options->pid_file = NULL; - options->server_key_bits = -1; - options->login_grace_time = -1; - options->key_regeneration_time = -1; - options->permit_root_login = PERMIT_NOT_SET; - options->ignore_rhosts = -1; - options->ignore_user_known_hosts = -1; - options->print_motd = -1; - options->print_lastlog = -1; - options->x11_forwarding = -1; - options->x11_display_offset = -1; - options->x11_use_localhost = -1; - options->xauth_location = NULL; - options->strict_modes = -1; - options->keepalives = -1; - options->log_facility = SYSLOG_FACILITY_NOT_SET; - options->log_level = SYSLOG_LEVEL_NOT_SET; - options->rhosts_authentication = -1; - options->rhosts_rsa_authentication = -1; - options->hostbased_authentication = -1; - options->hostbased_uses_name_from_packet_only = -1; - options->rsa_authentication = -1; - options->pubkey_authentication = -1; -#ifdef GSSAPI - options->gss_authentication = -1; - options->gss_keyex = -1; - options->gss_store_creds = -1; - options->gss_use_session_ccache = -1; - options->gss_cleanup_creds = -1; -#endif -#if defined(KRB4) || defined(KRB5) - options->kerberos_authentication = -1; - options->kerberos_or_local_passwd = -1; - options->kerberos_ticket_cleanup = -1; -#endif -#if defined(AFS) || defined(KRB5) - options->kerberos_tgt_passing = -1; -#endif -#ifdef AFS - options->afs_token_passing = -1; -#endif - options->password_authentication = -1; - options->kbd_interactive_authentication = -1; - options->challenge_response_authentication = -1; - options->pam_authentication_via_kbd_int = -1; - options->permit_empty_passwd = -1; - options->permit_user_env = -1; - options->compression = -1; - options->allow_tcp_forwarding = -1; - options->num_allow_users = 0; - options->num_deny_users = 0; - options->num_allow_groups = 0; - options->num_deny_groups = 0; - options->ciphers = NULL; - options->macs = NULL; - options->protocol = SSH_PROTO_UNKNOWN; - options->gateway_ports = -1; - options->num_subsystems = 0; - options->max_startups_begin = -1; - options->max_startups_rate = -1; - options->max_startups = -1; - options->banner = NULL; - options->verify_reverse_mapping = -1; - options->client_alive_interval = -1; - options->client_alive_count_max = -1; - options->authorized_keys_file = NULL; - options->authorized_keys_file2 = NULL; - - options->max_auth_tries = -1; - options->max_auth_tries_log = -1; - - options->max_init_auth_tries = -1; - options->max_init_auth_tries_log = -1; - - options->lookup_client_hostnames = -1; - options->use_openssl_engine = -1; - options->chroot_directory = NULL; - options->pre_userauth_hook = NULL; - options->pam_service_name = NULL; - options->pam_service_prefix = NULL; -} - -#ifdef HAVE_DEFOPEN -/* - * Reads /etc/default/login and defaults several ServerOptions: - * - * PermitRootLogin - * PermitEmptyPasswords - * LoginGraceTime - * - * CONSOLE=* -> PermitRootLogin=without-password - * #CONSOLE=* -> PermitRootLogin=yes - * - * PASSREQ=YES -> PermitEmptyPasswords=no - * PASSREQ=NO -> PermitEmptyPasswords=yes - * #PASSREQ=* -> PermitEmptyPasswords=no - * - * TIMEOUT=<secs> -> LoginGraceTime=<secs> - * #TIMEOUT=<secs> -> LoginGraceTime=300 - */ -static -void -deflt_fill_default_server_options(ServerOptions *options) -{ - int flags; - char *ptr; - - if (defopen(_PATH_DEFAULT_LOGIN)) - return; - - /* Ignore case */ - flags = defcntl(DC_GETFLAGS, 0); - TURNOFF(flags, DC_CASE); - (void) defcntl(DC_SETFLAGS, flags); - - if (options->permit_root_login == PERMIT_NOT_SET && - (ptr = defread("CONSOLE=")) != NULL) - options->permit_root_login = PERMIT_NO_PASSWD; - - if (options->permit_empty_passwd == -1 && - (ptr = defread("PASSREQ=")) != NULL) { - if (strcasecmp("YES", ptr) == 0) - options->permit_empty_passwd = 0; - else if (strcasecmp("NO", ptr) == 0) - options->permit_empty_passwd = 1; - } - - if (options->max_init_auth_tries == -1 && - (ptr = defread("RETRIES=")) != NULL) { - options->max_init_auth_tries = atoi(ptr); - } - - if (options->max_init_auth_tries_log == -1 && - (ptr = defread("SYSLOG_FAILED_LOGINS=")) != NULL) { - options->max_init_auth_tries_log = atoi(ptr); - } - - if (options->login_grace_time == -1) { - if ((ptr = defread("TIMEOUT=")) != NULL) - options->login_grace_time = (unsigned)atoi(ptr); - else - options->login_grace_time = 300; - } - - (void) defopen((char *)NULL); -} -#endif /* HAVE_DEFOPEN */ - -void -fill_default_server_options(ServerOptions *options) -{ - -#ifdef HAVE_DEFOPEN - deflt_fill_default_server_options(options); -#endif /* HAVE_DEFOPEN */ - - /* Standard Options */ - if (options->protocol == SSH_PROTO_UNKNOWN) - options->protocol = SSH_PROTO_1|SSH_PROTO_2; - if (options->num_host_key_files == 0) { - /* fill default hostkeys for protocols */ - if (options->protocol & SSH_PROTO_1) - options->host_key_files[options->num_host_key_files++] = - _PATH_HOST_KEY_FILE; -#ifndef GSSAPI - /* With GSS keyex we can run v2 w/ no host keys */ - if (options->protocol & SSH_PROTO_2) { - options->host_key_files[options->num_host_key_files++] = - _PATH_HOST_RSA_KEY_FILE; - options->host_key_files[options->num_host_key_files++] = - _PATH_HOST_DSA_KEY_FILE; - } -#endif /* GSSAPI */ - } - if (options->num_ports == 0) - options->ports[options->num_ports++] = SSH_DEFAULT_PORT; - if (options->listen_addrs == NULL) - add_listen_addr(options, NULL, 0); - if (options->pid_file == NULL) - options->pid_file = _PATH_SSH_DAEMON_PID_FILE; - if (options->server_key_bits == -1) - options->server_key_bits = 768; - if (options->login_grace_time == -1) - options->login_grace_time = 120; - if (options->key_regeneration_time == -1) - options->key_regeneration_time = 3600; - if (options->permit_root_login == PERMIT_NOT_SET) - options->permit_root_login = PERMIT_YES; - if (options->ignore_rhosts == -1) - options->ignore_rhosts = 1; - if (options->ignore_user_known_hosts == -1) - options->ignore_user_known_hosts = 0; - if (options->print_motd == -1) - options->print_motd = 1; - if (options->print_lastlog == -1) - options->print_lastlog = 1; - if (options->x11_forwarding == -1) - options->x11_forwarding = 1; - if (options->x11_display_offset == -1) - options->x11_display_offset = 10; - if (options->x11_use_localhost == -1) - options->x11_use_localhost = 1; - if (options->xauth_location == NULL) - options->xauth_location = _PATH_XAUTH; - if (options->strict_modes == -1) - options->strict_modes = 1; - if (options->keepalives == -1) - options->keepalives = 1; - if (options->log_facility == SYSLOG_FACILITY_NOT_SET) - options->log_facility = SYSLOG_FACILITY_AUTH; - if (options->log_level == SYSLOG_LEVEL_NOT_SET) - options->log_level = SYSLOG_LEVEL_INFO; - if (options->rhosts_authentication == -1) - options->rhosts_authentication = 0; - if (options->rhosts_rsa_authentication == -1) - options->rhosts_rsa_authentication = 0; - if (options->hostbased_authentication == -1) - options->hostbased_authentication = 0; - if (options->hostbased_uses_name_from_packet_only == -1) - options->hostbased_uses_name_from_packet_only = 0; - if (options->rsa_authentication == -1) - options->rsa_authentication = 1; - if (options->pubkey_authentication == -1) - options->pubkey_authentication = 1; -#ifdef GSSAPI - if (options->gss_authentication == -1) - options->gss_authentication = 1; - if (options->gss_keyex == -1) - options->gss_keyex = 1; - if (options->gss_store_creds == -1) - options->gss_store_creds = 1; - if (options->gss_use_session_ccache == -1) - options->gss_use_session_ccache = 1; - if (options->gss_cleanup_creds == -1) - options->gss_cleanup_creds = 1; -#endif -#if defined(KRB4) || defined(KRB5) - if (options->kerberos_authentication == -1) - options->kerberos_authentication = 0; - if (options->kerberos_or_local_passwd == -1) - options->kerberos_or_local_passwd = 1; - if (options->kerberos_ticket_cleanup == -1) - options->kerberos_ticket_cleanup = 1; -#endif -#if defined(AFS) || defined(KRB5) - if (options->kerberos_tgt_passing == -1) - options->kerberos_tgt_passing = 0; -#endif -#ifdef AFS - if (options->afs_token_passing == -1) - options->afs_token_passing = 0; -#endif - if (options->password_authentication == -1) - options->password_authentication = 1; - /* - * options->pam_authentication_via_kbd_int has intentionally no default - * value since we do not need it. - */ - if (options->kbd_interactive_authentication == -1) - options->kbd_interactive_authentication = 1; - if (options->challenge_response_authentication == -1) - options->challenge_response_authentication = 1; - if (options->permit_empty_passwd == -1) - options->permit_empty_passwd = 0; - if (options->permit_user_env == -1) - options->permit_user_env = 0; - if (options->compression == -1) - options->compression = 1; - if (options->allow_tcp_forwarding == -1) - options->allow_tcp_forwarding = 1; - if (options->gateway_ports == -1) - options->gateway_ports = 0; - if (options->max_startups == -1) - options->max_startups = 10; - if (options->max_startups_rate == -1) - options->max_startups_rate = 100; /* 100% */ - if (options->max_startups_begin == -1) - options->max_startups_begin = options->max_startups; - if (options->verify_reverse_mapping == -1) - options->verify_reverse_mapping = 0; - if (options->client_alive_interval == -1) - options->client_alive_interval = 0; - if (options->client_alive_count_max == -1) - options->client_alive_count_max = 3; - if (options->authorized_keys_file2 == NULL) { - /* authorized_keys_file2 falls back to authorized_keys_file */ - if (options->authorized_keys_file != NULL) - options->authorized_keys_file2 = options->authorized_keys_file; - else - options->authorized_keys_file2 = _PATH_SSH_USER_PERMITTED_KEYS2; - } - if (options->authorized_keys_file == NULL) - options->authorized_keys_file = _PATH_SSH_USER_PERMITTED_KEYS; - - if (options->max_auth_tries == -1) - options->max_auth_tries = AUTH_FAIL_MAX; - if (options->max_auth_tries_log == -1) - options->max_auth_tries_log = options->max_auth_tries / 2; - - if (options->max_init_auth_tries == -1) - options->max_init_auth_tries = AUTH_FAIL_MAX; - if (options->max_init_auth_tries_log == -1) - options->max_init_auth_tries_log = options->max_init_auth_tries / 2; - - if (options->lookup_client_hostnames == -1) - options->lookup_client_hostnames = 1; - if (options->use_openssl_engine == -1) - options->use_openssl_engine = 1; - if (options->pam_service_prefix == NULL) - options->pam_service_prefix = _SSH_PAM_SERVICE_PREFIX; - if (options->pam_service_name == NULL) - options->pam_service_name = NULL; -} - -/* Keyword tokens. */ -typedef enum { - sBadOption, /* == unknown option */ - /* Portable-specific options */ - sPAMAuthenticationViaKbdInt, - /* Standard Options */ - sPort, sHostKeyFile, sServerKeyBits, sLoginGraceTime, sKeyRegenerationTime, - sPermitRootLogin, sLogFacility, sLogLevel, - sRhostsAuthentication, sRhostsRSAAuthentication, sRSAAuthentication, -#ifdef GSSAPI - sGssAuthentication, sGssKeyEx, sGssStoreDelegCreds, - sGssUseSessionCredCache, sGssCleanupCreds, -#endif /* GSSAPI */ -#if defined(KRB4) || defined(KRB5) - sKerberosAuthentication, sKerberosOrLocalPasswd, sKerberosTicketCleanup, -#endif -#if defined(AFS) || defined(KRB5) - sKerberosTgtPassing, -#endif -#ifdef AFS - sAFSTokenPassing, -#endif - sChallengeResponseAuthentication, - sPasswordAuthentication, sKbdInteractiveAuthentication, sListenAddress, - sPrintMotd, sPrintLastLog, sIgnoreRhosts, - sX11Forwarding, sX11DisplayOffset, sX11UseLocalhost, - sStrictModes, sEmptyPasswd, sKeepAlives, - sPermitUserEnvironment, sUseLogin, sAllowTcpForwarding, sCompression, - sAllowUsers, sDenyUsers, sAllowGroups, sDenyGroups, - sIgnoreUserKnownHosts, sCiphers, sMacs, sProtocol, sPidFile, - sGatewayPorts, sPubkeyAuthentication, sXAuthLocation, sSubsystem, sMaxStartups, - sBanner, sVerifyReverseMapping, sHostbasedAuthentication, - sHostbasedUsesNameFromPacketOnly, sClientAliveInterval, - sClientAliveCountMax, sAuthorizedKeysFile, sAuthorizedKeysFile2, - sMaxAuthTries, sMaxAuthTriesLog, sUsePrivilegeSeparation, - sLookupClientHostnames, sUseOpenSSLEngine, sChrootDirectory, - sPreUserauthHook, sMatch, sPAMServicePrefix, sPAMServiceName, - sDeprecated -} ServerOpCodes; - -#define SSHCFG_GLOBAL 0x01 /* allowed in main section of sshd_config */ -#define SSHCFG_MATCH 0x02 /* allowed inside a Match section */ -#define SSHCFG_ALL (SSHCFG_GLOBAL|SSHCFG_MATCH) - -/* Textual representation of the tokens. */ -static struct { - const char *name; - ServerOpCodes opcode; - u_int flags; -} keywords[] = { - /* Portable-specific options */ - { "PAMAuthenticationViaKbdInt", sPAMAuthenticationViaKbdInt, SSHCFG_GLOBAL }, - /* Standard Options */ - { "port", sPort, SSHCFG_GLOBAL }, - { "hostkey", sHostKeyFile, SSHCFG_GLOBAL }, - { "hostdsakey", sHostKeyFile, SSHCFG_GLOBAL }, /* alias */ - { "pidfile", sPidFile, SSHCFG_GLOBAL }, - { "serverkeybits", sServerKeyBits, SSHCFG_GLOBAL }, - { "logingracetime", sLoginGraceTime, SSHCFG_GLOBAL }, - { "keyregenerationinterval", sKeyRegenerationTime, SSHCFG_GLOBAL }, - { "permitrootlogin", sPermitRootLogin, SSHCFG_ALL }, - { "syslogfacility", sLogFacility, SSHCFG_GLOBAL }, - { "loglevel", sLogLevel, SSHCFG_GLOBAL }, - { "rhostsauthentication", sRhostsAuthentication, SSHCFG_GLOBAL }, - { "rhostsrsaauthentication", sRhostsRSAAuthentication, SSHCFG_ALL }, - { "hostbasedauthentication", sHostbasedAuthentication, SSHCFG_ALL }, - { "hostbasedusesnamefrompacketonly", sHostbasedUsesNameFromPacketOnly }, - { "rsaauthentication", sRSAAuthentication, SSHCFG_ALL }, - { "pubkeyauthentication", sPubkeyAuthentication, SSHCFG_ALL }, - { "dsaauthentication", sPubkeyAuthentication, SSHCFG_GLOBAL }, /* alias */ -#ifdef GSSAPI - { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL }, - { "gssapikeyexchange", sGssKeyEx, SSHCFG_GLOBAL }, - { "gssapistoredelegatedcredentials", sGssStoreDelegCreds, SSHCFG_GLOBAL }, - { "gssauthentication", sGssAuthentication, SSHCFG_GLOBAL }, /* alias */ - { "gsskeyex", sGssKeyEx, SSHCFG_GLOBAL }, /* alias */ - { "gssstoredelegcreds", sGssStoreDelegCreds, SSHCFG_GLOBAL }, /* alias */ -#ifndef SUNW_GSSAPI - { "gssusesessionccache", sGssUseSessionCredCache, SSHCFG_GLOBAL }, - { "gssusesessioncredcache", sGssUseSessionCredCache, SSHCFG_GLOBAL }, - { "gsscleanupcreds", sGssCleanupCreds, SSHCFG_GLOBAL }, -#endif /* SUNW_GSSAPI */ -#endif -#if defined(KRB4) || defined(KRB5) - { "kerberosauthentication", sKerberosAuthentication, SSHCFG_ALL }, - { "kerberosorlocalpasswd", sKerberosOrLocalPasswd, SSHCFG_GLOBAL }, - { "kerberosticketcleanup", sKerberosTicketCleanup, SSHCFG_GLOBAL }, -#endif -#if defined(AFS) || defined(KRB5) - { "kerberostgtpassing", sKerberosTgtPassing, SSHCFG_GLOBAL }, -#endif -#ifdef AFS - { "afstokenpassing", sAFSTokenPassing, SSHCFG_GLOBAL }, -#endif - { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL }, - { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, - { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, - { "skeyauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, /* alias */ - { "checkmail", sDeprecated, SSHCFG_GLOBAL }, - { "listenaddress", sListenAddress, SSHCFG_GLOBAL }, - { "printmotd", sPrintMotd, SSHCFG_GLOBAL }, - { "printlastlog", sPrintLastLog, SSHCFG_GLOBAL }, - { "ignorerhosts", sIgnoreRhosts, SSHCFG_GLOBAL }, - { "ignoreuserknownhosts", sIgnoreUserKnownHosts, SSHCFG_GLOBAL }, - { "x11forwarding", sX11Forwarding, SSHCFG_ALL }, - { "x11displayoffset", sX11DisplayOffset, SSHCFG_ALL }, - { "x11uselocalhost", sX11UseLocalhost, SSHCFG_ALL }, - { "xauthlocation", sXAuthLocation, SSHCFG_GLOBAL }, - { "strictmodes", sStrictModes, SSHCFG_GLOBAL }, - { "permitemptypasswords", sEmptyPasswd, SSHCFG_ALL }, - { "permituserenvironment", sPermitUserEnvironment, SSHCFG_GLOBAL }, - { "uselogin", sUseLogin, SSHCFG_GLOBAL }, - { "compression", sCompression, SSHCFG_GLOBAL }, - { "tcpkeepalive", sKeepAlives, SSHCFG_GLOBAL }, - { "keepalive", sKeepAlives, SSHCFG_GLOBAL }, /* obsolete */ - { "allowtcpforwarding", sAllowTcpForwarding, SSHCFG_ALL }, - { "allowusers", sAllowUsers, SSHCFG_GLOBAL }, - { "denyusers", sDenyUsers, SSHCFG_GLOBAL }, - { "allowgroups", sAllowGroups, SSHCFG_GLOBAL }, - { "denygroups", sDenyGroups, SSHCFG_GLOBAL }, - { "ciphers", sCiphers, SSHCFG_GLOBAL }, - { "macs", sMacs, SSHCFG_GLOBAL}, - { "protocol", sProtocol,SSHCFG_GLOBAL }, - { "gatewayports", sGatewayPorts, SSHCFG_ALL }, - { "subsystem", sSubsystem, SSHCFG_GLOBAL}, - { "maxstartups", sMaxStartups, SSHCFG_GLOBAL }, - { "banner", sBanner, SSHCFG_ALL }, - { "verifyreversemapping", sVerifyReverseMapping, SSHCFG_GLOBAL }, - { "reversemappingcheck", sVerifyReverseMapping,SSHCFG_GLOBAL }, - { "clientaliveinterval", sClientAliveInterval, SSHCFG_GLOBAL }, - { "clientalivecountmax", sClientAliveCountMax, SSHCFG_GLOBAL }, - { "authorizedkeysfile", sAuthorizedKeysFile, SSHCFG_GLOBAL }, - { "authorizedkeysfile2", sAuthorizedKeysFile2, SSHCFG_GLOBAL }, - { "maxauthtries", sMaxAuthTries, SSHCFG_ALL }, - { "maxauthtrieslog", sMaxAuthTriesLog, SSHCFG_GLOBAL }, - { "useprivilegeseparation", sUsePrivilegeSeparation, SSHCFG_GLOBAL }, - { "lookupclienthostnames", sLookupClientHostnames, SSHCFG_GLOBAL }, - { "useopensslengine", sUseOpenSSLEngine, SSHCFG_GLOBAL }, - { "chrootdirectory", sChrootDirectory, SSHCFG_ALL }, - { "preuserauthhook", sPreUserauthHook, SSHCFG_ALL}, - { "match", sMatch, SSHCFG_ALL }, - { "pamserviceprefix", sPAMServicePrefix, SSHCFG_GLOBAL }, - { "pamservicename", sPAMServiceName, SSHCFG_GLOBAL }, - - { NULL, sBadOption, 0 } -}; - -/* - * Returns the number of the token pointed to by cp or sBadOption. - */ - -static ServerOpCodes -parse_token(const char *cp, const char *filename, - int linenum, u_int *flags) -{ - u_int i; - - for (i = 0; keywords[i].name; i++) - if (strcasecmp(cp, keywords[i].name) == 0) { - *flags = keywords[i].flags; - return keywords[i].opcode; - } - - error("%s: line %d: Bad configuration option: %s", - filename, linenum, cp); - return sBadOption; -} - -static void -add_listen_addr(ServerOptions *options, char *addr, u_short port) -{ - int i; - - if (options->num_ports == 0) - options->ports[options->num_ports++] = SSH_DEFAULT_PORT; - if (port == 0) - for (i = 0; i < options->num_ports; i++) - add_one_listen_addr(options, addr, options->ports[i]); - else - add_one_listen_addr(options, addr, port); -} - -static void -add_one_listen_addr(ServerOptions *options, char *addr, u_short port) -{ - struct addrinfo hints, *ai, *aitop; - char strport[NI_MAXSERV]; - int gaierr; - - (void) memset(&hints, 0, sizeof(hints)); - hints.ai_family = IPv4or6; - hints.ai_socktype = SOCK_STREAM; - hints.ai_flags = (addr == NULL) ? AI_PASSIVE : 0; - (void) snprintf(strport, sizeof strport, "%u", port); - if ((gaierr = getaddrinfo(addr, strport, &hints, &aitop)) != 0) - fatal("bad addr or host: %s (%s)", - addr ? addr : "<NULL>", - gai_strerror(gaierr)); - for (ai = aitop; ai->ai_next; ai = ai->ai_next) - ; - ai->ai_next = options->listen_addrs; - options->listen_addrs = aitop; -} - -/* - * The strategy for the Match blocks is that the config file is parsed twice. - * - * The first time is at startup. activep is initialized to 1 and the - * directives in the global context are processed and acted on. Hitting a - * Match directive unsets activep and the directives inside the block are - * checked for syntax only. - * - * The second time is after a connection has been established but before - * authentication. activep is initialized to 2 and global config directives - * are ignored since they have already been processed. If the criteria in a - * Match block is met, activep is set and the subsequent directives - * processed and actioned until EOF or another Match block unsets it. Any - * options set are copied into the main server config. - * - * Potential additions/improvements: - * - Add Match support for pre-kex directives, eg Protocol, Ciphers. - * - * - Add a Tag directive (idea from David Leonard) ala pf, eg: - * Match Address 192.168.0.* - * Tag trusted - * Match Group wheel - * Tag trusted - * Match Tag trusted - * AllowTcpForwarding yes - * GatewayPorts clientspecified - * [...] - * - * - Add a PermittedChannelRequests directive - * Match Group shell - * PermittedChannelRequests session,forwarded-tcpip - */ - -static int -match_cfg_line_group(const char *grps, int line, const char *user) -{ - int result = 0; - struct passwd *pw; - - if (user == NULL) - goto out; - - if ((pw = getpwnam(user)) == NULL) { - debug("Can't match group at line %d because user %.100s does " - "not exist", line, user); - } else if (ga_init(pw->pw_name, pw->pw_gid) == 0) { - debug("Can't Match group because user %.100s not in any group " - "at line %d", user, line); - } else if (ga_match_pattern_list(grps) != 1) { - debug("user %.100s does not match group list %.100s at line %d", - user, grps, line); - } else { - debug("user %.100s matched group list %.100s at line %d", user, - grps, line); - result = 1; - } -out: - ga_free(); - return result; -} - -static int -match_cfg_line(char **condition, int line, const char *user, const char *host, - const char *address) -{ - int result = 1; - char *arg, *attrib, *cp = *condition; - size_t len; - - if (user == NULL) - debug3("checking syntax for 'Match %s'", cp); - else - debug3("checking match for '%s' user %s host %s addr %s", cp, - user ? user : "(null)", host ? host : "(null)", - address ? address : "(null)"); - - while ((attrib = strdelim(&cp)) != NULL && *attrib != '\0') { - if ((arg = strdelim(&cp)) == NULL || *arg == '\0') { - error("Missing Match criteria for %s", attrib); - return -1; - } - len = strlen(arg); - if (strcasecmp(attrib, "user") == 0) { - if (!user) { - result = 0; - continue; - } - if (match_pattern_list(user, arg, len, 0) != 1) - result = 0; - else - debug("user %.100s matched 'User %.100s' at " - "line %d", user, arg, line); - } else if (strcasecmp(attrib, "group") == 0) { - switch (match_cfg_line_group(arg, line, user)) { - case -1: - return -1; - case 0: - result = 0; - } - } else if (strcasecmp(attrib, "host") == 0) { - if (!host) { - result = 0; - continue; - } - if (match_hostname(host, arg, len) != 1) - result = 0; - else - debug("connection from %.100s matched 'Host " - "%.100s' at line %d", host, arg, line); - } else if (strcasecmp(attrib, "address") == 0) { - switch (addr_match_list(address, arg)) { - case 1: - debug("connection from %.100s matched 'Address " - "%.100s' at line %d", address, arg, line); - break; - case 0: - case -1: - result = 0; - break; - case -2: - return -1; - } - } else { - error("Unsupported Match attribute %s", attrib); - return -1; - } - } - if (user != NULL) - debug3("match %sfound", result ? "" : "not "); - *condition = cp; - return result; -} - -#define WHITESPACE " \t\r\n" - -int -process_server_config_line(ServerOptions *options, char *line, - const char *filename, int linenum, int *activep, const char *user, - const char *host, const char *address) -{ - char *cp, **charptr, *arg, *p; - int cmdline = 0, *intptr, value, n; - ServerOpCodes opcode; - u_int i, flags = 0; - size_t len; - - cp = line; - arg = strdelim(&cp); - /* Ignore leading whitespace */ - if (*arg == '\0') - arg = strdelim(&cp); - if (!arg || !*arg || *arg == '#') - return 0; - intptr = NULL; - charptr = NULL; - opcode = parse_token(arg, filename, linenum, &flags); - - if (activep == NULL) { /* We are processing a command line directive */ - cmdline = 1; - activep = &cmdline; - } - if (*activep && opcode != sMatch) - debug3("%s:%d setting %s %s", filename, linenum, arg, cp); - if (*activep == 0 && !(flags & SSHCFG_MATCH)) { - if (user == NULL) { - fatal("%s line %d: Directive '%s' is not allowed " - "within a Match block", filename, linenum, arg); - } else { /* this is a directive we have already processed */ - while (arg) - arg = strdelim(&cp); - return 0; - } - } - - switch (opcode) { - /* Portable-specific options */ - case sPAMAuthenticationViaKbdInt: - log("%s line %d: PAMAuthenticationViaKbdInt has been " - "deprecated. You should use KbdInteractiveAuthentication " - "instead (which defaults to \"yes\").", filename, linenum); - intptr = &options->pam_authentication_via_kbd_int; - goto parse_flag; - - /* Standard Options */ - case sBadOption: - return -1; - case sPort: - /* ignore ports from configfile if cmdline specifies ports */ - if (options->ports_from_cmdline) - return 0; - if (options->listen_addrs != NULL) - fatal("%s line %d: ports must be specified before " - "ListenAddress.", filename, linenum); - if (options->num_ports >= MAX_PORTS) - fatal("%s line %d: too many ports.", - filename, linenum); - arg = strdelim(&cp); - if (!arg || *arg == '\0') - fatal("%s line %d: missing port number.", - filename, linenum); - options->ports[options->num_ports++] = a2port(arg); - if (options->ports[options->num_ports-1] == 0) - fatal("%s line %d: Badly formatted port number.", - filename, linenum); - break; - - case sServerKeyBits: - intptr = &options->server_key_bits; -parse_int: - arg = strdelim(&cp); - if (!arg || *arg == '\0') - fatal("%s line %d: missing integer value.", - filename, linenum); - value = atoi(arg); - if (*activep && *intptr == -1) - *intptr = value; - break; - - case sLoginGraceTime: - intptr = &options->login_grace_time; -parse_time: - arg = strdelim(&cp); - if (!arg || *arg == '\0') - fatal("%s line %d: missing time value.", - filename, linenum); - if ((value = convtime(arg)) == -1) - fatal("%s line %d: invalid time value.", - filename, linenum); - if (*intptr == -1) - *intptr = value; - break; - - case sKeyRegenerationTime: - intptr = &options->key_regeneration_time; - goto parse_time; - - case sListenAddress: - arg = strdelim(&cp); - if (!arg || *arg == '\0' || strncmp(arg, "[]", 2) == 0) - fatal("%s line %d: missing inet addr.", - filename, linenum); - if (*arg == '[') { - if ((p = strchr(arg, ']')) == NULL) - fatal("%s line %d: bad ipv6 inet addr usage.", - filename, linenum); - arg++; - (void) memmove(p, p+1, strlen(p+1)+1); - } else if (((p = strchr(arg, ':')) == NULL) || - (strchr(p+1, ':') != NULL)) { - add_listen_addr(options, arg, 0); - break; - } - if (*p == ':') { - u_short port; - - p++; - if (*p == '\0') - fatal("%s line %d: bad inet addr:port usage.", - filename, linenum); - else { - *(p-1) = '\0'; - if ((port = a2port(p)) == 0) - fatal("%s line %d: bad port number.", - filename, linenum); - add_listen_addr(options, arg, port); - } - } else if (*p == '\0') - add_listen_addr(options, arg, 0); - else - fatal("%s line %d: bad inet addr usage.", - filename, linenum); - break; - - case sHostKeyFile: - intptr = &options->num_host_key_files; - if (*intptr >= MAX_HOSTKEYS) - fatal("%s line %d: too many host keys specified (max %d).", - filename, linenum, MAX_HOSTKEYS); - charptr = &options->host_key_files[*intptr]; -parse_filename: - arg = strdelim(&cp); - if (!arg || *arg == '\0') - fatal("%s line %d: missing file name.", - filename, linenum); - if (*activep && *charptr == NULL) { - *charptr = tilde_expand_filename(arg, getuid()); - /* increase optional counter */ - if (intptr != NULL) - *intptr = *intptr + 1; - } - break; - - case sPidFile: - charptr = &options->pid_file; - goto parse_filename; - - case sPermitRootLogin: - intptr = &options->permit_root_login; - arg = strdelim(&cp); - if (!arg || *arg == '\0') - fatal("%s line %d: missing yes/" - "without-password/forced-commands-only/no " - "argument.", filename, linenum); - value = 0; /* silence compiler */ - if (strcmp(arg, "without-password") == 0) - value = PERMIT_NO_PASSWD; - else if (strcmp(arg, "forced-commands-only") == 0) - value = PERMIT_FORCED_ONLY; - else if (strcmp(arg, "yes") == 0) - value = PERMIT_YES; - else if (strcmp(arg, "no") == 0) - value = PERMIT_NO; - else - fatal("%s line %d: Bad yes/" - "without-password/forced-commands-only/no " - "argument: %s", filename, linenum, arg); - if (*activep && *intptr == -1) - *intptr = value; - break; - - case sIgnoreRhosts: - intptr = &options->ignore_rhosts; -parse_flag: - arg = strdelim(&cp); - if (!arg || *arg == '\0') - fatal("%s line %d: missing yes/no argument.", - filename, linenum); - value = 0; /* silence compiler */ - if (strcmp(arg, "yes") == 0) - value = 1; - else if (strcmp(arg, "no") == 0) - value = 0; - else - fatal("%s line %d: Bad yes/no argument: %s", - filename, linenum, arg); - if (*activep && *intptr == -1) - *intptr = value; - break; - - case sIgnoreUserKnownHosts: - intptr = &options->ignore_user_known_hosts; - goto parse_flag; - - case sRhostsAuthentication: - intptr = &options->rhosts_authentication; - goto parse_flag; - - case sRhostsRSAAuthentication: - intptr = &options->rhosts_rsa_authentication; - goto parse_flag; - - case sHostbasedAuthentication: - intptr = &options->hostbased_authentication; - goto parse_flag; - - case sHostbasedUsesNameFromPacketOnly: - intptr = &options->hostbased_uses_name_from_packet_only; - goto parse_flag; - - case sRSAAuthentication: - intptr = &options->rsa_authentication; - goto parse_flag; - - case sPubkeyAuthentication: - intptr = &options->pubkey_authentication; - goto parse_flag; -#ifdef GSSAPI - case sGssAuthentication: - intptr = &options->gss_authentication; - goto parse_flag; - case sGssKeyEx: - intptr = &options->gss_keyex; - goto parse_flag; - case sGssStoreDelegCreds: - intptr = &options->gss_keyex; - goto parse_flag; -#ifndef SUNW_GSSAPI - case sGssUseSessionCredCache: - intptr = &options->gss_use_session_ccache; - goto parse_flag; - case sGssCleanupCreds: - intptr = &options->gss_cleanup_creds; - goto parse_flag; -#endif /* SUNW_GSSAPI */ -#endif /* GSSAPI */ -#if defined(KRB4) || defined(KRB5) - case sKerberosAuthentication: - intptr = &options->kerberos_authentication; - goto parse_flag; - - case sKerberosOrLocalPasswd: - intptr = &options->kerberos_or_local_passwd; - goto parse_flag; - - case sKerberosTicketCleanup: - intptr = &options->kerberos_ticket_cleanup; - goto parse_flag; -#endif -#if defined(AFS) || defined(KRB5) - case sKerberosTgtPassing: - intptr = &options->kerberos_tgt_passing; - goto parse_flag; -#endif -#ifdef AFS - case sAFSTokenPassing: - intptr = &options->afs_token_passing; - goto parse_flag; -#endif - - case sPasswordAuthentication: - intptr = &options->password_authentication; - goto parse_flag; - - case sKbdInteractiveAuthentication: - intptr = &options->kbd_interactive_authentication; - goto parse_flag; - - case sChallengeResponseAuthentication: - intptr = &options->challenge_response_authentication; - goto parse_flag; - - case sPrintMotd: - intptr = &options->print_motd; - goto parse_flag; - - case sPrintLastLog: - intptr = &options->print_lastlog; - goto parse_flag; - - case sX11Forwarding: - intptr = &options->x11_forwarding; - goto parse_flag; - - case sX11DisplayOffset: - intptr = &options->x11_display_offset; - goto parse_int; - - case sX11UseLocalhost: - intptr = &options->x11_use_localhost; - goto parse_flag; - - case sXAuthLocation: - charptr = &options->xauth_location; - goto parse_filename; - - case sStrictModes: - intptr = &options->strict_modes; - goto parse_flag; - - case sKeepAlives: - intptr = &options->keepalives; - goto parse_flag; - - case sEmptyPasswd: - intptr = &options->permit_empty_passwd; - goto parse_flag; - - case sPermitUserEnvironment: - intptr = &options->permit_user_env; - goto parse_flag; - - case sUseLogin: - log("%s line %d: ignoring UseLogin option value." - " This option is always off.", filename, linenum); - while (arg) - arg = strdelim(&cp); - break; - - case sCompression: - intptr = &options->compression; - goto parse_flag; - - case sGatewayPorts: - intptr = &options->gateway_ports; - arg = strdelim(&cp); - if (!arg || *arg == '\0') - fatal("%s line %d: missing yes/no/clientspecified " - "argument.", filename, linenum); - value = 0; /* silence compiler */ - if (strcmp(arg, "clientspecified") == 0) - value = 2; - else if (strcmp(arg, "yes") == 0) - value = 1; - else if (strcmp(arg, "no") == 0) - value = 0; - else - fatal("%s line %d: Bad yes/no/clientspecified " - "argument: %s", filename, linenum, arg); - if (*activep && *intptr == -1) - *intptr = value; - break; - - case sVerifyReverseMapping: - intptr = &options->verify_reverse_mapping; - goto parse_flag; - - case sLogFacility: - intptr = (int *) &options->log_facility; - arg = strdelim(&cp); - value = log_facility_number(arg); - if (value == SYSLOG_FACILITY_NOT_SET) - fatal("%.200s line %d: unsupported log facility '%s'", - filename, linenum, arg ? arg : "<NONE>"); - if (*intptr == -1) - *intptr = (SyslogFacility) value; - break; - - case sLogLevel: - intptr = (int *) &options->log_level; - arg = strdelim(&cp); - value = log_level_number(arg); - if (value == SYSLOG_LEVEL_NOT_SET) - fatal("%.200s line %d: unsupported log level '%s'", - filename, linenum, arg ? arg : "<NONE>"); - if (*intptr == -1) - *intptr = (LogLevel) value; - break; - - case sAllowTcpForwarding: - intptr = &options->allow_tcp_forwarding; - goto parse_flag; - - case sUsePrivilegeSeparation: - log("%s line %d: ignoring UsePrivilegeSeparation option value." - " This option is always on.", filename, linenum); - while (arg) - arg = strdelim(&cp); - break; - - case sAllowUsers: - while (((arg = strdelim(&cp)) != NULL) && *arg != '\0') { - if (options->num_allow_users >= MAX_ALLOW_USERS) - fatal("%s line %d: too many allow users.", - filename, linenum); - options->allow_users[options->num_allow_users++] = - xstrdup(arg); - } - break; - - case sDenyUsers: - while (((arg = strdelim(&cp)) != NULL) && *arg != '\0') { - if (options->num_deny_users >= MAX_DENY_USERS) - fatal( "%s line %d: too many deny users.", - filename, linenum); - options->deny_users[options->num_deny_users++] = - xstrdup(arg); - } - break; - - case sAllowGroups: - while (((arg = strdelim(&cp)) != NULL) && *arg != '\0') { - if (options->num_allow_groups >= MAX_ALLOW_GROUPS) - fatal("%s line %d: too many allow groups.", - filename, linenum); - options->allow_groups[options->num_allow_groups++] = - xstrdup(arg); - } - break; - - case sDenyGroups: - while (((arg = strdelim(&cp)) != NULL) && *arg != '\0') { - if (options->num_deny_groups >= MAX_DENY_GROUPS) - fatal("%s line %d: too many deny groups.", - filename, linenum); - options->deny_groups[options->num_deny_groups++] = xstrdup(arg); - } - break; - - case sCiphers: - arg = strdelim(&cp); - if (!arg || *arg == '\0') - fatal("%s line %d: Missing argument.", filename, linenum); - if (!ciphers_valid(arg)) - fatal("%s line %d: Bad SSH2 cipher spec '%s'.", - filename, linenum, arg ? arg : "<NONE>"); - if (options->ciphers == NULL) - options->ciphers = xstrdup(arg); - break; - - case sMacs: - arg = strdelim(&cp); - if (!arg || *arg == '\0') - fatal("%s line %d: Missing argument.", filename, linenum); - if (!mac_valid(arg)) - fatal("%s line %d: Bad SSH2 mac spec '%s'.", - filename, linenum, arg ? arg : "<NONE>"); - if (options->macs == NULL) - options->macs = xstrdup(arg); - break; - - case sProtocol: - intptr = &options->protocol; - arg = strdelim(&cp); - if (!arg || *arg == '\0') - fatal("%s line %d: Missing argument.", filename, linenum); - value = proto_spec(arg); - if (value == SSH_PROTO_UNKNOWN) - fatal("%s line %d: Bad protocol spec '%s'.", - filename, linenum, arg ? arg : "<NONE>"); - if (*intptr == SSH_PROTO_UNKNOWN) - *intptr = value; - break; - - case sSubsystem: - if (options->num_subsystems >= MAX_SUBSYSTEMS) { - fatal("%s line %d: too many subsystems defined.", - filename, linenum); - } - arg = strdelim(&cp); - if (!arg || *arg == '\0') - fatal("%s line %d: Missing subsystem name.", - filename, linenum); - if (!*activep) { - arg = strdelim(&cp); - break; - } - for (i = 0; i < options->num_subsystems; i++) - if (strcmp(arg, options->subsystem_name[i]) == 0) - fatal("%s line %d: Subsystem '%s' already defined.", - filename, linenum, arg); - options->subsystem_name[options->num_subsystems] = xstrdup(arg); - arg = strdelim(&cp); - if (!arg || *arg == '\0') - fatal("%s line %d: Missing subsystem command.", - filename, linenum); - options->subsystem_command[options->num_subsystems] = xstrdup(arg); - - /* - * Collect arguments (separate to executable), including the - * name of the executable, in a way that is easier to parse - * later. - */ - p = xstrdup(arg); - len = strlen(p) + 1; - while ((arg = strdelim(&cp)) != NULL && *arg != '\0') { - len += 1 + strlen(arg); - p = xrealloc(p, len); - strlcat(p, " ", len); - strlcat(p, arg, len); - } - options->subsystem_args[options->num_subsystems] = p; - options->num_subsystems++; - break; - - case sMaxStartups: - arg = strdelim(&cp); - if (!arg || *arg == '\0') - fatal("%s line %d: Missing MaxStartups spec.", - filename, linenum); - if ((n = sscanf(arg, "%d:%d:%d", - &options->max_startups_begin, - &options->max_startups_rate, - &options->max_startups)) == 3) { - if (options->max_startups_begin > - options->max_startups || - options->max_startups_rate > 100 || - options->max_startups_rate < 1) - fatal("%s line %d: Illegal MaxStartups spec.", - filename, linenum); - } else if (n != 1) - fatal("%s line %d: Illegal MaxStartups spec.", - filename, linenum); - else - options->max_startups = options->max_startups_begin; - break; - - case sBanner: - charptr = &options->banner; - goto parse_filename; - /* - * These options can contain %X options expanded at - * connect time, so that you can specify paths like: - * - * AuthorizedKeysFile /etc/ssh_keys/%u - */ - case sAuthorizedKeysFile: - case sAuthorizedKeysFile2: - charptr = (opcode == sAuthorizedKeysFile) ? - &options->authorized_keys_file : - &options->authorized_keys_file2; - goto parse_filename; - - case sClientAliveInterval: - intptr = &options->client_alive_interval; - goto parse_time; - - case sClientAliveCountMax: - intptr = &options->client_alive_count_max; - goto parse_int; - - case sMaxAuthTries: - intptr = &options->max_auth_tries; - goto parse_int; - - case sMaxAuthTriesLog: - intptr = &options->max_auth_tries_log; - goto parse_int; - - case sLookupClientHostnames: - intptr = &options->lookup_client_hostnames; - goto parse_flag; - - case sUseOpenSSLEngine: - intptr = &options->use_openssl_engine; - goto parse_flag; - - case sChrootDirectory: - charptr = &options->chroot_directory; - - arg = strdelim(&cp); - if (arg == NULL || *arg == '\0') - fatal("%s line %d: missing directory name for " - "ChrootDirectory.", filename, linenum); - if (*activep && *charptr == NULL) - *charptr = xstrdup(arg); - break; - - case sPreUserauthHook: - charptr = &options->pre_userauth_hook; - goto parse_filename; - - case sMatch: - if (cmdline) - fatal("Match directive not supported as a command-line " - "option"); - value = match_cfg_line(&cp, linenum, user, host, address); - if (value < 0) - fatal("%s line %d: Bad Match condition", filename, - linenum); - *activep = value; - break; - - case sDeprecated: - log("%s line %d: Deprecated option %s", - filename, linenum, arg); - while (arg) - arg = strdelim(&cp); - break; - - case sPAMServicePrefix: - arg = strdelim(&cp); - if (!arg || *arg == '\0') - fatal("%s line %d: Missing argument.", - filename, linenum); - if (options->pam_service_name != NULL) - fatal("%s line %d: PAMServiceName and PAMServicePrefix " - "are mutually exclusive.", filename, linenum); - if (options->pam_service_prefix == NULL) - options->pam_service_prefix = xstrdup(arg); - break; - - case sPAMServiceName: - arg = strdelim(&cp); - if (!arg || *arg == '\0') - fatal("%s line %d: Missing argument.", - filename, linenum); - if (options->pam_service_prefix != NULL) - fatal("%s line %d: PAMServiceName and PAMServicePrefix " - "are mutually exclusive.", filename, linenum); - if (options->pam_service_name == NULL) - options->pam_service_name = xstrdup(arg); - break; - - default: - fatal("%s line %d: Missing handler for opcode %s (%d)", - filename, linenum, arg, opcode); - } - if ((arg = strdelim(&cp)) != NULL && *arg != '\0') - fatal("%s line %d: garbage at end of line; \"%.200s\".", - filename, linenum, arg); - return 0; -} - - -/* Reads the server configuration file. */ - -void -load_server_config(const char *filename, Buffer *conf) -{ - char line[1024], *cp; - FILE *f; - - debug2("%s: filename %s", __func__, filename); - if ((f = fopen(filename, "r")) == NULL) { - perror(filename); - exit(1); - } - buffer_clear(conf); - while (fgets(line, sizeof(line), f)) { - /* - * Trim out comments and strip whitespace - * NB - preserve newlines, they are needed to reproduce - * line numbers later for error messages - */ - if ((cp = strchr(line, '#')) != NULL) - memcpy(cp, "\n", 2); - cp = line + strspn(line, " \t\r"); - - buffer_append(conf, cp, strlen(cp)); - } - buffer_append(conf, "\0", 1); - fclose(f); - debug2("%s: done config len = %d", __func__, buffer_len(conf)); -} - -void -parse_server_match_config(ServerOptions *options, const char *user, - const char *host, const char *address) -{ - ServerOptions mo; - - initialize_server_options(&mo); - parse_server_config(&mo, "reprocess config", &cfg, user, host, address); - copy_set_server_options(options, &mo, 0); -} - - - -/* Helper macros */ -#define M_CP_INTOPT(n) do {\ - if (src->n != -1) \ - dst->n = src->n; \ -} while (0) -#define M_CP_STROPT(n) do {\ - if (src->n != NULL) { \ - if (dst->n != NULL) \ - xfree(dst->n); \ - dst->n = src->n; \ - } \ -} while(0) - -/* - * Copy any supported values that are set. - * - * If the preauth flag is set, we do not bother copying the the string or - * array values that are not used pre-authentication, because any that we - * do use must be explictly sent in mm_getpwnamallow(). - */ -void -copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth) -{ - M_CP_INTOPT(password_authentication); - M_CP_INTOPT(gss_authentication); - M_CP_INTOPT(rsa_authentication); - M_CP_INTOPT(pubkey_authentication); - M_CP_INTOPT(hostbased_authentication); - M_CP_INTOPT(kbd_interactive_authentication); - M_CP_INTOPT(permit_root_login); - M_CP_INTOPT(permit_empty_passwd); - M_CP_INTOPT(allow_tcp_forwarding); - M_CP_INTOPT(gateway_ports); - M_CP_INTOPT(x11_display_offset); - M_CP_INTOPT(x11_forwarding); - M_CP_INTOPT(x11_use_localhost); - M_CP_INTOPT(max_auth_tries); - M_CP_STROPT(banner); - - if (preauth) - return; - M_CP_STROPT(chroot_directory); -} - -#undef M_CP_INTOPT -#undef M_CP_STROPT - -void -parse_server_config(ServerOptions *options, const char *filename, Buffer *conf, - const char *user, const char *host, const char *address) -{ - int active, linenum, bad_options = 0; - char *cp, *obuf, *cbuf; - - debug2("%s: config %s len %d", __func__, filename, buffer_len(conf)); - - obuf = cbuf = xstrdup(buffer_ptr(conf)); - active = user ? 0 : 1; - linenum = 1; - while ((cp = strsep(&cbuf, "\n")) != NULL) { - if (process_server_config_line(options, cp, filename, - linenum++, &active, user, host, address) != 0) - bad_options++; - } - xfree(obuf); - if (bad_options > 0) - fatal("%s: terminating, %d bad configuration options", - filename, bad_options); -} - - -/* - * Note that "none" is a special path having the same affect on sshd - * configuration as not specifying ChrootDirectory at all. - */ -int -chroot_requested(char *chroot_directory) -{ - return (chroot_directory != NULL && - strcasecmp(chroot_directory, "none") != 0); -} diff --git a/usr/src/cmd/ssh/sshd/serverloop.c b/usr/src/cmd/ssh/sshd/serverloop.c deleted file mode 100644 index 3dae9b1840..0000000000 --- a/usr/src/cmd/ssh/sshd/serverloop.c +++ /dev/null @@ -1,1310 +0,0 @@ -/* - * Author: Tatu Ylonen <ylo@cs.hut.fi> - * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland - * All rights reserved - * Server main loop for handling the interactive session. - * - * As far as I am concerned, the code I have written for this software - * can be used freely for any purpose. Any derived versions of this - * software must be clearly marked as such, and if the derived work is - * incompatible with the protocol description in the RFC file, it must be - * called by a name other than "ssh" or "Secure Shell". - * - * SSH2 support by Markus Friedl. - * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -/* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - -#include "includes.h" -RCSID("$OpenBSD: serverloop.c,v 1.104 2002/09/19 16:03:15 stevesk Exp $"); - -#include "xmalloc.h" -#include "packet.h" -#include "buffer.h" -#include "log.h" -#include "servconf.h" -#include "canohost.h" -#include "sshpty.h" -#include "channels.h" -#include "compat.h" -#include "ssh1.h" -#include "ssh2.h" -#include "auth.h" -#include "session.h" -#include "dispatch.h" -#include "auth-options.h" -#include "serverloop.h" -#include "misc.h" -#include "kex.h" - -#ifdef ALTPRIVSEP -#include "altprivsep.h" -#endif /* ALTPRIVSEP*/ - -extern ServerOptions options; - -/* XXX */ -extern Kex *xxx_kex; -static Authctxt *xxx_authctxt; - -static Buffer stdin_buffer; /* Buffer for stdin data. */ -static Buffer stdout_buffer; /* Buffer for stdout data. */ -static Buffer stderr_buffer; /* Buffer for stderr data. */ -static int fdin; /* Descriptor for stdin (for writing) */ -static int fdout; /* Descriptor for stdout (for reading); - May be same number as fdin. */ -static int fderr; /* Descriptor for stderr. May be -1. */ -static long stdin_bytes = 0; /* Number of bytes written to stdin. */ -static long stdout_bytes = 0; /* Number of stdout bytes sent to client. */ -static long stderr_bytes = 0; /* Number of stderr bytes sent to client. */ -static long fdout_bytes = 0; /* Number of stdout bytes read from program. */ -static int stdin_eof = 0; /* EOF message received from client. */ -static int fdout_eof = 0; /* EOF encountered reading from fdout. */ -static int fderr_eof = 0; /* EOF encountered readung from fderr. */ -static int fdin_is_tty = 0; /* fdin points to a tty. */ -static int connection_in; /* Connection to client (input). */ -static int connection_out; /* Connection to client (output). */ -static int connection_closed = 0; /* Connection to client closed. */ -static u_int buffer_high; /* "Soft" max buffer size. */ -static int client_alive_timeouts = 0; - -/* - * This SIGCHLD kludge is used to detect when the child exits. The server - * will exit after that, as soon as forwarded connections have terminated. - */ - -static volatile sig_atomic_t child_terminated = 0; /* The child has terminated. */ - -/* prototypes */ -static void server_init_dispatch(void); - -/* - * we write to this pipe if a SIGCHLD is caught in order to avoid - * the race between select() and child_terminated - */ -static int notify_pipe[2]; -static void -notify_setup(void) -{ - if (pipe(notify_pipe) < 0) { - error("pipe(notify_pipe) failed %s", strerror(errno)); - } else if ((fcntl(notify_pipe[0], F_SETFD, FD_CLOEXEC) == -1) || - (fcntl(notify_pipe[1], F_SETFD, FD_CLOEXEC) == -1)) { - error("fcntl(notify_pipe, F_SETFD) failed %s", strerror(errno)); - (void) close(notify_pipe[0]); - (void) close(notify_pipe[1]); - } else { - set_nonblock(notify_pipe[0]); - set_nonblock(notify_pipe[1]); - return; - } - notify_pipe[0] = -1; /* read end */ - notify_pipe[1] = -1; /* write end */ -} -static void -notify_parent(void) -{ - if (notify_pipe[1] != -1) - (void) write(notify_pipe[1], "", 1); -} -static void -notify_prepare(fd_set *readset) -{ - if (notify_pipe[0] != -1) - FD_SET(notify_pipe[0], readset); -} -static void -notify_done(fd_set *readset) -{ - char c; - - if (notify_pipe[0] != -1 && FD_ISSET(notify_pipe[0], readset)) - while (read(notify_pipe[0], &c, 1) != -1) - debug2("notify_done: reading"); -} - -static void -sigchld_handler(int sig) -{ - int save_errno = errno; - debug("Received SIGCHLD."); - child_terminated = 1; -#ifndef _UNICOS - mysignal(SIGCHLD, sigchld_handler); -#endif - notify_parent(); - errno = save_errno; -} - -/* - * Make packets from buffered stderr data, and buffer it for sending - * to the client. - */ -static void -make_packets_from_stderr_data(void) -{ - int len; - - /* Send buffered stderr data to the client. */ - while (buffer_len(&stderr_buffer) > 0 && - packet_not_very_much_data_to_write()) { - len = buffer_len(&stderr_buffer); - if (packet_is_interactive()) { - if (len > 512) - len = 512; - } else { - /* Keep the packets at reasonable size. */ - if (len > packet_get_maxsize()) - len = packet_get_maxsize(); - } - packet_start(SSH_SMSG_STDERR_DATA); - packet_put_string(buffer_ptr(&stderr_buffer), len); - packet_send(); - buffer_consume(&stderr_buffer, len); - stderr_bytes += len; - } -} - -/* - * Make packets from buffered stdout data, and buffer it for sending to the - * client. - */ -static void -make_packets_from_stdout_data(void) -{ - int len; - - /* Send buffered stdout data to the client. */ - while (buffer_len(&stdout_buffer) > 0 && - packet_not_very_much_data_to_write()) { - len = buffer_len(&stdout_buffer); - if (packet_is_interactive()) { - if (len > 512) - len = 512; - } else { - /* Keep the packets at reasonable size. */ - if (len > packet_get_maxsize()) - len = packet_get_maxsize(); - } - packet_start(SSH_SMSG_STDOUT_DATA); - packet_put_string(buffer_ptr(&stdout_buffer), len); - packet_send(); - buffer_consume(&stdout_buffer, len); - stdout_bytes += len; - } -} - -static void -client_alive_check(void) -{ - static int had_channel = 0; - int id; - - id = channel_find_open(); - if (id == -1) { - if (!had_channel) - return; - packet_disconnect("No open channels after timeout!"); - } - had_channel = 1; - - /* timeout, check to see how many we have had */ - if (++client_alive_timeouts > options.client_alive_count_max) - packet_disconnect("Timeout, your session not responding."); - - /* - * send a bogus channel request with "wantreply", - * we should get back a failure - */ - channel_request_start(id, "keepalive@openssh.com", 1); - packet_send(); -} - -/* - * Sleep in select() until we can do something. This will initialize the - * select masks. Upon return, the masks will indicate which descriptors - * have data or can accept data. Optionally, a maximum time can be specified - * for the duration of the wait (0 = infinite). - */ -static void -wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, int *maxfdp, - int *nallocp, u_int max_time_milliseconds) -{ - struct timeval tv, *tvp; - int ret; - int client_alive_scheduled = 0; - - /* - * if using client_alive, set the max timeout accordingly, - * and indicate that this particular timeout was for client - * alive by setting the client_alive_scheduled flag. - * - * this could be randomized somewhat to make traffic - * analysis more difficult, but we're not doing it yet. - */ - if (compat20 && - max_time_milliseconds == 0 && options.client_alive_interval) { - client_alive_scheduled = 1; - max_time_milliseconds = options.client_alive_interval * 1000; - } - - /* Allocate and update select() masks for channel descriptors. */ - channel_prepare_select(readsetp, writesetp, maxfdp, nallocp, 0); - - if (compat20) { -#ifdef ALTPRIVSEP - int pipe_fd; - - if ((pipe_fd = altprivsep_get_pipe_fd()) != -1) { - *maxfdp = MAX(*maxfdp, pipe_fd); - FD_SET(altprivsep_get_pipe_fd(), *readsetp); - } -#endif /* ALTPRIVSEP */ -#if 0 - /* wrong: bad condition XXX */ - if (channel_not_very_much_buffered_data()) -#endif - FD_SET(connection_in, *readsetp); - } else { - /* - * Read packets from the client unless we have too much - * buffered stdin or channel data. - */ - if (buffer_len(&stdin_buffer) < buffer_high && - channel_not_very_much_buffered_data()) - FD_SET(connection_in, *readsetp); - /* - * If there is not too much data already buffered going to - * the client, try to get some more data from the program. - */ - if (packet_not_very_much_data_to_write()) { - if (!fdout_eof) - FD_SET(fdout, *readsetp); - if (!fderr_eof) - FD_SET(fderr, *readsetp); - } - /* - * If we have buffered data, try to write some of that data - * to the program. - */ - if (fdin != -1 && buffer_len(&stdin_buffer) > 0) - FD_SET(fdin, *writesetp); - } - notify_prepare(*readsetp); - - /* - * If we have buffered packet data going to the client, mark that - * descriptor. - */ - if (packet_have_data_to_write()) - FD_SET(connection_out, *writesetp); - - /* - * If child has terminated and there is enough buffer space to read - * from it, then read as much as is available and exit. - */ - if (child_terminated && packet_not_very_much_data_to_write()) - if (max_time_milliseconds == 0 || client_alive_scheduled) - max_time_milliseconds = 100; - - if (max_time_milliseconds == 0) - tvp = NULL; - else { - tv.tv_sec = max_time_milliseconds / 1000; - tv.tv_usec = 1000 * (max_time_milliseconds % 1000); - tvp = &tv; - } - - /* Wait for something to happen, or the timeout to expire. */ - ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp); - - if (ret == -1) { - memset(*readsetp, 0, *nallocp); - memset(*writesetp, 0, *nallocp); - if (errno != EINTR) - error("select: %.100s", strerror(errno)); - } else if (ret == 0 && client_alive_scheduled) - client_alive_check(); - - notify_done(*readsetp); -} - -/* - * Processes input from the client and the program. Input data is stored - * in buffers and processed later. - */ -static void -process_input(fd_set * readset) -{ - int len; - char buf[16384]; - - /* Read and buffer any input data from the client. */ - if (FD_ISSET(connection_in, readset)) { - len = read(connection_in, buf, sizeof(buf)); - if (len == 0) { - if (packet_is_monitor()) { - debug("child closed the communication pipe"); - } else { - verbose("Connection closed by %.100s", - get_remote_ipaddr()); - } - connection_closed = 1; - if (compat20) - return; - fatal_cleanup(); - } else if (len < 0) { - if (errno != EINTR && errno != EAGAIN) { - verbose("Read error from remote host " - "%.100s: %.100s", - get_remote_ipaddr(), strerror(errno)); - fatal_cleanup(); - } - } else { - /* Buffer any received data. */ - packet_process_incoming(buf, len); - } - } - if (compat20) - return; - - /* Read and buffer any available stdout data from the program. */ - if (!fdout_eof && FD_ISSET(fdout, readset)) { - len = read(fdout, buf, sizeof(buf)); - if (len < 0 && (errno == EINTR || errno == EAGAIN)) { - /* EMPTY */ - } else if (len <= 0) { - fdout_eof = 1; - } else { - buffer_append(&stdout_buffer, buf, len); - fdout_bytes += len; - } - } - /* Read and buffer any available stderr data from the program. */ - if (!fderr_eof && FD_ISSET(fderr, readset)) { - len = read(fderr, buf, sizeof(buf)); - if (len < 0 && (errno == EINTR || errno == EAGAIN)) { - /* EMPTY */ - } else if (len <= 0) { - fderr_eof = 1; - } else { - buffer_append(&stderr_buffer, buf, len); - } - } -} - -/* - * Sends data from internal buffers to client program stdin. - */ -static void -process_output(fd_set * writeset) -{ - struct termios tio; - u_char *data; - u_int dlen; - int len; - - /* Write buffered data to program stdin. */ - if (!compat20 && fdin != -1 && FD_ISSET(fdin, writeset)) { - data = buffer_ptr(&stdin_buffer); - dlen = buffer_len(&stdin_buffer); - len = write(fdin, data, dlen); - if (len < 0 && (errno == EINTR || errno == EAGAIN)) { - /* EMPTY */ - } else if (len <= 0) { - if (fdin != fdout) - (void) close(fdin); - else - (void) shutdown(fdin, SHUT_WR); /* We will no longer send. */ - fdin = -1; - } else { - /* Successful write. */ - if (fdin_is_tty && dlen >= 1 && data[0] != '\r' && - tcgetattr(fdin, &tio) == 0 && - !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) { - /* - * Simulate echo to reduce the impact of - * traffic analysis - */ - packet_send_ignore(len); - packet_send(); - } - /* Consume the data from the buffer. */ - buffer_consume(&stdin_buffer, len); - /* Update the count of bytes written to the program. */ - stdin_bytes += len; - } - } - /* Send any buffered packet data to the client. */ - if (FD_ISSET(connection_out, writeset)) - packet_write_poll(); -} - -/* - * Wait until all buffered output has been sent to the client. - * This is used when the program terminates. - */ -static void -drain_output(void) -{ - /* Send any buffered stdout data to the client. */ - if (buffer_len(&stdout_buffer) > 0) { - packet_start(SSH_SMSG_STDOUT_DATA); - packet_put_string(buffer_ptr(&stdout_buffer), - buffer_len(&stdout_buffer)); - packet_send(); - /* Update the count of sent bytes. */ - stdout_bytes += buffer_len(&stdout_buffer); - } - /* Send any buffered stderr data to the client. */ - if (buffer_len(&stderr_buffer) > 0) { - packet_start(SSH_SMSG_STDERR_DATA); - packet_put_string(buffer_ptr(&stderr_buffer), - buffer_len(&stderr_buffer)); - packet_send(); - /* Update the count of sent bytes. */ - stderr_bytes += buffer_len(&stderr_buffer); - } - /* Wait until all buffered data has been written to the client. */ - packet_write_wait(); -} - -static void -process_buffered_input_packets(void) -{ - dispatch_run(DISPATCH_NONBLOCK, NULL, compat20 ? xxx_kex : NULL); -} - -/* - * Performs the interactive session. This handles data transmission between - * the client and the program. Note that the notion of stdin, stdout, and - * stderr in this function is sort of reversed: this function writes to - * stdin (of the child program), and reads from stdout and stderr (of the - * child program). - */ -void -server_loop(pid_t pid, int fdin_arg, int fdout_arg, int fderr_arg) -{ - fd_set *readset = NULL, *writeset = NULL; - int max_fd = 0, nalloc = 0; - int wait_status; /* Status returned by wait(). */ - pid_t wait_pid; /* pid returned by wait(). */ - int waiting_termination = 0; /* Have displayed waiting close message. */ - u_int max_time_milliseconds; - u_int previous_stdout_buffer_bytes; - u_int stdout_buffer_bytes; - int type; - - debug("Entering interactive session."); - - /* Initialize the SIGCHLD kludge. */ - child_terminated = 0; - mysignal(SIGCHLD, sigchld_handler); - - /* Initialize our global variables. */ - fdin = fdin_arg; - fdout = fdout_arg; - fderr = fderr_arg; - - /* nonblocking IO */ - set_nonblock(fdin); - set_nonblock(fdout); - /* we don't have stderr for interactive terminal sessions, see below */ - if (fderr != -1) - set_nonblock(fderr); - - if (!(datafellows & SSH_BUG_IGNOREMSG) && isatty(fdin)) - fdin_is_tty = 1; - - connection_in = packet_get_connection_in(); - connection_out = packet_get_connection_out(); - - notify_setup(); - - previous_stdout_buffer_bytes = 0; - - /* Set approximate I/O buffer size. */ - if (packet_is_interactive()) - buffer_high = 4096; - else - buffer_high = 64 * 1024; - -#if 0 - /* Initialize max_fd to the maximum of the known file descriptors. */ - max_fd = MAX(connection_in, connection_out); - max_fd = MAX(max_fd, fdin); - max_fd = MAX(max_fd, fdout); - if (fderr != -1) - max_fd = MAX(max_fd, fderr); -#endif - - /* Initialize Initialize buffers. */ - buffer_init(&stdin_buffer); - buffer_init(&stdout_buffer); - buffer_init(&stderr_buffer); - - /* - * If we have no separate fderr (which is the case when we have a pty - * - there we cannot make difference between data sent to stdout and - * stderr), indicate that we have seen an EOF from stderr. This way - * we don\'t need to check the descriptor everywhere. - */ - if (fderr == -1) - fderr_eof = 1; - - server_init_dispatch(); - - /* Main loop of the server for the interactive session mode. */ - for (;;) { - - /* Process buffered packets from the client. */ - process_buffered_input_packets(); - - /* - * If we have received eof, and there is no more pending - * input data, cause a real eof by closing fdin. - */ - if (stdin_eof && fdin != -1 && buffer_len(&stdin_buffer) == 0) { - if (fdin != fdout) - (void) close(fdin); - else - (void) shutdown(fdin, SHUT_WR); /* We will no longer send. */ - fdin = -1; - } - /* Make packets from buffered stderr data to send to the client. */ - make_packets_from_stderr_data(); - - /* - * Make packets from buffered stdout data to send to the - * client. If there is very little to send, this arranges to - * not send them now, but to wait a short while to see if we - * are getting more data. This is necessary, as some systems - * wake up readers from a pty after each separate character. - */ - max_time_milliseconds = 0; - stdout_buffer_bytes = buffer_len(&stdout_buffer); - if (stdout_buffer_bytes != 0 && stdout_buffer_bytes < 256 && - stdout_buffer_bytes != previous_stdout_buffer_bytes) { - /* try again after a while */ - max_time_milliseconds = 10; - } else { - /* Send it now. */ - make_packets_from_stdout_data(); - } - previous_stdout_buffer_bytes = buffer_len(&stdout_buffer); - - /* Send channel data to the client. */ - if (packet_not_very_much_data_to_write()) - channel_output_poll(); - - /* - * Bail out of the loop if the program has closed its output - * descriptors, and we have no more data to send to the - * client, and there is no pending buffered data. - */ - if (fdout_eof && fderr_eof && !packet_have_data_to_write() && - buffer_len(&stdout_buffer) == 0 && buffer_len(&stderr_buffer) == 0) { - if (!channel_still_open()) - break; - if (!waiting_termination) { - const char *s = "Waiting for forwarded connections to terminate...\r\n"; - char *cp; - waiting_termination = 1; - buffer_append(&stderr_buffer, s, strlen(s)); - - /* Display list of open channels. */ - cp = channel_open_message(); - buffer_append(&stderr_buffer, cp, strlen(cp)); - xfree(cp); - } - } - max_fd = MAX(connection_in, connection_out); - max_fd = MAX(max_fd, fdin); - max_fd = MAX(max_fd, fdout); - max_fd = MAX(max_fd, fderr); - max_fd = MAX(max_fd, notify_pipe[0]); - - /* Sleep in select() until we can do something. */ - wait_until_can_do_something(&readset, &writeset, &max_fd, - &nalloc, max_time_milliseconds); - - /* Process any channel events. */ - channel_after_select(readset, writeset); - - /* Process input from the client and from program stdout/stderr. */ - process_input(readset); - - /* Process output to the client and to program stdin. */ - process_output(writeset); - } - if (readset) - xfree(readset); - if (writeset) - xfree(writeset); - - /* Cleanup and termination code. */ - - /* Wait until all output has been sent to the client. */ - drain_output(); - - debug("End of interactive session; stdin %ld, stdout (read %ld, sent %ld), stderr %ld bytes.", - stdin_bytes, fdout_bytes, stdout_bytes, stderr_bytes); - - /* Free and clear the buffers. */ - buffer_free(&stdin_buffer); - buffer_free(&stdout_buffer); - buffer_free(&stderr_buffer); - - /* Close the file descriptors. */ - if (fdout != -1) - (void) close(fdout); - fdout = -1; - fdout_eof = 1; - if (fderr != -1) - (void) close(fderr); - fderr = -1; - fderr_eof = 1; - if (fdin != -1) - (void) close(fdin); - fdin = -1; - - channel_free_all(); - - /* We no longer want our SIGCHLD handler to be called. */ - mysignal(SIGCHLD, SIG_DFL); - - while ((wait_pid = waitpid(-1, &wait_status, 0)) < 0) - if (errno != EINTR) - packet_disconnect("wait: %.100s", strerror(errno)); - if (wait_pid != pid) - error("Strange, wait returned pid %ld, expected %ld", - (long)wait_pid, (long)pid); - - /* Check if it exited normally. */ - if (WIFEXITED(wait_status)) { - /* Yes, normal exit. Get exit status and send it to the client. */ - debug("Command exited with status %d.", WEXITSTATUS(wait_status)); - packet_start(SSH_SMSG_EXITSTATUS); - packet_put_int(WEXITSTATUS(wait_status)); - packet_send(); - packet_write_wait(); - - /* - * Wait for exit confirmation. Note that there might be - * other packets coming before it; however, the program has - * already died so we just ignore them. The client is - * supposed to respond with the confirmation when it receives - * the exit status. - */ - do { - type = packet_read(); - } - while (type != SSH_CMSG_EXIT_CONFIRMATION); - - debug("Received exit confirmation."); - return; - } - /* Check if the program terminated due to a signal. */ - if (WIFSIGNALED(wait_status)) - packet_disconnect("Command terminated on signal %d.", - WTERMSIG(wait_status)); - - /* Some weird exit cause. Just exit. */ - packet_disconnect("wait returned status %04x.", wait_status); - /* NOTREACHED */ -} - -static void -collect_children(void) -{ - pid_t pid; - sigset_t oset, nset; - int status; - - /* block SIGCHLD while we check for dead children */ - (void) sigemptyset(&nset); - (void) sigaddset(&nset, SIGCHLD); - (void) sigprocmask(SIG_BLOCK, &nset, &oset); - if (child_terminated) { - while ((pid = waitpid(-1, &status, WNOHANG)) > 0 || - (pid < 0 && errno == EINTR)) - if (pid > 0) - session_close_by_pid(pid, status); - child_terminated = 0; - } - (void) sigprocmask(SIG_SETMASK, &oset, NULL); -} - -#ifdef ALTPRIVSEP -/* - * For ALTPRIVSEP the wait_until_can_do_something function is very - * simple: select() on the read side of the pipe, and if there's packets - * to send, on the write side, and on the read side of the SIGCHLD - * handler pipe. That's it. - */ -static void -aps_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, - int *maxfdp, int *nallocp, u_int max_time_milliseconds) -{ - int ret; - - /* - * Use channel_prepare_select() to make the fd sets. - * - * This is cheating, really, since because the last argument in - * this call is '1' nothing related to channels will be done -- - * we're using this function only to callocate the fd sets. - */ - channel_prepare_select(readsetp, writesetp, maxfdp, nallocp, 1); - - if ((connection_in = packet_get_connection_in()) >= 0 && - !connection_closed) - FD_SET(connection_in, *readsetp); - - notify_prepare(*readsetp); - - if ((connection_out = packet_get_connection_out()) >= 0 && - packet_have_data_to_write() && !connection_closed) - FD_SET(connection_out, *writesetp); - - /* Wait for something to happen, or the timeout to expire. */ - ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, NULL); - - if (ret == -1) { - memset(*readsetp, 0, *nallocp); - memset(*writesetp, 0, *nallocp); - if (errno != EINTR) - error("select: %.100s", strerror(errno)); - } - - notify_done(*readsetp); -} - -/* - * Slightly different than collect_children, aps_collect_child() has - * only the unprivileged sshd to wait for, no sessions, no channells, - * just one process. - */ -static int -aps_collect_child(pid_t child) -{ - pid_t pid; - sigset_t oset, nset; - int status; - - /* block SIGCHLD while we check for dead children */ - (void) sigemptyset(&nset); - (void) sigaddset(&nset, SIGCHLD); - (void) sigprocmask(SIG_BLOCK, &nset, &oset); - if (child_terminated) { - while ((pid = waitpid(child, &status, WNOHANG)) > 0 || - (pid < 0 && errno == EINTR)) - if (pid == child) { - (void) sigprocmask(SIG_SETMASK, &oset, NULL); - return (1); - } - child_terminated = 0; - } - (void) sigprocmask(SIG_SETMASK, &oset, NULL); - return (0); -} - -static int killed = 0; - -static void -aps_monitor_kill_handler(int sig) -{ - int save_errno = errno; - killed = 1; - notify_parent(); - mysignal(sig, aps_monitor_kill_handler); - errno = save_errno; -} - -static void -aps_monitor_sigchld_handler(int sig) -{ - int save_errno = errno; - debug("Monitor received SIGCHLD."); - child_terminated = 1; - mysignal(SIGCHLD, aps_monitor_sigchld_handler); - notify_parent(); - errno = save_errno; -} - -void -aps_monitor_loop(Authctxt *authctxt, pid_t child_pid) -{ - fd_set *readset = NULL, *writeset = NULL; - int max_fd, nalloc = 0; - - debug("Entering monitor loop."); - - /* - * Awful hack follows: fake compat20 == 1 to cause process_input() - * and process_output() to behave as they would for SSHv2 because that's - * the behaviour we need in SSHv2. - * - * This same hack is done in packet.c - */ - compat20 = 1; /* causes process_input/output() to ignore stdio */ - - mysignal(SIGHUP, aps_monitor_kill_handler); - mysignal(SIGINT, aps_monitor_kill_handler); - mysignal(SIGTERM, aps_monitor_kill_handler); - - child_terminated = 0; - mysignal(SIGCHLD, aps_monitor_sigchld_handler); - - connection_in = packet_get_connection_in(); - connection_out = packet_get_connection_out(); - - notify_setup(); - - max_fd = MAX(connection_in, connection_out); - max_fd = MAX(max_fd, notify_pipe[0]); - - dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit); - dispatch_range(SSH2_MSG_USERAUTH_MIN, SSH2_MSG_MAX, - &dispatch_protocol_error); - dispatch_set(SSH2_PRIV_MSG_ALTPRIVSEP, &aps_input_altpriv_msg); - - for (;;) { - process_buffered_input_packets(); - - aps_wait_until_can_do_something(&readset, &writeset, &max_fd, - &nalloc, 0); - - if (aps_collect_child(child_pid)) - break; - - if (killed) { - /* fatal cleanups will kill child, audit logout */ - log("Monitor killed; exiting"); - fatal_cleanup(); - } - - /* - * Unlike server_loop2() we don't care if connection_closed - * since we still want to wait for the monitor's child. - */ - process_input(readset); - process_output(writeset); - } - - packet_close(); -} -#endif /* ALTPRIVSEP */ - -/* - * This server loop is for unprivileged child only. Our monitor runs its own - * aps_monitor_loop() funtion. - */ -void -server_loop2(Authctxt *authctxt) -{ - fd_set *readset = NULL, *writeset = NULL; - int rekeying = 0, max_fd, nalloc = 0; - - debug("Entering interactive session for SSH2."); - - mysignal(SIGCHLD, sigchld_handler); - child_terminated = 0; - connection_in = packet_get_connection_in(); - connection_out = packet_get_connection_out(); - - notify_setup(); - - max_fd = MAX(connection_in, connection_out); - max_fd = MAX(max_fd, notify_pipe[0]); - - xxx_authctxt = authctxt; - - server_init_dispatch(); - - for (;;) { - process_buffered_input_packets(); - - rekeying = (xxx_kex != NULL && !xxx_kex->done); - - if (!rekeying && packet_not_very_much_data_to_write()) - channel_output_poll(); - wait_until_can_do_something(&readset, &writeset, &max_fd, - &nalloc, 0); - - collect_children(); - - if (!rekeying) { - channel_after_select(readset, writeset); - if (packet_need_rekeying()) { - debug("rekey limit reached, need rekeying"); - xxx_kex->done = 0; - debug("poking the monitor to start " - "key re-exchange"); - altprivsep_start_rekex(); - } - } -#ifdef ALTPRIVSEP - else - altprivsep_process_input(readset); -#endif /* ALTPRIVSEP */ - - process_input(readset); - if (connection_closed) - break; - process_output(writeset); - } - collect_children(); - - if (readset) - xfree(readset); - if (writeset) - xfree(writeset); - - /* free all channels, no more reads and writes */ - channel_free_all(); - - /* free remaining sessions, e.g. remove wtmp entries */ - session_destroy_all(NULL); -} - -static void -server_input_channel_failure(int type, u_int32_t seq, void *ctxt) -{ - debug("Got CHANNEL_FAILURE for keepalive"); - /* - * reset timeout, since we got a sane answer from the client. - * even if this was generated by something other than - * the bogus CHANNEL_REQUEST we send for keepalives. - */ - client_alive_timeouts = 0; -} - -static void -server_input_stdin_data(int type, u_int32_t seq, void *ctxt) -{ - char *data; - u_int data_len; - - /* Stdin data from the client. Append it to the buffer. */ - /* Ignore any data if the client has closed stdin. */ - if (fdin == -1) - return; - data = packet_get_string(&data_len); - packet_check_eom(); - buffer_append(&stdin_buffer, data, data_len); - memset(data, 0, data_len); - xfree(data); -} - -static void -server_input_eof(int type, u_int32_t seq, void *ctxt) -{ - /* - * Eof from the client. The stdin descriptor to the - * program will be closed when all buffered data has - * drained. - */ - debug("EOF received for stdin."); - packet_check_eom(); - stdin_eof = 1; -} - -static void -server_input_window_size(int type, u_int32_t seq, void *ctxt) -{ - int row = packet_get_int(); - int col = packet_get_int(); - int xpixel = packet_get_int(); - int ypixel = packet_get_int(); - - debug("Window change received."); - packet_check_eom(); - if (fdin != -1) - pty_change_window_size(fdin, row, col, xpixel, ypixel); -} - -static Channel * -server_request_direct_tcpip(char *ctype) -{ - Channel *c; - int sock; - char *target, *originator; - int target_port, originator_port; - - target = packet_get_string(NULL); - target_port = packet_get_int(); - originator = packet_get_string(NULL); - originator_port = packet_get_int(); - packet_check_eom(); - - debug("server_request_direct_tcpip: originator %s port %d, target %s port %d", - originator, originator_port, target, target_port); - - /* XXX check permission */ - sock = channel_connect_to(target, target_port); - - xfree(target); - xfree(originator); - if (sock < 0) - return NULL; - c = channel_new(ctype, SSH_CHANNEL_CONNECTING, - sock, sock, -1, CHAN_TCP_WINDOW_DEFAULT, - CHAN_TCP_PACKET_DEFAULT, 0, xstrdup("direct-tcpip"), 1); - return c; -} - -static Channel * -server_request_session(char *ctype) -{ - Channel *c; - - debug("input_session_request"); - packet_check_eom(); - /* - * A server session has no fd to read or write until a - * CHANNEL_REQUEST for a shell is made, so we set the type to - * SSH_CHANNEL_LARVAL. Additionally, a callback for handling all - * CHANNEL_REQUEST messages is registered. - */ - c = channel_new(ctype, SSH_CHANNEL_LARVAL, - -1, -1, -1, /*window size*/0, CHAN_SES_PACKET_DEFAULT, - 0, xstrdup("server-session"), 1); - if (session_open(xxx_authctxt, c->self) != 1) { - debug("session open failed, free channel %d", c->self); - channel_free(c); - return NULL; - } - channel_register_cleanup(c->self, session_close_by_channel); - return c; -} - -static void -server_input_channel_open(int type, u_int32_t seq, void *ctxt) -{ - Channel *c = NULL; - char *ctype; - int rchan; - u_int rmaxpack, rwindow, len; - - ctype = packet_get_string(&len); - rchan = packet_get_int(); - rwindow = packet_get_int(); - rmaxpack = packet_get_int(); - - debug("server_input_channel_open: ctype %s rchan %d win %d max %d", - ctype, rchan, rwindow, rmaxpack); - - if (strcmp(ctype, "session") == 0) { - c = server_request_session(ctype); - } else if (strcmp(ctype, "direct-tcpip") == 0) { - c = server_request_direct_tcpip(ctype); - } - if (c != NULL) { - debug("server_input_channel_open: confirm %s", ctype); - c->remote_id = rchan; - c->remote_window = rwindow; - c->remote_maxpacket = rmaxpack; - if (c->type != SSH_CHANNEL_CONNECTING) { - packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); - packet_put_int(c->remote_id); - packet_put_int(c->self); - packet_put_int(c->local_window); - packet_put_int(c->local_maxpacket); - packet_send(); - } - } else { - debug("server_input_channel_open: failure %s", ctype); - packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); - packet_put_int(rchan); - packet_put_int(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED); - if (!(datafellows & SSH_BUG_OPENFAILURE)) { - packet_put_utf8_cstring("open failed"); - packet_put_cstring(""); - } - packet_send(); - } - xfree(ctype); -} - -static void -server_input_global_request(int type, u_int32_t seq, void *ctxt) -{ - char *rtype; - int want_reply; - int success = 0; - - rtype = packet_get_string(NULL); - want_reply = packet_get_char(); - debug("server_input_global_request: rtype %s want_reply %d", rtype, want_reply); - - /* -R style forwarding */ - if (strcmp(rtype, "tcpip-forward") == 0) { - struct passwd *pw; - char *listen_address; - u_short listen_port; - - pw = auth_get_user(); - if (pw == NULL) - fatal("server_input_global_request: no user"); - listen_address = packet_get_string(NULL); /* XXX currently ignored */ - listen_port = (u_short)packet_get_int(); - debug("server_input_global_request: tcpip-forward listen %s port %d", - listen_address, listen_port); - - /* check permissions */ - if (!options.allow_tcp_forwarding || - no_port_forwarding_flag -#ifndef NO_IPPORT_RESERVED_CONCEPT - || (listen_port < IPPORT_RESERVED && pw->pw_uid != 0) -#endif - ) { - success = 0; - packet_send_debug("Server has disabled port forwarding."); - } else { - /* Start listening on the port */ - success = channel_setup_remote_fwd_listener( - listen_address, listen_port, options.gateway_ports); - } - xfree(listen_address); - } else if (strcmp(rtype, "cancel-tcpip-forward") == 0) { - char *cancel_address; - u_short cancel_port; - - cancel_address = packet_get_string(NULL); - cancel_port = (u_short)packet_get_int(); - debug("%s: cancel-tcpip-forward addr %s port %d", __func__, - cancel_address, cancel_port); - - success = channel_cancel_rport_listener(cancel_address, - cancel_port); - xfree(cancel_address); - } - if (want_reply) { - packet_start(success ? - SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE); - packet_send(); - packet_write_wait(); - } - xfree(rtype); -} - -static void -server_input_channel_req(int type, u_int32_t seq, void *ctxt) -{ - Channel *c; - int id, reply, success = 0; - char *rtype; - - id = packet_get_int(); - rtype = packet_get_string(NULL); - reply = packet_get_char(); - - debug("server_input_channel_req: channel %d request %s reply %d", - id, rtype, reply); - - if ((c = channel_lookup(id)) == NULL) - packet_disconnect("server_input_channel_req: " - "unknown channel %d", id); - if (!strcmp(rtype, "eow@openssh.com")) { - packet_check_eom(); - chan_rcvd_eow(c); - } else if (c->type == SSH_CHANNEL_LARVAL || c->type == SSH_CHANNEL_OPEN) - success = session_input_channel_req(c, rtype); - if (reply) { - packet_start(success ? - SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE); - packet_put_int(c->remote_id); - packet_send(); - } - xfree(rtype); -} - -static void -server_init_dispatch_20(void) -{ - debug("server_init_dispatch_20"); - dispatch_init(&dispatch_protocol_error); - dispatch_set(SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose); - dispatch_set(SSH2_MSG_CHANNEL_DATA, &channel_input_data); - dispatch_set(SSH2_MSG_CHANNEL_EOF, &channel_input_ieof); - dispatch_set(SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data); - dispatch_set(SSH2_MSG_CHANNEL_OPEN, &server_input_channel_open); - dispatch_set(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); - dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); - dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &server_input_channel_req); - dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust); - dispatch_set(SSH2_MSG_GLOBAL_REQUEST, &server_input_global_request); - /* client_alive */ - dispatch_set(SSH2_MSG_CHANNEL_FAILURE, &server_input_channel_failure); - /* rekeying */ - -#ifdef ALTPRIVSEP - /* unprivileged sshd has a kex packet handler that must not be reset */ - debug3("server_init_dispatch_20 -- should we dispatch_set(KEXINIT) here? %d && !%d", - packet_is_server(), packet_is_monitor()); - if (packet_is_server() && !packet_is_monitor()) { - debug3("server_init_dispatch_20 -- skipping dispatch_set(KEXINIT) in unpriv proc"); - dispatch_range(SSH2_MSG_KEXINIT, SSH2_MSG_TRANSPORT_MAX, - &altprivsep_rekey); - return; - } -#endif /* ALTPRIVSEP */ - dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit); -} -static void -server_init_dispatch_13(void) -{ - debug("server_init_dispatch_13"); - dispatch_init(NULL); - dispatch_set(SSH_CMSG_EOF, &server_input_eof); - dispatch_set(SSH_CMSG_STDIN_DATA, &server_input_stdin_data); - dispatch_set(SSH_CMSG_WINDOW_SIZE, &server_input_window_size); - dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_close); - dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_close_confirmation); - dispatch_set(SSH_MSG_CHANNEL_DATA, &channel_input_data); - dispatch_set(SSH_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); - dispatch_set(SSH_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); - dispatch_set(SSH_MSG_PORT_OPEN, &channel_input_port_open); -} -static void -server_init_dispatch_15(void) -{ - server_init_dispatch_13(); - debug("server_init_dispatch_15"); - dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_ieof); - dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_oclose); -} -static void -server_init_dispatch(void) -{ - if (compat20) - server_init_dispatch_20(); - else if (compat13) - server_init_dispatch_13(); - else - server_init_dispatch_15(); -} diff --git a/usr/src/cmd/ssh/sshd/session.c b/usr/src/cmd/ssh/sshd/session.c deleted file mode 100644 index 871b06c758..0000000000 --- a/usr/src/cmd/ssh/sshd/session.c +++ /dev/null @@ -1,2641 +0,0 @@ -/* - * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland - * All rights reserved - * - * As far as I am concerned, the code I have written for this software - * can be used freely for any purpose. Any derived versions of this - * software must be clearly marked as such, and if the derived work is - * incompatible with the protocol description in the RFC file, it must be - * called by a name other than "ssh" or "Secure Shell". - * - * SSH2 support by Markus Friedl. - * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -/* - * Copyright 2010 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - -#include "includes.h" -RCSID("$OpenBSD: session.c,v 1.150 2002/09/16 19:55:33 stevesk Exp $"); - -#ifdef HAVE_DEFOPEN -#include <deflt.h> -#include <ulimit.h> -#endif /* HAVE_DEFOPEN */ - -#ifdef HAVE_LIBGEN_H -#include <libgen.h> -#endif - -#include <priv.h> - -#include "ssh.h" -#include "ssh1.h" -#include "ssh2.h" -#include "xmalloc.h" -#include "sshpty.h" -#include "packet.h" -#include "buffer.h" -#include "mpaux.h" -#include "uidswap.h" -#include "compat.h" -#include "channels.h" -#include "bufaux.h" -#include "auth.h" -#include "auth-options.h" -#include "pathnames.h" -#include "log.h" -#include "servconf.h" -#include "sshlogin.h" -#include "serverloop.h" -#include "canohost.h" -#include "session.h" -#include "tildexpand.h" -#include "misc.h" -#include "sftp.h" - -#ifdef USE_PAM -#include <security/pam_appl.h> -#endif /* USE_PAM */ - -#ifdef GSSAPI -#include "ssh-gss.h" -#endif - -#ifdef ALTPRIVSEP -#include "altprivsep.h" -#endif /* ALTPRIVSEP */ - -#ifdef HAVE_CYGWIN -#include <windows.h> -#include <sys/cygwin.h> -#define is_winnt (GetVersion() < 0x80000000) -#endif - -/* func */ - -Session *session_new(void); -void session_set_fds(Session *, int, int, int); -void session_pty_cleanup(void *); -void session_xauthfile_cleanup(void *s); -void session_proctitle(Session *); -int session_setup_x11fwd(Session *); -void do_exec_pty(Session *, const char *); -void do_exec_no_pty(Session *, const char *); -void do_exec(Session *, const char *); -void do_login(Session *, const char *); -void do_child(Session *, const char *); -void do_motd(void); -int check_quietlogin(Session *, const char *); - -static void do_authenticated1(Authctxt *); -static void do_authenticated2(Authctxt *); - -static int session_pty_req(Session *); -static int session_env_req(Session *s); -static void session_free_env(char ***envp); -static void safely_chroot(const char *path, uid_t uid); -static void drop_privs(uid_t uid); - -#ifdef USE_PAM -static void session_do_pam(Session *, int); -#endif /* USE_PAM */ - -/* import */ -extern ServerOptions options; -extern char *__progname; -extern int log_stderr; -extern int debug_flag; -extern u_int utmp_len; -extern void destroy_sensitive_data(void); - -#ifdef GSSAPI -extern Gssctxt *xxx_gssctxt; -#endif /* GSSAPI */ - -/* original command from peer. */ -const char *original_command = NULL; - -/* data */ -#define MAX_SESSIONS 10 -Session sessions[MAX_SESSIONS]; - -#define SUBSYSTEM_NONE 0 -#define SUBSYSTEM_EXT 1 -#define SUBSYSTEM_INT_SFTP 2 - -#ifdef HAVE_LOGIN_CAP -login_cap_t *lc; -#endif - -/* Name and directory of socket for authentication agent forwarding. */ -static char *auth_sock_name = NULL; -static char *auth_sock_dir = NULL; - -/* removes the agent forwarding socket */ - -static void -auth_sock_cleanup_proc(void *_pw) -{ - struct passwd *pw = _pw; - - if (auth_sock_name != NULL) { - temporarily_use_uid(pw); - unlink(auth_sock_name); - rmdir(auth_sock_dir); - auth_sock_name = NULL; - restore_uid(); - } -} - -static int -auth_input_request_forwarding(struct passwd * pw) -{ - Channel *nc; - int sock; - struct sockaddr_un sunaddr; - - if (auth_sock_name != NULL) { - error("authentication forwarding requested twice."); - return 0; - } - - /* Temporarily drop privileged uid for mkdir/bind. */ - temporarily_use_uid(pw); - - /* Allocate a buffer for the socket name, and format the name. */ - auth_sock_name = xmalloc(MAXPATHLEN); - auth_sock_dir = xmalloc(MAXPATHLEN); - strlcpy(auth_sock_dir, "/tmp/ssh-XXXXXXXX", MAXPATHLEN); - - /* Create private directory for socket */ - if (mkdtemp(auth_sock_dir) == NULL) { - packet_send_debug("Agent forwarding disabled: " - "mkdtemp() failed: %.100s", strerror(errno)); - restore_uid(); - xfree(auth_sock_name); - xfree(auth_sock_dir); - auth_sock_name = NULL; - auth_sock_dir = NULL; - return 0; - } - snprintf(auth_sock_name, MAXPATHLEN, "%s/agent.%ld", - auth_sock_dir, (long) getpid()); - - /* delete agent socket on fatal() */ - fatal_add_cleanup(auth_sock_cleanup_proc, pw); - - /* Create the socket. */ - sock = socket(AF_UNIX, SOCK_STREAM, 0); - if (sock < 0) - packet_disconnect("socket: %.100s", strerror(errno)); - - /* Bind it to the name. */ - memset(&sunaddr, 0, sizeof(sunaddr)); - sunaddr.sun_family = AF_UNIX; - strlcpy(sunaddr.sun_path, auth_sock_name, sizeof(sunaddr.sun_path)); - - if (bind(sock, (struct sockaddr *) & sunaddr, sizeof(sunaddr)) < 0) - packet_disconnect("bind: %.100s", strerror(errno)); - - /* Restore the privileged uid. */ - restore_uid(); - - /* Start listening on the socket. */ - if (listen(sock, 5) < 0) - packet_disconnect("listen: %.100s", strerror(errno)); - - /* Allocate a channel for the authentication agent socket. */ - nc = channel_new("auth socket", - SSH_CHANNEL_AUTH_SOCKET, sock, sock, -1, - CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, - 0, xstrdup("auth socket"), 1); - strlcpy(nc->path, auth_sock_name, sizeof(nc->path)); - return 1; -} - - -void -do_authenticated(Authctxt *authctxt) -{ - /* 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 agent socket */ - if (auth_sock_name != NULL) - auth_sock_cleanup_proc(authctxt->pw); -#ifdef KRB4 - if (options.kerberos_ticket_cleanup) - krb4_cleanup_proc(authctxt); -#endif -#ifdef KRB5 - if (options.kerberos_ticket_cleanup) - krb5_cleanup_proc(authctxt); -#endif -} - -/* - * Prepares for an interactive session. This is called after the user has - * been successfully authenticated. During this message exchange, pseudo - * terminals are allocated, X11, TCP/IP, and authentication agent forwardings - * are requested, etc. - */ -static void -do_authenticated1(Authctxt *authctxt) -{ - Session *s; - char *command; - int success, type, screen_flag; - int enable_compression_after_reply = 0; - u_int proto_len, data_len, dlen, compression_level = 0; - - s = session_new(); - s->authctxt = authctxt; - s->pw = authctxt->pw; - - /* - * We stay in this loop until the client requests to execute a shell - * or a command. - */ - for (;;) { - success = 0; - - /* Get a packet from the client. */ - type = packet_read(); - - /* Process the packet. */ - switch (type) { - case SSH_CMSG_REQUEST_COMPRESSION: - compression_level = packet_get_int(); - packet_check_eom(); - if (compression_level < 1 || compression_level > 9) { - packet_send_debug("Received illegal compression level %d.", - compression_level); - break; - } - if (!options.compression) { - debug2("compression disabled"); - break; - } - /* Enable compression after we have responded with SUCCESS. */ - enable_compression_after_reply = 1; - success = 1; - break; - - case SSH_CMSG_REQUEST_PTY: - success = session_pty_req(s); - break; - - case SSH_CMSG_X11_REQUEST_FORWARDING: - s->auth_proto = packet_get_string(&proto_len); - s->auth_data = packet_get_string(&data_len); - - screen_flag = packet_get_protocol_flags() & - SSH_PROTOFLAG_SCREEN_NUMBER; - debug2("SSH_PROTOFLAG_SCREEN_NUMBER: %d", screen_flag); - - if (packet_remaining() == 4) { - if (!screen_flag) - debug2("Buggy client: " - "X11 screen flag missing"); - s->screen = packet_get_int(); - } else { - s->screen = 0; - } - packet_check_eom(); - success = session_setup_x11fwd(s); - if (!success) { - xfree(s->auth_proto); - xfree(s->auth_data); - s->auth_proto = NULL; - s->auth_data = NULL; - } - break; - - case SSH_CMSG_AGENT_REQUEST_FORWARDING: - if (no_agent_forwarding_flag || compat13) { - debug("Authentication agent forwarding not permitted for this authentication."); - break; - } - debug("Received authentication agent forwarding request."); - success = auth_input_request_forwarding(s->pw); - break; - - case SSH_CMSG_PORT_FORWARD_REQUEST: - if (no_port_forwarding_flag) { - debug("Port forwarding not permitted for this authentication."); - break; - } - if (!options.allow_tcp_forwarding) { - debug("Port forwarding not permitted."); - break; - } - debug("Received TCP/IP port forwarding request."); - channel_input_port_forward_request(s->pw->pw_uid == 0, options.gateway_ports); - success = 1; - break; - - case SSH_CMSG_MAX_PACKET_SIZE: - if (packet_set_maxsize(packet_get_int()) > 0) - success = 1; - break; - -#if defined(AFS) || defined(KRB5) - case SSH_CMSG_HAVE_KERBEROS_TGT: - if (!options.kerberos_tgt_passing) { - verbose("Kerberos TGT passing disabled."); - } else { - char *kdata = packet_get_string(&dlen); - packet_check_eom(); - - /* XXX - 0x41, see creds_to_radix version */ - if (kdata[0] != 0x41) { -#ifdef KRB5 - krb5_data tgt; - tgt.data = kdata; - tgt.length = dlen; - - if (auth_krb5_tgt(s->authctxt, &tgt)) - success = 1; - else - verbose("Kerberos v5 TGT refused for %.100s", s->authctxt->user); -#endif /* KRB5 */ - } else { -#ifdef AFS - if (auth_krb4_tgt(s->authctxt, kdata)) - success = 1; - else - verbose("Kerberos v4 TGT refused for %.100s", s->authctxt->user); -#endif /* AFS */ - } - xfree(kdata); - } - break; -#endif /* AFS || KRB5 */ - -#ifdef AFS - case SSH_CMSG_HAVE_AFS_TOKEN: - if (!options.afs_token_passing || !k_hasafs()) { - verbose("AFS token passing disabled."); - } else { - /* Accept AFS token. */ - char *token = packet_get_string(&dlen); - packet_check_eom(); - - if (auth_afs_token(s->authctxt, token)) - success = 1; - else - verbose("AFS token refused for %.100s", - s->authctxt->user); - xfree(token); - } - break; -#endif /* AFS */ - - case SSH_CMSG_EXEC_SHELL: - case SSH_CMSG_EXEC_CMD: - if (type == SSH_CMSG_EXEC_CMD) { - command = packet_get_string(&dlen); - debug("Exec command '%.500s'", command); - do_exec(s, command); - xfree(command); - } else { - do_exec(s, NULL); - } - packet_check_eom(); - session_close(s); - return; - - default: - /* - * Any unknown messages in this phase are ignored, - * and a failure message is returned. - */ - log("Unknown packet type received after authentication: %d", type); - } - packet_start(success ? SSH_SMSG_SUCCESS : SSH_SMSG_FAILURE); - packet_send(); - packet_write_wait(); - - /* Enable compression now that we have replied if appropriate. */ - if (enable_compression_after_reply) { - enable_compression_after_reply = 0; - packet_start_compression(compression_level); - } - } -} - -/* - * This is called to fork and execute a command when we have no tty. This - * will call do_child from the child, and server_loop from the parent after - * setting up file descriptors and such. - */ -void -do_exec_no_pty(Session *s, const char *command) -{ - pid_t pid; - - int inout[2], err[2]; - /* Uses socket pairs to communicate with the program. */ - if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) < 0 || - socketpair(AF_UNIX, SOCK_STREAM, 0, err) < 0) - packet_disconnect("Could not create socket pairs: %.100s", - strerror(errno)); - if (s == NULL) - fatal("do_exec_no_pty: no session"); - - session_proctitle(s); - - /* Fork the child. */ - if ((pid = fork()) == 0) { - fatal_remove_all_cleanups(); - - /* Child. Reinitialize the log since the pid has changed. */ - log_init(__progname, options.log_level, options.log_facility, log_stderr); - - /* - * Create a new session and process group since the 4.4BSD - * setlogin() affects the entire process group. - */ - if (setsid() < 0) - error("setsid failed: %.100s", strerror(errno)); - - /* - * Redirect stdin, stdout, and stderr. Stdin and stdout will - * use the same socket, as some programs (particularly rdist) - * seem to depend on it. - */ - close(inout[1]); - close(err[1]); - if (dup2(inout[0], 0) < 0) /* stdin */ - perror("dup2 stdin"); - if (dup2(inout[0], 1) < 0) /* stdout. Note: same socket as stdin. */ - perror("dup2 stdout"); - if (s->is_subsystem) { - /* - * Redirect the subsystem's stderr to /dev/null. We might send it - * over to the other side but changing that might break existing - * SSH clients. - */ - close(err[0]); - if ((err[0] = open(_PATH_DEVNULL, O_WRONLY)) == -1) - fatal("Cannot open /dev/null: %.100s", strerror(errno)); - } - if (dup2(err[0], 2) < 0) /* stderr */ - perror("dup2 stderr"); - -#ifdef _UNICOS - cray_init_job(s->pw); /* set up cray jid and tmpdir */ -#endif - - /* Do processing for the child (exec command etc). */ - do_child(s, command); - /* NOTREACHED */ - } -#ifdef _UNICOS - signal(WJSIGNAL, cray_job_termination_handler); -#endif /* _UNICOS */ -#ifdef HAVE_CYGWIN - if (is_winnt) - cygwin_set_impersonation_token(INVALID_HANDLE_VALUE); -#endif - if (pid < 0) - packet_disconnect("fork failed: %.100s", strerror(errno)); - - s->pid = pid; - /* Set interactive/non-interactive mode. */ - packet_set_interactive(s->display != NULL); - - /* We are the parent. Close the child sides of the socket pairs. */ - close(inout[0]); - close(err[0]); - - /* - * Enter the interactive session. Note: server_loop must be able to - * handle the case that fdin and fdout are the same. - */ - if (compat20) { - session_set_fds(s, inout[1], inout[1], s->is_subsystem ? -1 : err[1]); - if (s->is_subsystem) - close(err[1]); - /* Don't close channel before sending exit-status! */ - channel_set_wait_for_exit(s->chanid, 1); - } else { - server_loop(pid, inout[1], inout[1], err[1]); - /* server_loop has closed inout[1] and err[1]. */ - } -} - -/* - * This is called to fork and execute a command when we have a tty. This - * will call do_child from the child, and server_loop from the parent after - * setting up file descriptors, controlling tty, updating wtmp, utmp, - * lastlog, and other such operations. - */ -void -do_exec_pty(Session *s, const char *command) -{ - int fdout, ptyfd, ttyfd, ptymaster, pipe_fds[2]; - pid_t pid; - - if (s == NULL) - fatal("do_exec_pty: no session"); - ptyfd = s->ptyfd; - ttyfd = s->ttyfd; - -#ifdef USE_PAM - session_do_pam(s, 1); /* pam_open_session() */ -#endif /* USE_PAM */ - - /* - * This pipe lets sshd wait for child to exec or exit. This is - * particularly important for ALTPRIVSEP because the child is - * the one to call the monitor to request a record_login() and - * we don't want the child and the parent to compete for the - * monitor's attention. But this is generic code and doesn't - * hurt to have here even if ALTPRIVSEP is not used. - */ - if (pipe(pipe_fds) != 0) - packet_disconnect("pipe failed: %.100s", strerror(errno)); - - (void) fcntl(pipe_fds[0], F_SETFD, FD_CLOEXEC); - (void) fcntl(pipe_fds[1], F_SETFD, FD_CLOEXEC); - - /* Fork the child. */ - if ((pid = fork()) == 0) { - (void) close(pipe_fds[0]); - - fatal_remove_all_cleanups(); - - /* Child. Reinitialize the log because the pid has changed. */ - log_init(__progname, options.log_level, options.log_facility, log_stderr); - /* Close the master side of the pseudo tty. */ - close(ptyfd); - - /* Make the pseudo tty our controlling tty. */ - pty_make_controlling_tty(&ttyfd, s->tty); - - /* Redirect stdin/stdout/stderr from the pseudo tty. */ - if (dup2(ttyfd, 0) < 0) - error("dup2 stdin: %s", strerror(errno)); - if (dup2(ttyfd, 1) < 0) - error("dup2 stdout: %s", strerror(errno)); - if (dup2(ttyfd, 2) < 0) - error("dup2 stderr: %s", strerror(errno)); - - /* Close the extra descriptor for the pseudo tty. */ - close(ttyfd); - - /* record login, etc. similar to login(1) */ - do_login(s, command); - - /* - * Close the pipe to the parent so it can re-enter its event - * loop and service the ptm; if enough debug messages get - * written to the pty before this happens there will be a - * deadlock. - */ - close(pipe_fds[1]); - - /* - * do_motd() was called originally in do_login(). However, - * when the /etc/motd file is large, a deadlock would happen, - * because - * - The child is blocked at fputs() to pty, when pty buffer - * is full. - * - The parent can not consume the pty buffer, because it is - * still blocked at read(pipe_fds[0]). - * - * To resolve the deadlock issue, we defer do_motd() after - * close(pipe_fds[1]). - */ - do_motd(); - - /* Do common processing for the child, such as execing the command. */ - do_child(s, command); - /* NOTREACHED */ - } - - /* Wait for child to exec() or exit() */ - (void) close(pipe_fds[1]); - (void) read(pipe_fds[0], &pipe_fds[1], sizeof(int)); - -#ifdef _UNICOS - signal(WJSIGNAL, cray_job_termination_handler); -#endif /* _UNICOS */ -#ifdef HAVE_CYGWIN - if (is_winnt) - cygwin_set_impersonation_token(INVALID_HANDLE_VALUE); -#endif - if (pid < 0) - packet_disconnect("fork failed: %.100s", strerror(errno)); - s->pid = pid; - - /* Parent. Close the slave side of the pseudo tty. */ - close(ttyfd); - - /* - * Create another descriptor of the pty master side for use as the - * standard input. We could use the original descriptor, but this - * simplifies code in server_loop. The descriptor is bidirectional. - */ - fdout = dup(ptyfd); - if (fdout < 0) - packet_disconnect("dup #1 failed: %.100s", strerror(errno)); - - /* we keep a reference to the pty master */ - ptymaster = dup(ptyfd); - if (ptymaster < 0) - packet_disconnect("dup #2 failed: %.100s", strerror(errno)); - s->ptymaster = ptymaster; - - /* Enter interactive session. */ - packet_set_interactive(1); - if (compat20) { - session_set_fds(s, ptyfd, fdout, -1); - /* Don't close channel before sending exit-status! */ - channel_set_wait_for_exit(s->chanid, 1); - } else { - server_loop(pid, ptyfd, fdout, -1); - /* server_loop _has_ closed ptyfd and fdout. */ - } -} - -/* - * This is called to fork and execute a command. If another command is - * to be forced, execute that instead. - */ -void -do_exec(Session *s, const char *command) -{ - if (command) - s->command = xstrdup(command); - - if (forced_command) { - original_command = command; - command = forced_command; - debug("Forced command '%.900s'", command); - } - - if (s->ttyfd != -1) - do_exec_pty(s, command); - else - do_exec_no_pty(s, command); - - original_command = NULL; -} - - -/* administrative, login(1)-like work */ -void -do_login(Session *s, const char *command) -{ - char *time_string; -#ifndef ALTPRIVSEP - struct passwd * pw = s->pw; -#endif /* ALTPRIVSEP*/ - pid_t pid = getpid(); - - /* Record that there was a login on that tty from the remote host. */ -#ifdef ALTPRIVSEP - debug3("Recording SSHv2 channel login in utmpx/wtmpx"); - altprivsep_record_login(pid, s->tty); -#endif /* ALTPRIVSEP*/ - - if (check_quietlogin(s, command)) - return; - -#ifdef USE_PAM - print_pam_messages(); -#endif /* USE_PAM */ -#ifdef WITH_AIXAUTHENTICATE - if (aixloginmsg && *aixloginmsg) - printf("%s\n", aixloginmsg); -#endif /* WITH_AIXAUTHENTICATE */ - -#ifndef NO_SSH_LASTLOG - if (options.print_lastlog && s->last_login_time != 0) { - time_string = ctime(&s->last_login_time); - if (strchr(time_string, '\n')) - *strchr(time_string, '\n') = 0; - if (strcmp(s->hostname, "") == 0) - printf("Last login: %s\r\n", time_string); - else - printf("Last login: %s from %s\r\n", time_string, - s->hostname); - } -#endif /* NO_SSH_LASTLOG */ - -} - -/* - * Display the message of the day. - */ -void -do_motd(void) -{ - FILE *f; - char buf[256]; - - if (options.print_motd) { -#ifdef HAVE_LOGIN_CAP - f = fopen(login_getcapstr(lc, "welcome", "/etc/motd", - "/etc/motd"), "r"); -#else - f = fopen("/etc/motd", "r"); -#endif - if (f) { - while (fgets(buf, sizeof(buf), f)) - fputs(buf, stdout); - fclose(f); - } - } -} - - -/* - * 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 - * already exists, its value is overriden. - */ -void -child_set_env(char ***envp, u_int *envsizep, const char *name, - const char *value) -{ - debug3("child_set_env(%s, %s)", name, value); - child_set_env_silent(envp, envsizep, name, value); -} - - -void -child_set_env_silent(char ***envp, u_int *envsizep, const char *name, - const char *value) -{ - u_int i, namelen; - char **env; - - /* - * Find the slot where the value should be stored. If the variable - * already exists, we reuse the slot; otherwise we append a new slot - * at the end of the array, expanding if necessary. - */ - env = *envp; - namelen = strlen(name); - for (i = 0; env[i]; i++) - if (strncmp(env[i], name, namelen) == 0 && env[i][namelen] == '=') - break; - if (env[i]) { - /* Reuse the slot. */ - xfree(env[i]); - } else { - /* New variable. Expand if necessary. */ - if (i >= (*envsizep) - 1) { - if (*envsizep >= 1000) - fatal("child_set_env: too many env vars," - " skipping: %.100s", name); - (*envsizep) += 50; - env = (*envp) = xrealloc(env, (*envsizep) * sizeof(char *)); - } - /* Need to set the NULL pointer at end of array beyond the new slot. */ - env[i + 1] = NULL; - } - - /* Allocate space and format the variable in the appropriate slot. */ - env[i] = xmalloc(strlen(name) + 1 + strlen(value) + 1); - snprintf(env[i], strlen(name) + 1 + strlen(value) + 1, "%s=%s", name, value); -} - -/* - * Reads environment variables from the given file and adds/overrides them - * into the environment. If the file does not exist, this does nothing. - * Otherwise, it must consist of empty lines, comments (line starts with '#') - * and assignments of the form name=value. No other forms are allowed. - */ -static void -read_environment_file(char ***env, u_int *envsize, - const char *filename) -{ - FILE *f; - char buf[4096]; - char *cp, *value; - u_int lineno = 0; - - f = fopen(filename, "r"); - if (!f) - return; - - while (fgets(buf, sizeof(buf), f)) { - if (++lineno > 1000) - fatal("Too many lines in environment file %s", filename); - for (cp = buf; *cp == ' ' || *cp == '\t'; cp++) - ; - if (!*cp || *cp == '#' || *cp == '\n') - continue; - if (strchr(cp, '\n')) - *strchr(cp, '\n') = '\0'; - value = strchr(cp, '='); - if (value == NULL) { - fprintf(stderr, gettext("Bad line %u in %.100s\n"), - lineno, filename); - continue; - } - /* - * Replace the equals sign by nul, and advance value to - * the value string. - */ - *value = '\0'; - value++; - child_set_env(env, envsize, cp, value); - } - fclose(f); -} - -void copy_environment(char **source, char ***env, u_int *envsize) -{ - char *var_name, *var_val; - int i; - - if (source == NULL) - return; - - for(i = 0; source[i] != NULL; i++) { - var_name = xstrdup(source[i]); - if ((var_val = strstr(var_name, "=")) == NULL) { - xfree(var_name); - continue; - } - *var_val++ = '\0'; - - debug3("Copy environment: %s=%s", var_name, var_val); - child_set_env(env, envsize, var_name, var_val); - - xfree(var_name); - } -} - -#ifdef HAVE_DEFOPEN -static -void -deflt_do_setup_env(Session *s, const char *shell, char ***env, u_int *envsize) -{ - int flags; - char *ptr; - mode_t Umask = 022; - - if (defopen(_PATH_DEFAULT_LOGIN)) - return; - - /* Ignore case */ - flags = defcntl(DC_GETFLAGS, 0); - TURNOFF(flags, DC_CASE); - (void) defcntl(DC_SETFLAGS, flags); - - /* TZ & HZ */ - if ((ptr = defread("TIMEZONE=")) != NULL) - child_set_env(env, envsize, "TZ", ptr); - if ((ptr = defread("HZ=")) != NULL) - child_set_env(env, envsize, "HZ", ptr); - - /* PATH */ - if (s->pw->pw_uid != 0 && (ptr = defread("PATH=")) != NULL) - child_set_env(env, envsize, "PATH", ptr); - if (s->pw->pw_uid == 0 && (ptr = defread("SUPATH=")) != NULL) - child_set_env(env, envsize, "PATH", ptr); - - /* SHELL */ - if ((ptr = defread("ALTSHELL=")) != NULL) { - if (strcasecmp("YES", ptr) == 0) - child_set_env(env, envsize, "SHELL", shell); - else - child_set_env(env, envsize, "SHELL", ""); - } - - /* UMASK */ - if ((ptr = defread("UMASK=")) != NULL && - sscanf(ptr, "%lo", &Umask) == 1 && - Umask <= (mode_t)0777) - (void) umask(Umask); - else - (void) umask(022); - - /* ULIMIT */ - if ((ptr = defread("ULIMIT=")) != NULL && atol(ptr) > 0L && - ulimit(UL_SETFSIZE, atol(ptr)) < 0L) - error("Could not set ULIMIT to %ld from %s\n", atol(ptr), - _PATH_DEFAULT_LOGIN); - - (void) defopen(NULL); -} -#endif /* HAVE_DEFOPEN */ - -static char ** -do_setup_env(Session *s, const char *shell) -{ - char buf[256]; - char path_maildir[] = _PATH_MAILDIR; - u_int i, envsize, pm_len; - char **env; - struct passwd *pw = s->pw; - - /* Initialize the environment. */ - envsize = 100; - env = xmalloc(envsize * sizeof(char *)); - env[0] = NULL; - -#ifdef HAVE_CYGWIN - /* - * The Windows environment contains some setting which are - * important for a running system. They must not be dropped. - */ - copy_environment(environ, &env, &envsize); -#endif - -#ifdef GSSAPI - /* Allow any GSSAPI methods that we've used to alter - * the childs environment as they see fit - */ - ssh_gssapi_do_child(xxx_gssctxt, &env,&envsize); -#endif - - /* Set basic environment. */ - child_set_env(&env, &envsize, "USER", pw->pw_name); - child_set_env(&env, &envsize, "LOGNAME", pw->pw_name); - child_set_env(&env, &envsize, "HOME", pw->pw_dir); -#ifdef HAVE_LOGIN_CAP - if (setusercontext(lc, pw, pw->pw_uid, LOGIN_SETPATH) < 0) - child_set_env(&env, &envsize, "PATH", _PATH_STDPATH); - else - child_set_env(&env, &envsize, "PATH", getenv("PATH")); -#else /* HAVE_LOGIN_CAP */ -# ifndef HAVE_CYGWIN - /* - * There's no standard path on Windows. The path contains - * important components pointing to the system directories, - * needed for loading shared libraries. So the path better - * remains intact here. - */ -# ifdef SUPERUSER_PATH - child_set_env(&env, &envsize, "PATH", - s->pw->pw_uid == 0 ? SUPERUSER_PATH : _PATH_STDPATH); -# else - child_set_env(&env, &envsize, "PATH", _PATH_STDPATH); -# endif /* SUPERUSER_PATH */ -# endif /* HAVE_CYGWIN */ -#endif /* HAVE_LOGIN_CAP */ - - pm_len = strlen(path_maildir); - if (path_maildir[pm_len - 1] == '/' && pm_len > 1) - path_maildir[pm_len - 1] = NULL; - snprintf(buf, sizeof buf, "%.200s/%.50s", - path_maildir, pw->pw_name); - child_set_env(&env, &envsize, "MAIL", buf); - - /* Normal systems set SHELL by default. */ - child_set_env(&env, &envsize, "SHELL", shell); - -#ifdef HAVE_DEFOPEN - deflt_do_setup_env(s, shell, &env, &envsize); -#endif /* HAVE_DEFOPEN */ - -#define PASS_ENV(x) \ - if (getenv(x)) \ - child_set_env(&env, &envsize, x, getenv(x)); - - if (getenv("TZ")) - child_set_env(&env, &envsize, "TZ", getenv("TZ")); - - if (s->auth_file != NULL) - child_set_env(&env, &envsize, "XAUTHORITY", s->auth_file); - - PASS_ENV("LANG") - PASS_ENV("LC_ALL") - PASS_ENV("LC_CTYPE") - PASS_ENV("LC_COLLATE") - PASS_ENV("LC_TIME") - PASS_ENV("LC_NUMERIC") - PASS_ENV("LC_MONETARY") - PASS_ENV("LC_MESSAGES") - -#undef PASS_ENV - - if (s->env != NULL) - copy_environment(s->env, &env, &envsize); - - /* Set custom environment options from RSA authentication. */ - while (custom_environment) { - struct envstring *ce = custom_environment; - char *str = ce->s; - - for (i = 0; str[i] != '=' && str[i]; i++) - ; - if (str[i] == '=') { - str[i] = 0; - child_set_env(&env, &envsize, str, str + i + 1); - } - custom_environment = ce->next; - xfree(ce->s); - xfree(ce); - } - - /* SSH_CLIENT deprecated */ - snprintf(buf, sizeof buf, "%.50s %d %d", - get_remote_ipaddr(), get_remote_port(), get_local_port()); - child_set_env(&env, &envsize, "SSH_CLIENT", buf); - - snprintf(buf, sizeof buf, "%.50s %d %.50s %d", - get_remote_ipaddr(), get_remote_port(), - get_local_ipaddr(packet_get_connection_in()), get_local_port()); - child_set_env(&env, &envsize, "SSH_CONNECTION", buf); - - if (s->ttyfd != -1) - child_set_env(&env, &envsize, "SSH_TTY", s->tty); - if (s->term) - child_set_env(&env, &envsize, "TERM", s->term); - if (s->display) - child_set_env(&env, &envsize, "DISPLAY", s->display); - if (original_command) - child_set_env(&env, &envsize, "SSH_ORIGINAL_COMMAND", - original_command); - -#ifdef _UNICOS - if (cray_tmpdir[0] != '\0') - child_set_env(&env, &envsize, "TMPDIR", cray_tmpdir); -#endif /* _UNICOS */ - -#ifdef _AIX - { - char *cp; - - if ((cp = getenv("AUTHSTATE")) != NULL) - child_set_env(&env, &envsize, "AUTHSTATE", cp); - if ((cp = getenv("KRB5CCNAME")) != NULL) - child_set_env(&env, &envsize, "KRB5CCNAME", cp); - read_environment_file(&env, &envsize, "/etc/environment"); - } -#endif -#ifdef KRB4 - if (s->authctxt->krb4_ticket_file) - child_set_env(&env, &envsize, "KRBTKFILE", - s->authctxt->krb4_ticket_file); -#endif -#ifdef KRB5 - if (s->authctxt->krb5_ticket_file) - child_set_env(&env, &envsize, "KRB5CCNAME", - s->authctxt->krb5_ticket_file); -#endif -#ifdef USE_PAM - /* - * Pull in any environment variables that may have - * been set by PAM. - */ - { - char **p; - - p = fetch_pam_environment(s->authctxt); - copy_environment(p, &env, &envsize); - free_pam_environment(p); - } -#endif /* USE_PAM */ - - if (auth_sock_name != NULL) - child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME, - auth_sock_name); - - /* read $HOME/.ssh/environment. */ - if (options.permit_user_env) { - snprintf(buf, sizeof buf, "%.200s/.ssh/environment", - strcmp(pw->pw_dir, "/") ? pw->pw_dir : ""); - read_environment_file(&env, &envsize, buf); - } - if (debug_flag) { - /* dump the environment */ - fprintf(stderr, gettext("Environment:\n")); - for (i = 0; env[i]; i++) - fprintf(stderr, " %.200s\n", env[i]); - } - return env; -} - -/* - * Run $HOME/.ssh/rc, /etc/ssh/sshrc, or xauth (whichever is found - * first in this order). - */ -static void -do_rc_files(Session *s, const char *shell) -{ - FILE *f = NULL; - char cmd[1024]; - int do_xauth; - struct stat st; - - do_xauth = - s->display != NULL && s->auth_proto != NULL && s->auth_data != NULL; - - /* ignore _PATH_SSH_USER_RC for subsystems */ - if (!s->is_subsystem && (stat(_PATH_SSH_USER_RC, &st) >= 0)) { - snprintf(cmd, sizeof cmd, "%s -c '%s %s'", - shell, _PATH_BSHELL, _PATH_SSH_USER_RC); - if (debug_flag) - fprintf(stderr, "Running %s\n", cmd); - f = popen(cmd, "w"); - if (f) { - if (do_xauth) - fprintf(f, "%s %s\n", s->auth_proto, - s->auth_data); - pclose(f); - } else - fprintf(stderr, "Could not run %s\n", - _PATH_SSH_USER_RC); - } else if (stat(_PATH_SSH_SYSTEM_RC, &st) >= 0) { - if (debug_flag) - fprintf(stderr, "Running %s %s\n", _PATH_BSHELL, - _PATH_SSH_SYSTEM_RC); - f = popen(_PATH_BSHELL " " _PATH_SSH_SYSTEM_RC, "w"); - if (f) { - if (do_xauth) - fprintf(f, "%s %s\n", s->auth_proto, - s->auth_data); - pclose(f); - } else - fprintf(stderr, "Could not run %s\n", - _PATH_SSH_SYSTEM_RC); - } else if (do_xauth && options.xauth_location != NULL) { - /* Add authority data to .Xauthority if appropriate. */ - if (debug_flag) { - fprintf(stderr, - "Running %.500s add " - "%.100s %.100s %.100s\n", - options.xauth_location, s->auth_display, - s->auth_proto, s->auth_data); - } - snprintf(cmd, sizeof cmd, "%s -q -", - options.xauth_location); - f = popen(cmd, "w"); - if (f) { - fprintf(f, "add %s %s %s\n", - s->auth_display, s->auth_proto, - s->auth_data); - pclose(f); - } else { - fprintf(stderr, "Could not run %s\n", - cmd); - } - } -} - -/* Disallow logins if /etc/nologin exists. This does not apply to root. */ -static void -do_nologin(struct passwd *pw) -{ - FILE *f = NULL; - char buf[1024]; - struct stat sb; - - if (pw->pw_uid == 0) - return; - - if (stat(_PATH_NOLOGIN, &sb) == -1) - return; - - /* /etc/nologin exists. Print its contents if we can and exit. */ - log("User %.100s not allowed because %s exists.", pw->pw_name, - _PATH_NOLOGIN); - if ((f = fopen(_PATH_NOLOGIN, "r")) != NULL) { - while (fgets(buf, sizeof(buf), f)) - fputs(buf, stderr); - fclose(f); - } - exit(254); -} - -/* Chroot into ChrootDirectory if the option is set. */ -void -chroot_if_needed(struct passwd *pw) -{ - char *chroot_path, *tmp; - - if (chroot_requested(options.chroot_directory)) { - tmp = tilde_expand_filename(options.chroot_directory, - pw->pw_uid); - chroot_path = percent_expand(tmp, "h", pw->pw_dir, - "u", pw->pw_name, (char *)NULL); - safely_chroot(chroot_path, pw->pw_uid); - free(tmp); - free(chroot_path); - } -} - -/* - * Chroot into a directory after checking it for safety: all path components - * must be root-owned directories with strict permissions. - */ -static void -safely_chroot(const char *path, uid_t uid) -{ - const char *cp; - char component[MAXPATHLEN]; - struct stat st; - - if (*path != '/') - fatal("chroot path does not begin at root"); - if (strlen(path) >= sizeof(component)) - fatal("chroot path too long"); - - /* - * Descend the path, checking that each component is a - * root-owned directory with strict permissions. - */ - for (cp = path; cp != NULL;) { - if ((cp = strchr(cp, '/')) == NULL) - strlcpy(component, path, sizeof(component)); - else { - cp++; - memcpy(component, path, cp - path); - component[cp - path] = '\0'; - } - - debug3("%s: checking '%s'", __func__, component); - - if (stat(component, &st) != 0) - fatal("%s: stat(\"%s\"): %s", __func__, - component, strerror(errno)); - if (st.st_uid != 0 || (st.st_mode & 022) != 0) - fatal("bad ownership or modes for chroot " - "directory %s\"%s\"", - cp == NULL ? "" : "component ", component); - if (!S_ISDIR(st.st_mode)) - fatal("chroot path %s\"%s\" is not a directory", - cp == NULL ? "" : "component ", component); - } - - if (chdir(path) == -1) - fatal("Unable to chdir to chroot path \"%s\": " - "%s", path, strerror(errno)); - if (chroot(path) == -1) - fatal("chroot(\"%s\"): %s", path, strerror(errno)); - if (chdir("/") == -1) - fatal("%s: chdir(/) after chroot: %s", - __func__, strerror(errno)); - verbose("Changed root directory to \"%s\"", path); -} - -static void -launch_login(struct passwd *pw, const char *hostname) -{ - /* Launch login(1). */ - - execl(LOGIN_PROGRAM, "login", "-h", hostname, -#ifdef xxxLOGIN_NEEDS_TERM - (s->term ? s->term : "unknown"), -#endif /* LOGIN_NEEDS_TERM */ -#ifdef LOGIN_NO_ENDOPT - "-p", "-f", pw->pw_name, (char *)NULL); -#else - "-p", "-f", "--", pw->pw_name, (char *)NULL); -#endif - - /* Login couldn't be executed, die. */ - - perror("login"); - exit(1); -} - -/* - * Performs common processing for the child, such as setting up the - * environment, closing extra file descriptors, setting the user and group - * ids, and executing the command or shell. - */ -#define ARGV_MAX 10 -void -do_child(Session *s, const char *command) -{ - extern char **environ; - char **env; - char *argv[ARGV_MAX]; - const char *shell, *shell0; - struct passwd *pw = s->pw; - - /* remove hostkey from the child's memory */ - destroy_sensitive_data(); - - do_nologin(pw); - chroot_if_needed(pw); - - /* - * Get the shell from the password data. An empty shell field is - * legal, and means /bin/sh. - */ - shell = (pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell; -#ifdef HAVE_LOGIN_CAP - shell = login_getcapstr(lc, "shell", (char *)shell, (char *)shell); -#endif - - env = do_setup_env(s, shell); - - /* - * Close the connection descriptors; note that this is the child, and - * the server will still have the socket open, and it is important - * that we do not shutdown it. Note that the descriptors cannot be - * closed before building the environment, as we call - * get_remote_ipaddr there. - */ - if (packet_get_connection_in() == packet_get_connection_out()) - close(packet_get_connection_in()); - else { - close(packet_get_connection_in()); - close(packet_get_connection_out()); - } - /* - * Close all descriptors related to channels. They will still remain - * open in the parent. - */ - /* XXX better use close-on-exec? -markus */ - channel_close_all(); - - /* - * Close any extra file descriptors. Note that there may still be - * descriptors left by system functions. They will be closed later. - */ - endpwent(); - - /* - * Must switch to the new environment variables so that .ssh/rc, - * /etc/ssh/sshrc, and xauth are run in the proper environment. - */ - environ = env; - - /* - * New environment has been installed. We need to update locale - * so that error messages beyond this point have the proper - * character encoding. - */ - (void) setlocale(LC_ALL, ""); - - /* - * Close any extra open file descriptors so that we don\'t have them - * hanging around in clients. Note that we want to do this after - * initgroups, because at least on Solaris 2.3 it leaves file - * descriptors open. - */ - closefrom(STDERR_FILENO + 1); - -#ifdef AFS - /* Try to get AFS tokens for the local cell. */ - if (k_hasafs()) { - char cell[64]; - - if (k_afs_cell_of_file(pw->pw_dir, cell, sizeof(cell)) == 0) - krb_afslog(cell, 0); - - krb_afslog(0, 0); - } -#endif /* AFS */ - - /* Change current directory to the user's home directory. */ - if (chdir(pw->pw_dir) < 0) { - /* Suppress missing homedir warning for chroot case */ - if (!chroot_requested(options.chroot_directory)) - fprintf(stderr, "Could not chdir to home " - "directory %s: %s\n", pw->pw_dir, - strerror(errno)); - } - - do_rc_files(s, shell); - - /* restore SIGPIPE for child */ - signal(SIGPIPE, SIG_DFL); - - if (s->is_subsystem == SUBSYSTEM_INT_SFTP) { - int i; - char *p, *args; - extern int optind, optreset; - - /* This will set the E/P sets here, simulating exec(2). */ - drop_privs(pw->pw_uid); - - setproctitle("%s@internal-sftp-server", s->pw->pw_name); - args = xstrdup(command ? command : "sftp-server"); - - i = 0; - for ((p = strtok(args, " ")); p != NULL; (p = strtok(NULL, " "))) { - if (i < ARGV_MAX - 1) - argv[i++] = p; - } - - argv[i] = NULL; - optind = optreset = 1; - __progname = argv[0]; - exit(sftp_server_main(i, argv, s->pw)); - } - - /* Get the last component of the shell name. */ - if ((shell0 = strrchr(shell, '/')) != NULL) - shell0++; - else - shell0 = 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 - * this is a login shell. - */ - if (!command) { - char argv0[256]; - - /* Start the shell. Set initial character to '-'. */ - argv0[0] = '-'; - - if (strlcpy(argv0 + 1, shell0, sizeof(argv0) - 1) - >= sizeof(argv0) - 1) { - errno = EINVAL; - perror(shell); - exit(1); - } - - /* Execute the shell. */ - argv[0] = argv0; - argv[1] = NULL; - execve(shell, argv, env); - - /* Executing the shell failed. */ - perror(shell); - exit(1); - } - /* - * Execute the command using the user's shell. This uses the -c - * option to execute the command. - */ - argv[0] = (char *) shell0; - argv[1] = "-c"; - argv[2] = (char *) command; - argv[3] = NULL; - execve(shell, argv, env); - perror(shell); - exit(1); -} - -Session * -session_new(void) -{ - int i; - static int did_init = 0; - if (!did_init) { - debug("session_new: init"); - for (i = 0; i < MAX_SESSIONS; i++) { - sessions[i].used = 0; - } - did_init = 1; - } - for (i = 0; i < MAX_SESSIONS; i++) { - Session *s = &sessions[i]; - if (! s->used) { - memset(s, 0, sizeof(*s)); - s->chanid = -1; - s->ptyfd = -1; - s->ttyfd = -1; - s->used = 1; - s->self = i; - s->env = NULL; - debug("session_new: session %d", i); - return s; - } - } - return NULL; -} - -static void -session_dump(void) -{ - int i; - for (i = 0; i < MAX_SESSIONS; i++) { - Session *s = &sessions[i]; - debug("dump: used %d session %d %p channel %d pid %ld", - s->used, - s->self, - s, - s->chanid, - (long)s->pid); - } -} - -int -session_open(Authctxt *authctxt, int chanid) -{ - Session *s = session_new(); - debug("session_open: channel %d", chanid); - if (s == NULL) { - error("no more sessions"); - return 0; - } - s->authctxt = authctxt; - s->pw = authctxt->pw; - if (s->pw == NULL) - fatal("no user for session %d", s->self); - debug("session_open: session %d: link with channel %d", s->self, chanid); - s->chanid = chanid; - return 1; -} - -#ifndef lint -Session * -session_by_tty(char *tty) -{ - int i; - for (i = 0; i < MAX_SESSIONS; i++) { - Session *s = &sessions[i]; - if (s->used && s->ttyfd != -1 && strcmp(s->tty, tty) == 0) { - debug("session_by_tty: session %d tty %s", i, tty); - return s; - } - } - debug("session_by_tty: unknown tty %.100s", tty); - session_dump(); - return NULL; -} -#endif /* lint */ - -static Session * -session_by_channel(int id) -{ - int i; - for (i = 0; i < MAX_SESSIONS; i++) { - Session *s = &sessions[i]; - if (s->used && s->chanid == id) { - debug("session_by_channel: session %d channel %d", i, id); - return s; - } - } - debug("session_by_channel: unknown channel %d", id); - session_dump(); - return NULL; -} - -static Session * -session_by_pid(pid_t pid) -{ - int i; - debug("session_by_pid: pid %ld", (long)pid); - for (i = 0; i < MAX_SESSIONS; i++) { - Session *s = &sessions[i]; - if (s->used && s->pid == pid) - return s; - } - error("session_by_pid: unknown pid %ld", (long)pid); - session_dump(); - return NULL; -} - -static int -session_window_change_req(Session *s) -{ - s->col = packet_get_int(); - s->row = packet_get_int(); - s->xpixel = packet_get_int(); - s->ypixel = packet_get_int(); - packet_check_eom(); - pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel); - return 1; -} - -static int -session_pty_req(Session *s) -{ - u_int len; - int n_bytes; - - if (no_pty_flag) { - debug("Allocating a pty not permitted for this authentication."); - return 0; - } - if (s->ttyfd != -1) { - packet_disconnect("Protocol error: you already have a pty."); - return 0; - } - /* Get the time and hostname when the user last logged in. */ - if (options.print_lastlog) { - s->hostname[0] = '\0'; - s->last_login_time = get_last_login_time(s->pw->pw_uid, - s->pw->pw_name, s->hostname, sizeof(s->hostname)); - - /* - * PAM may update the last login date. - * - * Ideally PAM would also show the last login date as a - * PAM_TEXT_INFO conversation message, and then we could just - * always force the use of keyboard-interactive just so we can - * pass any such PAM prompts and messages from the account and - * session stacks, but skip pam_authenticate() if other userauth - * has succeeded and the user's password isn't expired. - * - * Unfortunately this depends on support for keyboard- - * interactive in the client, and support for lastlog messages - * in some PAM module. - * - * As it is Solaris updates the lastlog in PAM, but does - * not show the lastlog date in PAM. If and when this state of - * affairs changes this hack can be reconsidered, and, maybe, - * removed. - * - * So we're stuck with a crude hack: get the lastlog - * time before calling pam_open_session() and store it - * in the Authctxt and then use it here once. After - * that, if the client opens any more pty sessions we'll - * show the last lastlog entry since userauth. - */ - if (s->authctxt != NULL && s->authctxt->last_login_time > 0) { - s->last_login_time = s->authctxt->last_login_time; - (void) strlcpy(s->hostname, - s->authctxt->last_login_host, - sizeof(s->hostname)); - s->authctxt->last_login_time = 0; - s->authctxt->last_login_host[0] = '\0'; - } - } - - s->term = packet_get_string(&len); - - if (compat20) { - s->col = packet_get_int(); - s->row = packet_get_int(); - } else { - s->row = packet_get_int(); - s->col = packet_get_int(); - } - s->xpixel = packet_get_int(); - s->ypixel = packet_get_int(); - - if (strcmp(s->term, "") == 0) { - xfree(s->term); - s->term = NULL; - } - - /* Allocate a pty and open it. */ - debug("Allocating pty."); - if (!pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, sizeof(s->tty))) { - if (s->term) - xfree(s->term); - s->term = NULL; - s->ptyfd = -1; - s->ttyfd = -1; - error("session_pty_req: session %d alloc failed", s->self); - return 0; - } - debug("session_pty_req: session %d alloc %s", s->self, s->tty); - - /* for SSH1 the tty modes length is not given */ - if (!compat20) - n_bytes = packet_remaining(); - tty_parse_modes(s->ttyfd, &n_bytes); - - /* - * Add a cleanup function to clear the utmp entry and record logout - * time in case we call fatal() (e.g., the connection gets closed). - */ - fatal_add_cleanup(session_pty_cleanup, (void *)s); - pty_setowner(s->pw, s->tty); - - /* Set window size from the packet. */ - pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel); - - packet_check_eom(); - session_proctitle(s); - return 1; -} - -static int -session_subsystem_req(Session *s) -{ - struct stat st; - u_int len; - int success = 0; - char *prog, *cmd, *subsys = packet_get_string(&len); - u_int i; - - packet_check_eom(); - log("subsystem request for %.100s", subsys); - - for (i = 0; i < options.num_subsystems; i++) { - if (strcmp(subsys, options.subsystem_name[i]) == 0) { - prog = options.subsystem_command[i]; - cmd = options.subsystem_args[i]; - if (strcmp(INTERNAL_SFTP_NAME, prog) == 0) { - s->is_subsystem = SUBSYSTEM_INT_SFTP; - /* - * We must stat(2) the subsystem before we chroot in - * order to be able to send a proper error message. - */ - } else if (chroot_requested(options.chroot_directory)) { - char chdirsub[MAXPATHLEN]; - - strlcpy(chdirsub, options.chroot_directory, - sizeof (chdirsub)); - strlcat(chdirsub, "/", sizeof (chdirsub)); - strlcat(chdirsub, prog, sizeof (chdirsub)); - if (stat(chdirsub, &st) < 0) { - error("subsystem: cannot stat %s under " - "chroot directory %s: %s", prog, - options.chroot_directory, - strerror(errno)); - if (strcmp(subsys, "sftp") == 0) - error("subsystem: please see " - "the Subsystem option in " - "sshd_config(4) for an " - "explanation of '%s'.", - INTERNAL_SFTP_NAME); - break; - } - } else if (stat(prog, &st) < 0) { - error("subsystem: cannot stat %s: %s", prog, - strerror(errno)); - break; - } else { - s->is_subsystem = SUBSYSTEM_EXT; - } - debug("subsystem: exec() %s", cmd); - do_exec(s, cmd); - success = 1; - break; - } - } - - if (!success) - log("subsystem request for %.100s failed, subsystem not found", - subsys); - - xfree(subsys); - return success; -} - -/* - * Serve "x11-req" channel request for X11 forwarding for the current session - * channel. - */ -static int -session_x11_req(Session *s) -{ - int success, fd; - char xauthdir[] = "/tmp/ssh-xauth-XXXXXX"; - - s->single_connection = packet_get_char(); - s->auth_proto = packet_get_string(NULL); - s->auth_data = packet_get_string(NULL); - s->screen = packet_get_int(); - packet_check_eom(); - - success = session_setup_x11fwd(s); - if (!success) { - xfree(s->auth_proto); - xfree(s->auth_data); - s->auth_proto = NULL; - s->auth_data = NULL; - return (success); - } - - /* - * Create per session X authority file so that different sessions - * don't contend for one common file. The reason for this is that - * xauth(1) locking doesn't work too well over network filesystems. - * - * If mkdtemp() or open() fails then s->auth_file remains NULL which - * means that we won't set XAUTHORITY variable in child's environment - * and xauth(1) will use the default location for the authority file. - */ - if (mkdtemp(xauthdir) != NULL) { - s->auth_file = xmalloc(MAXPATHLEN); - snprintf(s->auth_file, MAXPATHLEN, "%s/xauthfile", - xauthdir); - /* - * we don't want that "creating new authority file" message to - * be printed by xauth(1) so we must create that file - * beforehand. - */ - if ((fd = open(s->auth_file, O_CREAT | O_EXCL | O_RDONLY, - S_IRUSR | S_IWUSR)) == -1) { - error("failed to create the temporary X authority " - "file %s: %.100s; will use the default one", - s->auth_file, strerror(errno)); - xfree(s->auth_file); - s->auth_file = NULL; - if (rmdir(xauthdir) == -1) { - error("cannot remove xauth directory %s: %.100s", - xauthdir, strerror(errno)); - } - } else { - close(fd); - debug("temporary X authority file %s created", - s->auth_file); - - /* - * add a cleanup function to remove the temporary - * xauth file in case we call fatal() (e.g., the - * connection gets closed). - */ - fatal_add_cleanup(session_xauthfile_cleanup, (void *)s); - } - } - else { - error("failed to create a directory for the temporary X " - "authority file: %.100s; will use the default xauth file", - strerror(errno)); - } - - return (success); -} - -static int -session_shell_req(Session *s) -{ - packet_check_eom(); - do_exec(s, NULL); - return 1; -} - -static int -session_exec_req(Session *s) -{ - u_int len; - char *command = packet_get_string(&len); - packet_check_eom(); - do_exec(s, command); - xfree(command); - return 1; -} - -static int -session_auth_agent_req(Session *s) -{ - static int called = 0; - packet_check_eom(); - if (no_agent_forwarding_flag) { - debug("session_auth_agent_req: no_agent_forwarding_flag"); - return 0; - } - if (called) { - return 0; - } else { - called = 1; - return auth_input_request_forwarding(s->pw); - } -} - -static int -session_loc_env_check(char *var, char *val) -{ - char *current; - int cat, ret; - - if (strcmp(var, "LANG") == 0) - cat = LC_ALL; - else if (strcmp(var, "LC_ALL") == 0) - cat = LC_ALL; - else if (strcmp(var, "LC_CTYPE") == 0) - cat = LC_CTYPE; - else if (strcmp(var, "LC_COLLATE") == 0) - cat = LC_COLLATE; - else if (strcmp(var, "LC_TIME") == 0) - cat = LC_TIME; - else if (strcmp(var, "LC_NUMERIC") == 0) - cat = LC_NUMERIC; - else if (strcmp(var, "LC_MONETARY") == 0) - cat = LC_MONETARY; - else if (strcmp(var, "LC_MESSAGES") == 0) - cat = LC_MESSAGES; - - current = setlocale(cat, NULL); - - ret = (setlocale(cat, val) != NULL); - (void) setlocale(cat, current); - return (ret); -} - -static int -session_env_req(Session *s) -{ - Channel *c; - char *var, *val, *e; - char **p; - size_t len; - int ret = 0; - - /* Get var/val from the rest of this packet */ - var = packet_get_string(NULL); - val = packet_get_string(NULL); - - /* - * We'll need the channel ID for the packet_send_debug messages, - * so get it now. - */ - if ((c = channel_lookup(s->chanid)) == NULL) - goto done; /* shouldn't happen! */ - - debug2("Received request for environment variable %s=%s", var, val); - - /* For now allow only LANG and LC_* */ - if (strcmp(var, "LANG") != 0 && strncmp(var, "LC_", 3) != 0) { - debug2("Rejecting request for environment variable %s", var); - goto done; - } - - if (!session_loc_env_check(var, val)) { - packet_send_debug(gettext("Missing locale support for %s=%s"), - var, val); - goto done; - } - - packet_send_debug(gettext("Channel %d set: %s=%s"), c->remote_id, - var, val); - - /* - * Always append new environment variables without regard to old - * ones being overriden. The way these are actually added to - * the environment of the session process later settings - * override earlier ones; see copy_environment(). - */ - if (s->env == NULL) { - char **env; - - env = xmalloc(sizeof (char **) * 2); - memset(env, 0, sizeof (char **) * 2); - - s->env = env; - p = env; - } else { - for (p = s->env; *p != NULL ; p++); - - s->env = xrealloc(s->env, (p - s->env + 2) * sizeof (char **)); - - for (p = s->env; *p != NULL ; p++); - } - - len = snprintf(NULL, 0, "%s=%s", var, val); - e = xmalloc(len + 1); - (void) snprintf(e, len + 1, "%s=%s", var, val); - - (*p++) = e; - *p = NULL; - - ret = 1; - -done: - xfree(var); - xfree(val); - - return (ret); -} - -static void -session_free_env(char ***envp) -{ - char **env, **p; - - if (envp == NULL || *envp == NULL) - return; - - env = *envp; - - *envp = NULL; - - for (p = env; *p != NULL; p++) - xfree(*p); - - xfree(env); -} - -int -session_input_channel_req(Channel *c, const char *rtype) -{ - int success = 0; - Session *s; - - if ((s = session_by_channel(c->self)) == NULL) { - log("session_input_channel_req: no session %d req %.100s", - c->self, rtype); - return 0; - } - debug("session_input_channel_req: session %d req %s", s->self, rtype); - - /* - * a session is in LARVAL state until a shell, a command - * or a subsystem is executed - */ - if (c->type == SSH_CHANNEL_LARVAL) { - if (strcmp(rtype, "shell") == 0) { - success = session_shell_req(s); - } else if (strcmp(rtype, "exec") == 0) { - success = session_exec_req(s); - } else if (strcmp(rtype, "pty-req") == 0) { - success = session_pty_req(s); - } else if (strcmp(rtype, "x11-req") == 0) { - success = session_x11_req(s); - } else if (strcmp(rtype, "auth-agent-req@openssh.com") == 0) { - success = session_auth_agent_req(s); - } else if (strcmp(rtype, "subsystem") == 0) { - success = session_subsystem_req(s); - } else if (strcmp(rtype, "env") == 0) { - success = session_env_req(s); - } - } - if (strcmp(rtype, "window-change") == 0) { - success = session_window_change_req(s); - } - return success; -} - -void -session_set_fds(Session *s, int fdin, int fdout, int fderr) -{ - if (!compat20) - fatal("session_set_fds: called for proto != 2.0"); - /* - * now that have a child and a pipe to the child, - * we can activate our channel and register the fd's - */ - if (s->chanid == -1) - fatal("no channel for session %d", s->self); - channel_set_fds(s->chanid, - fdout, fdin, fderr, - fderr == -1 ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ, - 1, - CHAN_SES_WINDOW_DEFAULT); -} - -/* - * Function to perform pty cleanup. Also called if we get aborted abnormally - * (e.g., due to a dropped connection). - */ -void -session_pty_cleanup2(void *session) -{ - Session *s = session; - - if (s == NULL) { - error("session_pty_cleanup: no session"); - return; - } - if (s->ttyfd == -1) - return; - - debug("session_pty_cleanup: session %d release %s", s->self, s->tty); - -#ifdef USE_PAM - session_do_pam(s, 0); -#endif /* USE_PAM */ - - /* Record that the user has logged out. */ - if (s->pid != 0) { - debug3("Recording SSHv2 channel logout in utmpx/wtmpx"); -#ifdef ALTPRIVSEP - altprivsep_record_logout(s->pid); -#endif /* ALTPRIVSEP */ - } - - /* Release the pseudo-tty. */ - if (getuid() == 0) - pty_release(s->tty); - - /* - * Close the server side of the socket pairs. We must do this after - * the pty cleanup, so that another process doesn't get this pty - * while we're still cleaning up. - */ - if (close(s->ptymaster) < 0) - error("close(s->ptymaster/%d): %s", s->ptymaster, strerror(errno)); - - /* unlink pty from session */ - s->ttyfd = -1; -} - -void -session_pty_cleanup(void *session) -{ - session_pty_cleanup2(session); -} - -/* - * We use a different temporary X authority file per every session so we - * should remove those files when fatal() is called. - */ -void -session_xauthfile_cleanup(void *session) -{ - Session *s = session; - - if (s == NULL) { - error("session_xauthfile_cleanup: no session"); - return; - } - - debug("session_xauthfile_cleanup: session %d removing %s", s->self, - s->auth_file); - - if (unlink(s->auth_file) == -1) { - error("session_xauthfile_cleanup: cannot remove xauth file: " - "%.100s", strerror(errno)); - return; - } - - /* dirname() will modify s->auth_file but that's ok */ - if (rmdir(dirname(s->auth_file)) == -1) { - error("session_xauthfile_cleanup: " - "cannot remove xauth directory: %.100s", strerror(errno)); - return; - } -} - -static char * -sig2name(int sig) -{ -#define SSH_SIG(x) if (sig == SIG ## x) return #x - SSH_SIG(ABRT); - SSH_SIG(ALRM); - SSH_SIG(FPE); - SSH_SIG(HUP); - SSH_SIG(ILL); - SSH_SIG(INT); - SSH_SIG(KILL); - SSH_SIG(PIPE); - SSH_SIG(QUIT); - SSH_SIG(SEGV); - SSH_SIG(TERM); - SSH_SIG(USR1); - SSH_SIG(USR2); -#undef SSH_SIG - return "SIG@openssh.com"; -} - -static void -session_exit_message(Session *s, int status) -{ - Channel *c; - - if ((c = channel_lookup(s->chanid)) == NULL) - fatal("session_exit_message: session %d: no channel %d", - s->self, s->chanid); - debug("session_exit_message: session %d channel %d pid %ld", - s->self, s->chanid, (long)s->pid); - - if (WIFEXITED(status)) { - channel_request_start(s->chanid, "exit-status", 0); - packet_put_int(WEXITSTATUS(status)); - packet_send(); - } else if (WIFSIGNALED(status)) { - channel_request_start(s->chanid, "exit-signal", 0); - packet_put_cstring(sig2name(WTERMSIG(status))); -#ifdef WCOREDUMP - packet_put_char(WCOREDUMP(status)); -#else /* WCOREDUMP */ - packet_put_char(0); -#endif /* WCOREDUMP */ - packet_put_cstring(""); - packet_put_cstring(""); - packet_send(); - } else { - /* Some weird exit cause. Just exit. */ - packet_disconnect("wait returned status %04x.", status); - } - - /* Ok to close channel now */ - channel_set_wait_for_exit(s->chanid, 0); - - /* disconnect channel */ - debug("session_exit_message: release channel %d", s->chanid); - channel_cancel_cleanup(s->chanid); - /* - * emulate a write failure with 'chan_write_failed', nobody will be - * interested in data we write. - * Note that we must not call 'chan_read_failed', since there could - * be some more data waiting in the pipe. - */ - if (c->ostate != CHAN_OUTPUT_CLOSED) - chan_write_failed(c); - s->chanid = -1; -} - -void -session_close(Session *s) -{ - debug("session_close: session %d pid %ld", s->self, (long)s->pid); - if (s->ttyfd != -1) { - fatal_remove_cleanup(session_pty_cleanup, (void *)s); - session_pty_cleanup(s); - } - if (s->auth_file != NULL) { - fatal_remove_cleanup(session_xauthfile_cleanup, (void *)s); - session_xauthfile_cleanup(s); - xfree(s->auth_file); - } - if (s->term) - xfree(s->term); - if (s->display) - xfree(s->display); - if (s->auth_display) - xfree(s->auth_display); - if (s->auth_data) - xfree(s->auth_data); - if (s->auth_proto) - xfree(s->auth_proto); - if (s->command) - xfree(s->command); - session_free_env(&s->env); - s->used = 0; - session_proctitle(s); -} - -void -session_close_by_pid(pid_t pid, int status) -{ - Session *s = session_by_pid(pid); - if (s == NULL) { - debug("session_close_by_pid: no session for pid %ld", - (long)pid); - return; - } - if (s->chanid != -1) - session_exit_message(s, status); - session_close(s); -} - -/* - * This is called when a channel dies before the session 'child' itself dies. - * It can happen for example if we exit from an interactive shell before we - * exit from forwarded X11 applications. - */ -void -session_close_by_channel(int id, void *arg) -{ - Session *s = session_by_channel(id); - if (s == NULL) { - debug("session_close_by_channel: no session for id %d", id); - return; - } - debug("session_close_by_channel: channel %d child %ld", - id, (long)s->pid); - if (s->pid != 0) { - debug("session_close_by_channel: channel %d: has child", id); - /* - * delay detach of session, but release pty, since - * the fd's to the child are already closed - */ - if (s->ttyfd != -1) { - fatal_remove_cleanup(session_pty_cleanup, (void *)s); - session_pty_cleanup(s); - } - return; - } - /* detach by removing callback */ - channel_cancel_cleanup(s->chanid); - s->chanid = -1; - session_close(s); -} - -void -session_destroy_all(void (*closefunc)(Session *)) -{ - int i; - for (i = 0; i < MAX_SESSIONS; i++) { - Session *s = &sessions[i]; - if (s->used) { - if (closefunc != NULL) - closefunc(s); - else - session_close(s); - } - } -} - -static char * -session_tty_list(void) -{ - static char buf[1024]; - int i; - buf[0] = '\0'; - for (i = 0; i < MAX_SESSIONS; i++) { - Session *s = &sessions[i]; - if (s->used && s->ttyfd != -1) { - if (buf[0] != '\0') - strlcat(buf, ",", sizeof buf); - strlcat(buf, strrchr(s->tty, '/') + 1, sizeof buf); - } - } - if (buf[0] == '\0') - strlcpy(buf, "notty", sizeof buf); - return buf; -} - -void -session_proctitle(Session *s) -{ - if (s->pw == NULL) - error("no user for session %d", s->self); - else - setproctitle("%s@%s", s->pw->pw_name, session_tty_list()); -} - -int -session_setup_x11fwd(Session *s) -{ - struct stat st; - char display[512], auth_display[512]; - char hostname[MAXHOSTNAMELEN]; - - if (no_x11_forwarding_flag) { - packet_send_debug("X11 forwarding disabled in user configuration file."); - return 0; - } - if (!options.x11_forwarding) { - debug("X11 forwarding disabled in server configuration file."); - return 0; - } - if (!options.xauth_location || - (stat(options.xauth_location, &st) == -1)) { - packet_send_debug("No xauth program; cannot forward with spoofing."); - return 0; - } - if (s->display != NULL) { - debug("X11 display already set."); - return 0; - } - if (x11_create_display_inet(options.x11_display_offset, - options.x11_use_localhost, s->single_connection, - &s->display_number) == -1) { - debug("x11_create_display_inet failed."); - return 0; - } - - /* Set up a suitable value for the DISPLAY variable. */ - if (gethostname(hostname, sizeof(hostname)) < 0) - fatal("gethostname: %.100s", strerror(errno)); - /* - * auth_display must be used as the displayname when the - * authorization entry is added with xauth(1). This will be - * different than the DISPLAY string for localhost displays. - */ - if (options.x11_use_localhost) { - snprintf(display, sizeof display, "localhost:%u.%u", - s->display_number, s->screen); - snprintf(auth_display, sizeof auth_display, "unix:%u.%u", - s->display_number, s->screen); - s->display = xstrdup(display); - s->auth_display = xstrdup(auth_display); - } else { -#ifdef IPADDR_IN_DISPLAY - struct hostent *he; - struct in_addr my_addr; - - he = gethostbyname(hostname); - if (he == NULL) { - error("Can't get IP address for X11 DISPLAY."); - packet_send_debug("Can't get IP address for X11 DISPLAY."); - return 0; - } - memcpy(&my_addr, he->h_addr_list[0], sizeof(struct in_addr)); - snprintf(display, sizeof display, "%.50s:%u.%u", inet_ntoa(my_addr), - s->display_number, s->screen); -#else - snprintf(display, sizeof display, "%.400s:%u.%u", hostname, - s->display_number, s->screen); -#endif - s->display = xstrdup(display); - s->auth_display = xstrdup(display); - } - - return 1; -} - -#ifdef USE_PAM -int session_do_pam_conv(int, struct pam_message **, - struct pam_response **, void *); - -static struct pam_conv session_pam_conv = { - session_do_pam_conv, - NULL -}; - -static void -session_do_pam(Session *s, int do_open) -{ - int pam_retval; - char *where, *old_tty, *old_tty_copy = NULL; - struct pam_conv old_conv, *old_conv_ptr; - - if (!s || !s->authctxt || !s->authctxt->pam || !s->authctxt->pam->h) - return; - - /* Save current PAM item values */ - where = "getting PAM_CONV"; - pam_retval = pam_get_item(s->authctxt->pam->h, PAM_CONV, - (void **) &old_conv_ptr); - if (pam_retval != PAM_SUCCESS) - goto done; - old_conv = *old_conv_ptr; - - where = "getting PAM_TTY"; - pam_retval = pam_get_item(s->authctxt->pam->h, PAM_TTY, - (void **) &old_tty); - if (pam_retval != PAM_SUCCESS) - goto done; - old_tty_copy = xstrdup(old_tty); - - /* Change PAM_TTY and PAM_CONV items */ - where = "setting PAM_TTY"; - pam_retval = pam_set_item(s->authctxt->pam->h, PAM_TTY, s->tty); - if (pam_retval != PAM_SUCCESS) - goto done; - - where = "setting PAM_CONV"; - session_pam_conv.appdata_ptr = s; - pam_retval = pam_set_item(s->authctxt->pam->h, - PAM_CONV, &session_pam_conv); - if (pam_retval != PAM_SUCCESS) - goto done; - - /* Call pam_open/close_session() */ - if (do_open) { - where = "calling pam_open_session()"; - pam_retval = pam_open_session(s->authctxt->pam->h, 0); - } - else { - where = "calling pam_close_session()"; - pam_retval = pam_close_session(s->authctxt->pam->h, 0); - } - - /* Reset PAM_TTY and PAM_CONV items to previous values */ - where = "setting PAM_TTY"; - pam_retval = pam_set_item(s->authctxt->pam->h, PAM_TTY, old_tty_copy); - if (pam_retval != PAM_SUCCESS) - goto done; - - where = "setting PAM_CONV"; - pam_retval = pam_set_item(s->authctxt->pam->h, PAM_CONV, &old_conv); - if (pam_retval != PAM_SUCCESS) - goto done; - - session_pam_conv.appdata_ptr = NULL; - -done: - if (old_tty_copy) - xfree(old_tty_copy); - - if (pam_retval == PAM_SUCCESS) - return; - - /* fatal()? probably not... */ - log("PAM failed[%d] while %s: %s", pam_retval, where, - PAM_STRERROR(s->authctxt->pam->h, pam_retval)); -} - -int -session_do_pam_conv(int num_prompts, - struct pam_message **prompts, - struct pam_response **resp, - void *app_data) -{ - Session *s = (Session *) app_data; - - struct pam_response *reply; - int count; - char *prompt; - - if (channel_lookup(s->chanid) == NULL) - return PAM_CONV_ERR; - - /* PAM will free this later */ - reply = xmalloc(num_prompts * sizeof(*reply)); - - (void) memset(reply, 0, num_prompts * sizeof(*reply)); - for (count = 0; count < num_prompts; count++) { - switch(PAM_MSG_MEMBER(prompts, count, msg_style)) { - case PAM_TEXT_INFO: - /* Write to stdout of channel */ - prompt = PAM_MSG_MEMBER(prompts, count, msg); - if (prompt != NULL && s->ttyfd != -1) { - debug2("session_do_pam_conv: text info " - "prompt: %s", prompt); - (void) write(s->ttyfd, prompt, strlen(prompt)); - (void) write(s->ttyfd, "\n", 1); - } - reply[count].resp = xstrdup(""); - reply[count].resp_retcode = PAM_SUCCESS; - break; - case PAM_ERROR_MSG: - /* Write to stderr of channel */ - prompt = PAM_MSG_MEMBER(prompts, count, msg); - if (prompt != NULL && s->ttyfd != -1) { - debug2("session_do_pam_conv: error " - "prompt: %s", prompt); - (void) write(s->ttyfd, prompt, strlen(prompt)); - (void) write(s->ttyfd, "\n", 1); - } - reply[count].resp = xstrdup(""); - reply[count].resp_retcode = PAM_SUCCESS; - break; - case PAM_PROMPT_ECHO_ON: - case PAM_PROMPT_ECHO_OFF: - /* - * XXX Someday add support for echo on/off prompts - * here on sessions with ttys. - */ - default: - xfree(reply); - return PAM_CONV_ERR; - } - } - - *resp = reply; - - return PAM_SUCCESS; -} -#endif /* USE_PAM */ - -static void -do_authenticated2(Authctxt *authctxt) -{ - server_loop2(authctxt); -} - -/* - * Drop the privileges. We need this for the in-process SFTP server only. For - * the shell and the external subsystem the exec(2) call will do the P = E = I - * assignment itself. Never change the privileges if the connecting user is - * root. See privileges(5) if the terminology used here is not known to you. - */ -static void -drop_privs(uid_t uid) -{ - priv_set_t *priv_inherit; - - /* If root is connecting we are done. */ - if (uid == 0) - return; - - if ((priv_inherit = priv_allocset()) == NULL) - fatal("priv_allocset: %s", strerror(errno)); - if (getppriv(PRIV_INHERITABLE, priv_inherit) != 0) - fatal("getppriv: %s", strerror(errno)); - - /* - * This will limit E as well. Note that before this P was a - * superset of I, see permanently_set_uid(). - */ - if (setppriv(PRIV_SET, PRIV_PERMITTED, priv_inherit) == -1) - fatal("setppriv: %s", strerror(errno)); - - priv_freeset(priv_inherit); - - /* - * By manipulating the P set above we entered a PA mode which we - * do not need to retain in. - */ - if (setpflags(PRIV_AWARE, 0) == -1) - fatal("setpflags: %s", strerror(errno)); -} diff --git a/usr/src/cmd/ssh/sshd/sshd.c b/usr/src/cmd/ssh/sshd/sshd.c deleted file mode 100644 index 3be0890a8c..0000000000 --- a/usr/src/cmd/ssh/sshd/sshd.c +++ /dev/null @@ -1,2051 +0,0 @@ -/* - * Author: Tatu Ylonen <ylo@cs.hut.fi> - * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland - * All rights reserved - * This program is the ssh daemon. It listens for connections from clients, - * and performs authentication, executes use commands or shell, and forwards - * information to/from the application to the user client over an encrypted - * connection. This can also handle forwarding of X11, TCP/IP, and - * authentication agent connections. - * - * As far as I am concerned, the code I have written for this software - * can be used freely for any purpose. Any derived versions of this - * software must be clearly marked as such, and if the derived work is - * incompatible with the protocol description in the RFC file, it must be - * called by a name other than "ssh" or "Secure Shell". - * - * SSH2 implementation: - * Privilege Separation: - * - * Copyright (c) 2000, 2001, 2002 Markus Friedl. All rights reserved. - * Copyright (c) 2002 Niels Provos. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -/* - * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. - */ - -#include "includes.h" -RCSID("$OpenBSD: sshd.c,v 1.260 2002/09/27 10:42:09 mickey Exp $"); - -#include <openssl/dh.h> -#include <openssl/bn.h> -#include <openssl/md5.h> - -#include <openssl/rand.h> - -#include "ssh.h" -#include "ssh1.h" -#include "ssh2.h" -#include "xmalloc.h" -#include "rsa.h" -#include "sshpty.h" -#include "packet.h" -#include "mpaux.h" -#include "log.h" -#include "servconf.h" -#include "uidswap.h" -#include "compat.h" -#include "buffer.h" -#include "cipher.h" -#include "kex.h" -#include "key.h" -#include "dh.h" -#include "myproposal.h" -#include "authfile.h" -#include "pathnames.h" -#include "atomicio.h" -#include "canohost.h" -#include "auth.h" -#include "misc.h" -#include "dispatch.h" -#include "channels.h" -#include "session.h" -#include "g11n.h" -#include "sshlogin.h" -#include "xlist.h" -#include "engine.h" - -#ifdef HAVE_BSM -#include "bsmaudit.h" -#endif /* HAVE_BSM */ - -#ifdef ALTPRIVSEP -#include "altprivsep.h" -#endif /* ALTPRIVSEP */ - -#ifdef HAVE_SOLARIS_CONTRACTS -#include <sys/ctfs.h> -#include <sys/contract.h> -#include <sys/contract/process.h> -#include <libcontract.h> -#endif /* HAVE_SOLARIS_CONTRACTS */ - -#ifdef GSSAPI -#include "ssh-gss.h" -#endif /* GSSAPI */ - -#ifdef LIBWRAP -#include <tcpd.h> -#include <syslog.h> -#ifndef lint -int allow_severity = LOG_INFO; -int deny_severity = LOG_WARNING; -#endif /* lint */ -#endif /* LIBWRAP */ - -#ifndef O_NOCTTY -#define O_NOCTTY 0 -#endif - -#ifdef HAVE___PROGNAME -extern char *__progname; -#else -char *__progname; -#endif - -/* Server configuration options. */ -ServerOptions options; - -/* Name of the server configuration file. */ -static char *config_file_name = _PATH_SERVER_CONFIG_FILE; - -/* - * Flag indicating whether IPv4 or IPv6. This can be set on the command line. - * Default value is AF_UNSPEC means both IPv4 and IPv6. - */ -#ifdef IPV4_DEFAULT -int IPv4or6 = AF_INET; -#else -int IPv4or6 = AF_UNSPEC; -#endif - -/* - * Debug mode flag. This can be set on the command line. If debug - * mode is enabled, extra debugging output will be sent to the system - * log, the daemon will not go to background, and will exit after processing - * the first connection. - */ -int debug_flag = 0; - -/* Flag indicating that the daemon should only test the configuration and keys. */ -static int test_flag = 0; - -/* Flag indicating that the daemon is being started from inetd. */ -static int inetd_flag = 0; - -/* Flag indicating that sshd should not detach and become a daemon. */ -static int no_daemon_flag = 0; - -/* debug goes to stderr unless inetd_flag is set */ -int log_stderr = 0; - -/* Saved arguments to main(). */ -static char **saved_argv; -static int saved_argc; - -/* - * The sockets that the server is listening; this is used in the SIGHUP - * signal handler. - */ -#define MAX_LISTEN_SOCKS 16 -static int listen_socks[MAX_LISTEN_SOCKS]; -static int num_listen_socks = 0; - -/* - * the client's version string, passed by sshd2 in compat mode. if != NULL, - * sshd will skip the version-number exchange - */ -static char *client_version_string = NULL; -static char *server_version_string = NULL; - -/* for rekeying XXX fixme */ -Kex *xxx_kex; - -/* - * Any really sensitive data in the application is contained in this - * structure. The idea is that this structure could be locked into memory so - * that the pages do not get written into swap. However, there are some - * problems. The private key contains BIGNUMs, and we do not (in principle) - * have access to the internals of them, and locking just the structure is - * not very useful. Currently, memory locking is not implemented. - */ -static struct { - Key *server_key; /* ephemeral server key */ - Key *ssh1_host_key; /* ssh1 host key */ - Key **host_keys; /* all private host keys */ - int have_ssh1_key; - int have_ssh2_key; - u_char ssh1_cookie[SSH_SESSION_KEY_LENGTH]; -} sensitive_data; - -/* - * Flag indicating whether the RSA server key needs to be regenerated. - * Is set in the SIGALRM handler and cleared when the key is regenerated. - */ -static volatile sig_atomic_t key_do_regen = 0; - -/* This is set to true when a signal is received. */ -static volatile sig_atomic_t received_sighup = 0; -static volatile sig_atomic_t received_sigterm = 0; - -/* session identifier, used by RSA-auth */ -u_char session_id[16]; - -/* same for ssh2 */ -u_char *session_id2 = NULL; -int session_id2_len = 0; - -/* record remote hostname or ip */ -u_int utmp_len = MAXHOSTNAMELEN; - -/* options.max_startup sized array of fd ints */ -static int *startup_pipes = NULL; -static int startup_pipe = -1; /* in child */ - -/* sshd_config buffer */ -Buffer cfg; - -#ifdef GSSAPI -static gss_OID_set mechs = GSS_C_NULL_OID_SET; -#endif /* GSSAPI */ - -/* Prototypes for various functions defined later in this file. */ -void destroy_sensitive_data(void); -static void demote_sensitive_data(void); - -static void do_ssh1_kex(void); -static void do_ssh2_kex(void); - -/* - * Close all listening sockets - */ -static void -close_listen_socks(void) -{ - int i; - - for (i = 0; i < num_listen_socks; i++) - (void) close(listen_socks[i]); - num_listen_socks = -1; -} - -static void -close_startup_pipes(void) -{ - int i; - - if (startup_pipes) - for (i = 0; i < options.max_startups; i++) - if (startup_pipes[i] != -1) - (void) close(startup_pipes[i]); -} - -/* - * Signal handler for SIGHUP. Sshd execs itself when it receives SIGHUP; - * the effect is to reread the configuration file (and to regenerate - * the server key). - */ -static void -sighup_handler(int sig) -{ - int save_errno = errno; - - received_sighup = 1; - (void) signal(SIGHUP, sighup_handler); - errno = save_errno; -} - -/* - * Called from the main program after receiving SIGHUP. - * Restarts the server. - */ -static void -sighup_restart(void) -{ - log("Received SIGHUP; restarting."); - close_listen_socks(); - close_startup_pipes(); - (void) execv(saved_argv[0], saved_argv); - log("RESTART FAILED: av[0]='%.100s', error: %.100s.", saved_argv[0], - strerror(errno)); - exit(1); -} - -/* - * Generic signal handler for terminating signals in the master daemon. - */ -static void -sigterm_handler(int sig) -{ - received_sigterm = sig; -} - -/* - * SIGCHLD handler. This is called whenever a child dies. This will then - * reap any zombies left by exited children. - */ -static void -main_sigchld_handler(int sig) -{ - int save_errno = errno; - pid_t pid; - int status; - - while ((pid = waitpid(-1, &status, WNOHANG)) > 0 || - (pid < 0 && errno == EINTR)) - ; - - (void) signal(SIGCHLD, main_sigchld_handler); - errno = save_errno; -} - -/* - * Signal handler for the alarm after the login grace period has expired. This - * is for the (soon-to-be) unprivileged child only. The monitor gets an event on - * the communication pipe and exits as well. - */ -static void -grace_alarm_handler(int sig) -{ - /* Log error and exit. */ - fatal("Timeout before authentication for %.200s", get_remote_ipaddr()); -} - -#ifdef HAVE_SOLARIS_CONTRACTS -static int contracts_fd = -1; -void -contracts_pre_fork() -{ - const char *during = "opening process contract template"; - - /* - * Failure should not be treated as fatal on the theory that - * it's better to start with children in the same contract as - * the master listener than not at all. - */ - - if (contracts_fd == -1) { - if ((contracts_fd = open64(CTFS_ROOT "/process/template", - O_RDWR)) == -1) - goto cleanup; - - during = "setting sundry contract terms"; - if ((errno = ct_pr_tmpl_set_param(contracts_fd, CT_PR_PGRPONLY))) - goto cleanup; - - if ((errno = ct_tmpl_set_informative(contracts_fd, CT_PR_EV_HWERR))) - goto cleanup; - - if ((errno = ct_pr_tmpl_set_fatal(contracts_fd, CT_PR_EV_HWERR))) - goto cleanup; - - if ((errno = ct_tmpl_set_critical(contracts_fd, 0))) - goto cleanup; - } - - during = "setting active template"; - if ((errno = ct_tmpl_activate(contracts_fd))) - goto cleanup; - - debug3("Set active contract"); - return; - -cleanup: - if (contracts_fd != -1) - (void) close(contracts_fd); - - contracts_fd = -1; - - if (errno) - debug2("Error while trying to set up active contract" - " template: %s while %s", strerror(errno), during); -} - -void -contracts_post_fork_child() -{ - /* Clear active template so fork() creates no new contracts. */ - - if (contracts_fd == -1) - return; - - if ((errno = (ct_tmpl_clear(contracts_fd)))) - debug2("Error while trying to clear active contract template" - " (child): %s", strerror(errno)); - else - debug3("Cleared active contract template (child)"); - - (void) close(contracts_fd); - - contracts_fd = -1; -} - -void -contracts_post_fork_parent(int fork_succeeded) -{ - char path[PATH_MAX]; - int cfd, n; - ct_stathdl_t st; - ctid_t latest; - - /* Clear active template, abandon latest contract. */ - if (contracts_fd == -1) - return; - - if ((errno = ct_tmpl_clear(contracts_fd))) - debug2("Error while clearing active contract template: %s", - strerror(errno)); - else - debug3("Cleared active contract template (parent)"); - - if (!fork_succeeded) - return; - - if ((cfd = open64(CTFS_ROOT "/process/latest", O_RDONLY)) == -1) { - debug2("Error while getting latest contract: %s", - strerror(errno)); - return; - } - - if ((errno = ct_status_read(cfd, CTD_COMMON, &st)) != 0) { - debug2("Error while getting latest contract ID: %s", - strerror(errno)); - (void) close(cfd); - return; - } - - latest = ct_status_get_id(st); - ct_status_free(st); - (void) close(cfd); - - n = snprintf(path, PATH_MAX, CTFS_ROOT "/all/%ld/ctl", latest); - - if (n >= PATH_MAX) { - debug2("Error while opening the latest contract ctl file: %s", - strerror(ENAMETOOLONG)); - return; - } - - if ((cfd = open64(path, O_WRONLY)) == -1) { - debug2("Error while opening the latest contract ctl file: %s", - strerror(errno)); - return; - } - - if ((errno = ct_ctl_abandon(cfd))) - debug2("Error while abandoning latest contract: %s", - strerror(errno)); - else - debug3("Abandoned latest contract"); - - (void) close(cfd); -} -#endif /* HAVE_SOLARIS_CONTRACTS */ - -/* - * Signal handler for the key regeneration alarm. Note that this - * alarm only occurs in the daemon waiting for connections, and it does not - * do anything with the private key or random state before forking. - * Thus there should be no concurrency control/asynchronous execution - * problems. - */ -static void -generate_ephemeral_server_key(void) -{ - u_int32_t rnd = 0; - int i; - - verbose("Generating %s%d bit RSA key.", - sensitive_data.server_key ? "new " : "", options.server_key_bits); - if (sensitive_data.server_key != NULL) - key_free(sensitive_data.server_key); - sensitive_data.server_key = key_generate(KEY_RSA1, - options.server_key_bits); - verbose("RSA key generation complete."); - - for (i = 0; i < SSH_SESSION_KEY_LENGTH; i++) { - if (i % 4 == 0) - rnd = arc4random(); - sensitive_data.ssh1_cookie[i] = rnd & 0xff; - rnd >>= 8; - } - arc4random_stir(); -} - -static void -key_regeneration_alarm(int sig) -{ - int save_errno = errno; - - (void) signal(SIGALRM, SIG_DFL); - errno = save_errno; - key_do_regen = 1; -} - -static void -sshd_exchange_identification(int sock_in, int sock_out) -{ - int i, mismatch; - int remote_major, remote_minor; - int major, minor; - char *s; - char buf[256]; /* Must not be larger than remote_version. */ - char remote_version[256]; /* Must be at least as big as buf. */ - - if ((options.protocol & SSH_PROTO_1) && - (options.protocol & SSH_PROTO_2)) { - major = PROTOCOL_MAJOR_1; - minor = 99; - } else if (options.protocol & SSH_PROTO_2) { - major = PROTOCOL_MAJOR_2; - minor = PROTOCOL_MINOR_2; - } else { - major = PROTOCOL_MAJOR_1; - minor = PROTOCOL_MINOR_1; - } - (void) snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", major, minor, SSH_VERSION); - server_version_string = xstrdup(buf); - - if (client_version_string == NULL) { - /* Send our protocol version identification. */ - if (atomicio(write, sock_out, server_version_string, - strlen(server_version_string)) - != strlen(server_version_string)) { - log("Could not write ident string to %s", get_remote_ipaddr()); - fatal_cleanup(); - } - - /* Read other sides version identification. */ - (void) memset(buf, 0, sizeof(buf)); - for (i = 0; i < sizeof(buf) - 1; i++) { - if (atomicio(read, sock_in, &buf[i], 1) != 1) { - log("Did not receive identification string from %s", - get_remote_ipaddr()); - fatal_cleanup(); - } - if (buf[i] == '\r') { - buf[i] = 0; - /* Kludge for F-Secure Macintosh < 1.0.2 */ - if (i == 12 && - strncmp(buf, "SSH-1.5-W1.0", 12) == 0) - break; - continue; - } - if (buf[i] == '\n') { - buf[i] = 0; - break; - } - } - buf[sizeof(buf) - 1] = 0; - client_version_string = xstrdup(buf); - } - - /* - * Check that the versions match. In future this might accept - * several versions and set appropriate flags to handle them. - */ - if (sscanf(client_version_string, "SSH-%d.%d-%[^\n]\n", - &remote_major, &remote_minor, remote_version) != 3) { - s = "Protocol mismatch.\n"; - (void) atomicio(write, sock_out, s, strlen(s)); - (void) close(sock_in); - (void) close(sock_out); - log("Bad protocol version identification '%.100s' from %s", - client_version_string, get_remote_ipaddr()); - fatal_cleanup(); - } - debug("Client protocol version %d.%d; client software version %.100s", - remote_major, remote_minor, remote_version); - - compat_datafellows(remote_version); - - if (datafellows & SSH_BUG_PROBE) { - log("probed from %s with %s. Don't panic.", - get_remote_ipaddr(), client_version_string); - fatal_cleanup(); - } - - if (datafellows & SSH_BUG_SCANNER) { - log("scanned from %s with %s. Don't panic.", - get_remote_ipaddr(), client_version_string); - fatal_cleanup(); - } - - mismatch = 0; - switch (remote_major) { - case 1: - if (remote_minor == 99) { - if (options.protocol & SSH_PROTO_2) - enable_compat20(); - else - mismatch = 1; - break; - } - if (!(options.protocol & SSH_PROTO_1)) { - mismatch = 1; - break; - } - if (remote_minor < 3) { - packet_disconnect("Your ssh version is too old and " - "is no longer supported. Please install a newer version."); - } else if (remote_minor == 3) { - /* note that this disables agent-forwarding */ - enable_compat13(); - } - break; - case 2: - if (options.protocol & SSH_PROTO_2) { - enable_compat20(); - break; - } - /* FALLTHROUGH */ - default: - mismatch = 1; - break; - } - chop(server_version_string); - debug("Local version string %.200s", server_version_string); - - if (mismatch) { - s = "Protocol major versions differ.\n"; - (void) atomicio(write, sock_out, s, strlen(s)); - (void) close(sock_in); - (void) close(sock_out); - log("Protocol major versions differ for %s: %.200s vs. %.200s", - get_remote_ipaddr(), - server_version_string, client_version_string); - fatal_cleanup(); - } -} - -/* Destroy the host and server keys. They will no longer be needed. */ -void -destroy_sensitive_data(void) -{ - int i; - - if (sensitive_data.server_key) { - key_free(sensitive_data.server_key); - sensitive_data.server_key = NULL; - } - for (i = 0; i < options.num_host_key_files; i++) { - if (sensitive_data.host_keys[i]) { - key_free(sensitive_data.host_keys[i]); - sensitive_data.host_keys[i] = NULL; - } - } - sensitive_data.ssh1_host_key = NULL; - (void) memset(sensitive_data.ssh1_cookie, 0, SSH_SESSION_KEY_LENGTH); -} - -/* Demote private to public keys for network child */ -static void -demote_sensitive_data(void) -{ - Key *tmp; - int i; - - if (sensitive_data.server_key) { - tmp = key_demote(sensitive_data.server_key); - key_free(sensitive_data.server_key); - sensitive_data.server_key = tmp; - } - - for (i = 0; i < options.num_host_key_files; i++) { - if (sensitive_data.host_keys[i]) { - tmp = key_demote(sensitive_data.host_keys[i]); - key_free(sensitive_data.host_keys[i]); - sensitive_data.host_keys[i] = tmp; - if (tmp->type == KEY_RSA1) - sensitive_data.ssh1_host_key = tmp; - } - } - - /* We do not clear ssh1_host key and cookie. XXX - Okay Niels? */ -} - -static char * -list_hostkey_types(void) -{ - Buffer b; - char *p; - int i; - - buffer_init(&b); - for (i = 0; i < options.num_host_key_files; i++) { - Key *key = sensitive_data.host_keys[i]; - if (key == NULL) - continue; - switch (key->type) { - case KEY_RSA: - case KEY_DSA: - if (buffer_len(&b) > 0) - buffer_append(&b, ",", 1); - p = key_ssh_name(key); - buffer_append(&b, p, strlen(p)); - break; - } - } - buffer_append(&b, "\0", 1); - p = xstrdup(buffer_ptr(&b)); - buffer_free(&b); - debug("list_hostkey_types: %s", p); - return p; -} - -#ifdef lint -static -#endif /* lint */ -Key * -get_hostkey_by_type(int type) -{ - int i; - - for (i = 0; i < options.num_host_key_files; i++) { - Key *key = sensitive_data.host_keys[i]; - if (key != NULL && key->type == type) - return key; - } - return NULL; -} - -#ifdef lint -static -#endif /* lint */ -Key * -get_hostkey_by_index(int ind) -{ - if (ind < 0 || ind >= options.num_host_key_files) - return (NULL); - return (sensitive_data.host_keys[ind]); -} - -#ifdef lint -static -#endif /* lint */ -int -get_hostkey_index(Key *key) -{ - int i; - - for (i = 0; i < options.num_host_key_files; i++) { - if (key == sensitive_data.host_keys[i]) - return (i); - } - return (-1); -} - -/* - * returns 1 if connection should be dropped, 0 otherwise. - * dropping starts at connection #max_startups_begin with a probability - * of (max_startups_rate/100). the probability increases linearly until - * all connections are dropped for startups > max_startups - */ -static int -drop_connection(int startups) -{ - double p, r; - - if (startups < options.max_startups_begin) - return 0; - if (startups >= options.max_startups) - return 1; - if (options.max_startups_rate == 100) - return 1; - - p = 100 - options.max_startups_rate; - p *= startups - options.max_startups_begin; - p /= (double) (options.max_startups - options.max_startups_begin); - p += options.max_startups_rate; - p /= 100.0; - r = arc4random() / (double) UINT_MAX; - - debug("drop_connection: p %g, r %g", p, r); - return (r < p) ? 1 : 0; -} - -static void -usage(void) -{ - (void) fprintf(stderr, gettext("sshd version %s\n"), SSH_VERSION); - (void) fprintf(stderr, - gettext("Usage: %s [options]\n" - "Options:\n" - " -f file Configuration file (default %s)\n" - " -d Debugging mode (multiple -d means more " - "debugging)\n" - " -i Started from inetd\n" - " -D Do not fork into daemon mode\n" - " -t Only test configuration file and keys\n" - " -q Quiet (no logging)\n" - " -p port Listen on the specified port (default: 22)\n" - " -k seconds Regenerate server key every this many seconds " - "(default: 3600)\n" - " -g seconds Grace period for authentication (default: 600)\n" - " -b bits Size of server RSA key (default: 768 bits)\n" - " -h file File from which to read host key (default: %s)\n" - " -4 Use IPv4 only\n" - " -6 Use IPv6 only\n" - " -o option Process the option as if it was read from " - "a configuration file.\n"), - __progname, _PATH_SERVER_CONFIG_FILE, _PATH_HOST_KEY_FILE); - exit(1); -} - -/* - * Main program for the daemon. - */ -int -main(int ac, char **av) -{ - extern char *optarg; - extern int optind; - int opt, j, i, fdsetsz, sock_in = 0, sock_out = 0, newsock = -1, on = 1; - pid_t pid; - socklen_t fromlen; - fd_set *fdset; - struct sockaddr_storage from; - const char *remote_ip; - int remote_port; - FILE *f; - struct addrinfo *ai; - char ntop[NI_MAXHOST], strport[NI_MAXSERV]; - int listen_sock, maxfd; - int startup_p[2]; - int startups = 0; - Authctxt *authctxt = NULL; - Key *key; - int ret, key_used = 0; -#ifdef HAVE_BSM - au_id_t auid = AU_NOAUDITID; -#endif /* HAVE_BSM */ - int mpipe; - - __progname = get_progname(av[0]); - - (void) g11n_setlocale(LC_ALL, ""); - - init_rng(); - - /* Save argv. */ - saved_argc = ac; - saved_argv = av; - - /* Initialize configuration options to their default values. */ - initialize_server_options(&options); - - /* Parse command-line arguments. */ - while ((opt = getopt(ac, av, "f:p:b:k:h:g:V:u:o:dDeiqtQ46")) != -1) { - switch (opt) { - case '4': - IPv4or6 = AF_INET; - break; - case '6': - IPv4or6 = AF_INET6; - break; - case 'f': - config_file_name = optarg; - break; - case 'd': - if (0 == debug_flag) { - debug_flag = 1; - options.log_level = SYSLOG_LEVEL_DEBUG1; - } else if (options.log_level < SYSLOG_LEVEL_DEBUG3) { - options.log_level++; - } else { - (void) fprintf(stderr, - gettext("Debug level too high.\n")); - exit(1); - } - break; - case 'D': - no_daemon_flag = 1; - break; - case 'e': - log_stderr = 1; - break; - case 'i': - inetd_flag = 1; - break; - case 'Q': - /* ignored */ - break; - case 'q': - options.log_level = SYSLOG_LEVEL_QUIET; - break; - case 'b': - options.server_key_bits = atoi(optarg); - break; - case 'p': - options.ports_from_cmdline = 1; - if (options.num_ports >= MAX_PORTS) { - (void) fprintf(stderr, gettext("too many ports.\n")); - exit(1); - } - options.ports[options.num_ports++] = a2port(optarg); - if (options.ports[options.num_ports-1] == 0) { - (void) fprintf(stderr, gettext("Bad port number.\n")); - exit(1); - } - break; - case 'g': - if ((options.login_grace_time = convtime(optarg)) == -1) { - (void) fprintf(stderr, - gettext("Invalid login grace time.\n")); - exit(1); - } - break; - case 'k': - if ((options.key_regeneration_time = convtime(optarg)) == -1) { - (void) fprintf(stderr, - gettext("Invalid key regeneration " - "interval.\n")); - exit(1); - } - break; - case 'h': - if (options.num_host_key_files >= MAX_HOSTKEYS) { - (void) fprintf(stderr, - gettext("too many host keys.\n")); - exit(1); - } - options.host_key_files[options.num_host_key_files++] = optarg; - break; - case 'V': - client_version_string = optarg; - /* only makes sense with inetd_flag, i.e. no listen() */ - inetd_flag = 1; - break; - case 't': - test_flag = 1; - break; - case 'o': - if (process_server_config_line(&options, optarg, - "command-line", 0, NULL, NULL, NULL, NULL) != 0) - exit(1); - break; - case '?': - default: - usage(); - break; - } - } - - /* - * There is no need to use the PKCS#11 engine in the master SSH process. - */ - SSLeay_add_all_algorithms(); - seed_rng(); - channel_set_af(IPv4or6); - - /* - * Force logging to stderr until we have loaded the private host - * key (unless started from inetd) - */ - log_init(__progname, - options.log_level == SYSLOG_LEVEL_NOT_SET ? - SYSLOG_LEVEL_INFO : options.log_level, - options.log_facility == SYSLOG_FACILITY_NOT_SET ? - SYSLOG_FACILITY_AUTH : options.log_facility, - !inetd_flag); - -#ifdef _UNICOS - /* Cray can define user privs drop all prives now! - * Not needed on PRIV_SU systems! - */ - drop_cray_privs(); -#endif - - /* Fetch our configuration */ - buffer_init(&cfg); - load_server_config(config_file_name, &cfg); - parse_server_config(&options, config_file_name, &cfg, NULL, NULL, NULL); - - /* - * ChallengeResponseAuthentication is deprecated for protocol 2 which is - * the default setting on Solaris. Warn the user about it. Note that - * ChallengeResponseAuthentication is on by default but the option is - * not set until fill_default_server_options() is called. If the option - * is already set now, the user must have set it manually. - */ - if ((options.protocol & SSH_PROTO_2) && - !(options.protocol & SSH_PROTO_1) && - options.challenge_response_authentication != -1) { - log("ChallengeResponseAuthentication has been " - "deprecated for the SSH Protocol 2. You should use " - "KbdInteractiveAuthentication instead (which defaults to " - "\"yes\")."); - } - - /* - * While PAMAuthenticationViaKbdInt was not documented, it was - * previously set in our default sshd_config and also the only way to - * switch off the keyboard-interactive authentication. To maintain - * backward compatibility, if PAMAuthenticationViaKbdInt is manually set - * to "no" and KbdInteractiveAuthentication is not set, switch off the - * keyboard-interactive authentication method as before. As with the - * challenge response auth situation dealt above, we have not called - * fill_default_server_options() yet so if KbdInteractiveAuthentication - * is already set to 1 here the admin must have set it manually and we - * will honour it. - */ - if (options.kbd_interactive_authentication != 1 && - options.pam_authentication_via_kbd_int == 0) { - options.kbd_interactive_authentication = 0; - } - - /* Fill in default values for those options not explicitly set. */ - fill_default_server_options(&options); - - utmp_len = options.lookup_client_hostnames ? utmp_len : 0; - - /* Check that there are no remaining arguments. */ - if (optind < ac) { - (void) fprintf(stderr, gettext("Extra argument %s.\n"), av[optind]); - exit(1); - } - - debug("sshd version %.100s", SSH_VERSION); - - /* load private host keys */ - if (options.num_host_key_files > 0) - sensitive_data.host_keys = - xmalloc(options.num_host_key_files * sizeof(Key *)); - for (i = 0; i < options.num_host_key_files; i++) - sensitive_data.host_keys[i] = NULL; - sensitive_data.server_key = NULL; - sensitive_data.ssh1_host_key = NULL; - sensitive_data.have_ssh1_key = 0; - sensitive_data.have_ssh2_key = 0; - - for (i = 0; i < options.num_host_key_files; i++) { - key = key_load_private(options.host_key_files[i], "", NULL); - sensitive_data.host_keys[i] = key; - if (key == NULL) { - error("Could not load host key: %s", - options.host_key_files[i]); - sensitive_data.host_keys[i] = NULL; - continue; - } - switch (key->type) { - case KEY_RSA1: - sensitive_data.ssh1_host_key = key; - sensitive_data.have_ssh1_key = 1; - break; - case KEY_RSA: - case KEY_DSA: - sensitive_data.have_ssh2_key = 1; - break; - } - debug("private host key: #%d type %d %s", i, key->type, - key_type(key)); - } - if ((options.protocol & SSH_PROTO_1) && !sensitive_data.have_ssh1_key) { - log("Disabling protocol version 1. Could not load host key"); - options.protocol &= ~SSH_PROTO_1; - } - if ((options.protocol & SSH_PROTO_2) && - !sensitive_data.have_ssh2_key) { -#ifdef GSSAPI - if (options.gss_keyex) - ssh_gssapi_server_mechs(&mechs); - - if (mechs == GSS_C_NULL_OID_SET) { - log("Disabling protocol version 2. Could not load host" - "key or GSS-API mechanisms"); - options.protocol &= ~SSH_PROTO_2; - } -#else - log("Disabling protocol version 2. Could not load host key"); - options.protocol &= ~SSH_PROTO_2; -#endif /* GSSAPI */ - } - if (!(options.protocol & (SSH_PROTO_1|SSH_PROTO_2))) { - log("sshd: no hostkeys available -- exiting."); - exit(1); - } - - /* Check certain values for sanity. */ - if (options.protocol & SSH_PROTO_1) { - if (options.server_key_bits < 512 || - options.server_key_bits > 32768) { - (void) fprintf(stderr, gettext("Bad server key size.\n")); - exit(1); - } - /* - * Check that server and host key lengths differ sufficiently. This - * is necessary to make double encryption work with rsaref. Oh, I - * hate software patents. I dont know if this can go? Niels - */ - if (options.server_key_bits > - BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) - - SSH_KEY_BITS_RESERVED && options.server_key_bits < - BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) + - SSH_KEY_BITS_RESERVED) { - options.server_key_bits = - BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) + - SSH_KEY_BITS_RESERVED; - debug("Forcing server key to %d bits to make it differ from host key.", - options.server_key_bits); - } - } - - /* Configuration looks good, so exit if in test mode. */ - if (test_flag) - exit(0); - - /* - * Clear out any supplemental groups we may have inherited. This - * prevents inadvertent creation of files with bad modes (in the - * portable version at least, it's certainly possible for PAM - * to create a file, and we can't control the code in every - * module which might be used). - */ - if (setgroups(0, NULL) < 0) - debug("setgroups() failed: %.200s", strerror(errno)); - - /* Initialize the log (it is reinitialized below in case we forked). */ - if (debug_flag && !inetd_flag) - log_stderr = 1; - log_init(__progname, options.log_level, options.log_facility, log_stderr); - - /* - * Solaris 9 and systems upgraded from it may have the Ciphers option - * explicitly set to "aes128-cbc,blowfish-cbc,3des-cbc" in the - * sshd_config. Since the default server cipher list completely changed - * since then we rather notify the administator on startup. We do this - * check after log_init() so that the message goes to syslogd and not to - * stderr (unless the server is in the debug mode). Note that since - * Solaris 10 we no longer ship sshd_config with explicit settings for - * Ciphers or MACs. Do not try to augment the cipher list here since - * that might end up in a very confusing situation. - */ -#define OLD_DEFAULT_CIPHERS_LIST "aes128-cbc,blowfish-cbc,3des-cbc" - if (options.ciphers != NULL && - strcmp(options.ciphers, OLD_DEFAULT_CIPHERS_LIST) == 0) { - notice("Old default value \"%s\" for the \"Ciphers\" " - "option found in use. In general it is prudent to let " - "the server choose the defaults unless your environment " - "specifically needs an explicit setting. See " - "sshd_config(4) for more information.", - OLD_DEFAULT_CIPHERS_LIST); - } - -#ifdef HAVE_BSM - (void) setauid(&auid); -#endif /* HAVE_BSM */ - - /* - * If not in debugging mode, and not started from inetd, disconnect - * from the controlling terminal, and fork. The original process - * exits. - */ - if (!(debug_flag || inetd_flag || no_daemon_flag)) { -#ifdef TIOCNOTTY - int fd; -#endif /* TIOCNOTTY */ - if (daemon(0, 0) < 0) - fatal("daemon() failed: %.200s", strerror(errno)); - - /* Disconnect from the controlling tty. */ -#ifdef TIOCNOTTY - fd = open(_PATH_TTY, O_RDWR | O_NOCTTY); - if (fd >= 0) { - (void) ioctl(fd, TIOCNOTTY, NULL); - (void) close(fd); - } -#endif /* TIOCNOTTY */ - } - /* Reinitialize the log (because of the fork above). */ - log_init(__progname, options.log_level, options.log_facility, log_stderr); - - /* Initialize the random number generator. */ - arc4random_stir(); - - /* Chdir to the root directory so that the current disk can be - unmounted if desired. */ - (void) chdir("/"); - - /* ignore SIGPIPE */ - (void) signal(SIGPIPE, SIG_IGN); - - /* Start listening for a socket, unless started from inetd. */ - if (inetd_flag) { - int s1; - s1 = dup(0); /* Make sure descriptors 0, 1, and 2 are in use. */ - (void) dup(s1); - sock_in = dup(0); - sock_out = dup(1); - startup_pipe = -1; - /* we need this later for setting audit context */ - newsock = sock_in; - /* - * We intentionally do not close the descriptors 0, 1, and 2 - * as our code for setting the descriptors won\'t work if - * ttyfd happens to be one of those. - */ - debug("inetd sockets after dupping: %d, %d", sock_in, sock_out); - if (options.protocol & SSH_PROTO_1) - generate_ephemeral_server_key(); - } else { - for (ai = options.listen_addrs; ai; ai = ai->ai_next) { - if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) - continue; - if (num_listen_socks >= MAX_LISTEN_SOCKS) - fatal("Too many listen sockets. " - "Enlarge MAX_LISTEN_SOCKS"); - if (getnameinfo(ai->ai_addr, ai->ai_addrlen, - ntop, sizeof(ntop), strport, sizeof(strport), - NI_NUMERICHOST|NI_NUMERICSERV) != 0) { - error("getnameinfo failed"); - continue; - } - /* Create socket for listening. */ - listen_sock = socket(ai->ai_family, SOCK_STREAM, 0); - if (listen_sock < 0) { - /* kernel may not support ipv6 */ - verbose("socket: %.100s", strerror(errno)); - continue; - } - if (fcntl(listen_sock, F_SETFL, O_NONBLOCK) < 0) { - error("listen_sock O_NONBLOCK: %s", strerror(errno)); - (void) close(listen_sock); - continue; - } - /* - * Set socket options. - * Allow local port reuse in TIME_WAIT. - */ - if (setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, - &on, sizeof(on)) == -1) - error("setsockopt SO_REUSEADDR: %s", strerror(errno)); - - debug("Bind to port %s on %s.", strport, ntop); - - /* Bind the socket to the desired port. */ - if (bind(listen_sock, ai->ai_addr, ai->ai_addrlen) < 0) { - if (!ai->ai_next) - error("Bind to port %s on %s failed: %.200s.", - strport, ntop, strerror(errno)); - (void) close(listen_sock); - continue; - } - listen_socks[num_listen_socks] = listen_sock; - num_listen_socks++; - - /* Start listening on the port. */ - log("Server listening on %s port %s.", ntop, strport); - if (listen(listen_sock, 5) < 0) - fatal("listen: %.100s", strerror(errno)); - - } - freeaddrinfo(options.listen_addrs); - - if (!num_listen_socks) - fatal("Cannot bind any address."); - - if (options.protocol & SSH_PROTO_1) - generate_ephemeral_server_key(); - - /* - * Arrange to restart on SIGHUP. The handler needs - * listen_sock. - */ - (void) signal(SIGHUP, sighup_handler); - - (void) signal(SIGTERM, sigterm_handler); - (void) signal(SIGQUIT, sigterm_handler); - - /* Arrange SIGCHLD to be caught. */ - (void) signal(SIGCHLD, main_sigchld_handler); - - /* Write out the pid file after the sigterm handler is setup */ - if (!debug_flag) { - /* - * Record our pid in /var/run/sshd.pid to make it - * easier to kill the correct sshd. We don't want to - * do this before the bind above because the bind will - * fail if there already is a daemon, and this will - * overwrite any old pid in the file. - */ - f = fopen(options.pid_file, "wb"); - if (f) { - (void) fprintf(f, "%ld\n", (long) getpid()); - (void) fclose(f); - } - } - - /* setup fd set for listen */ - fdset = NULL; - maxfd = 0; - for (i = 0; i < num_listen_socks; i++) - if (listen_socks[i] > maxfd) - maxfd = listen_socks[i]; - /* pipes connected to unauthenticated childs */ - startup_pipes = xmalloc(options.max_startups * sizeof(int)); - for (i = 0; i < options.max_startups; i++) - startup_pipes[i] = -1; - - /* - * Stay listening for connections until the system crashes or - * the daemon is killed with a signal. - */ - for (;;) { - if (received_sighup) - sighup_restart(); - if (fdset != NULL) - xfree(fdset); - fdsetsz = howmany(maxfd+1, NFDBITS) * sizeof(fd_mask); - fdset = (fd_set *)xmalloc(fdsetsz); - (void) memset(fdset, 0, fdsetsz); - - for (i = 0; i < num_listen_socks; i++) - FD_SET(listen_socks[i], fdset); - for (i = 0; i < options.max_startups; i++) - if (startup_pipes[i] != -1) - FD_SET(startup_pipes[i], fdset); - - /* Wait in select until there is a connection. */ - ret = select(maxfd+1, fdset, NULL, NULL, NULL); - if (ret < 0 && errno != EINTR) - error("select: %.100s", strerror(errno)); - if (received_sigterm) { - log("Received signal %d; terminating.", - (int) received_sigterm); - close_listen_socks(); - (void) unlink(options.pid_file); - exit(255); - } - if (key_used && key_do_regen) { - generate_ephemeral_server_key(); - key_used = 0; - key_do_regen = 0; - } - if (ret < 0) - continue; - - for (i = 0; i < options.max_startups; i++) - if (startup_pipes[i] != -1 && - FD_ISSET(startup_pipes[i], fdset)) { - /* - * the read end of the pipe is ready - * if the child has closed the pipe - * after successful authentication - * or if the child has died - */ - (void) close(startup_pipes[i]); - startup_pipes[i] = -1; - startups--; - } - for (i = 0; i < num_listen_socks; i++) { - if (!FD_ISSET(listen_socks[i], fdset)) - continue; - fromlen = sizeof(from); - newsock = accept(listen_socks[i], (struct sockaddr *)&from, - &fromlen); - if (newsock < 0) { - if (errno != EINTR && errno != EWOULDBLOCK) - error("accept: %.100s", strerror(errno)); - continue; - } - if (fcntl(newsock, F_SETFL, 0) < 0) { - error("newsock del O_NONBLOCK: %s", strerror(errno)); - (void) close(newsock); - continue; - } - if (drop_connection(startups) == 1) { - debug("drop connection #%d", startups); - (void) close(newsock); - continue; - } - if (pipe(startup_p) == -1) { - (void) close(newsock); - continue; - } - - for (j = 0; j < options.max_startups; j++) - if (startup_pipes[j] == -1) { - startup_pipes[j] = startup_p[0]; - if (maxfd < startup_p[0]) - maxfd = startup_p[0]; - startups++; - break; - } - - /* - * Got connection. Fork a child to handle it, unless - * we are in debugging mode. - */ - if (debug_flag) { - /* - * In debugging mode. Close the listening - * socket, and start processing the - * connection without forking. - */ - debug("Server will not fork when running in debugging mode."); - close_listen_socks(); - sock_in = newsock; - sock_out = newsock; - startup_pipe = -1; - pid = getpid(); - break; - } else { - /* - * Normal production daemon. Fork, and have - * the child process the connection. The - * parent continues listening. - */ -#ifdef HAVE_SOLARIS_CONTRACTS - /* - * Setup Solaris contract template so - * the child process is in a different - * process contract than the parent; - * prevents established connections from - * being killed when the sshd master - * listener service is stopped. - */ - contracts_pre_fork(); -#endif /* HAVE_SOLARIS_CONTRACTS */ - if ((pid = fork()) == 0) { - /* - * Child. Close the listening and max_startup - * sockets. Start using the accepted socket. - * Reinitialize logging (since our pid has - * changed). We break out of the loop to handle - * the connection. - */ -#ifdef HAVE_SOLARIS_CONTRACTS - contracts_post_fork_child(); -#endif /* HAVE_SOLARIS_CONTRACTS */ - xfree(fdset); - startup_pipe = startup_p[1]; - close_startup_pipes(); - close_listen_socks(); - sock_in = newsock; - sock_out = newsock; - log_init(__progname, options.log_level, options.log_facility, log_stderr); - break; - } - - /* Parent. Stay in the loop. */ - if (pid < 0) - error("fork: %.100s", strerror(errno)); - else - debug("Forked child %ld.", (long)pid); - -#ifdef HAVE_SOLARIS_CONTRACTS - contracts_post_fork_parent((pid > 0)); -#endif /* HAVE_SOLARIS_CONTRACTS */ - } - - (void) close(startup_p[1]); - - /* Mark that the key has been used (it was "given" to the child). */ - if ((options.protocol & SSH_PROTO_1) && - key_used == 0) { - /* Schedule server key regeneration alarm. */ - (void) signal(SIGALRM, key_regeneration_alarm); - (void) alarm(options.key_regeneration_time); - key_used = 1; - } - - arc4random_stir(); - - /* - * Close the accepted socket since the child - * will now take care of the new connection. - */ - (void) close(newsock); - } - /* child process check (or debug mode) */ - if (num_listen_socks < 0) - break; - } - } - - /* - * This is the child processing a new connection, the SSH master process - * stays in the ( ; ; ) loop above. - */ -#ifdef HAVE_BSM - audit_sshd_settid(newsock); -#endif - /* - * Create a new session and process group since the 4.4BSD - * setlogin() affects the entire process group. We don't - * want the child to be able to affect the parent. - */ -#if 0 - /* XXX: this breaks Solaris */ - if (!debug_flag && !inetd_flag && setsid() < 0) - error("setsid: %.100s", strerror(errno)); -#endif - - /* - * Disable the key regeneration alarm. We will not regenerate the - * key since we are no longer in a position to give it to anyone. We - * will not restart on SIGHUP since it no longer makes sense. - */ - (void) alarm(0); - (void) signal(SIGALRM, SIG_DFL); - (void) signal(SIGHUP, SIG_DFL); - (void) signal(SIGTERM, SIG_DFL); - (void) signal(SIGQUIT, SIG_DFL); - (void) signal(SIGCHLD, SIG_DFL); - (void) signal(SIGINT, SIG_DFL); - - /* Set keepalives if requested. */ - if (options.keepalives && - setsockopt(sock_in, SOL_SOCKET, SO_KEEPALIVE, &on, - sizeof(on)) < 0) - debug2("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); - - /* - * Register our connection. This turns encryption off because we do - * not have a key. - */ - packet_set_connection(sock_in, sock_out); - - remote_port = get_remote_port(); - remote_ip = get_remote_ipaddr(); - -#ifdef LIBWRAP - /* Check whether logins are denied from this host. */ - { - struct request_info req; - - (void) request_init(&req, RQ_DAEMON, __progname, RQ_FILE, sock_in, 0); - fromhost(&req); - - if (!hosts_access(&req)) { - debug("Connection refused by tcp wrapper"); - refuse(&req); - /* NOTREACHED */ - fatal("libwrap refuse returns"); - } - } -#endif /* LIBWRAP */ - - /* Log the connection. */ - verbose("Connection from %.500s port %d", remote_ip, remote_port); - - sshd_exchange_identification(sock_in, sock_out); - /* - * Check that the connection comes from a privileged port. - * Rhosts-Authentication only makes sense from privileged - * programs. Of course, if the intruder has root access on his local - * machine, he can connect from any port. So do not use these - * authentication methods from machines that you do not trust. - */ - if (options.rhosts_authentication && - (remote_port >= IPPORT_RESERVED || - remote_port < IPPORT_RESERVED / 2)) { - debug("Rhosts Authentication disabled, " - "originating port %d not trusted.", remote_port); - options.rhosts_authentication = 0; - } -#if defined(KRB4) && !defined(KRB5) - if (!packet_connection_is_ipv4() && - options.kerberos_authentication) { - debug("Kerberos Authentication disabled, only available for IPv4."); - options.kerberos_authentication = 0; - } -#endif /* KRB4 && !KRB5 */ -#ifdef AFS - /* If machine has AFS, set process authentication group. */ - if (k_hasafs()) { - k_setpag(); - k_unlog(); - } -#endif /* AFS */ - - packet_set_nonblocking(); - - /* - * Start the monitor. That way both processes will have their own - * PKCS#11 sessions. See the PKCS#11 standard for more information on - * fork safety and packet.c for information about forking with the - * engine. - * - * Note that the monitor stays in the function while the child is the - * only one that returns. - */ - altprivsep_start_and_do_monitor(options.use_openssl_engine, - inetd_flag, newsock, startup_pipe); - - /* - * We don't want to listen forever unless the other side successfully - * authenticates itself. So we set up an alarm which is cleared after - * successful authentication. A limit of zero indicates no limit. Note - * that we don't set the alarm in debugging mode; it is just annoying to - * have the server exit just when you are about to discover the bug. - */ - (void) signal(SIGALRM, grace_alarm_handler); - if (!debug_flag) - (void) alarm(options.login_grace_time); - - /* - * The child is about to start the first key exchange while the monitor - * stays in altprivsep_start_and_do_monitor() function. - */ - (void) pkcs11_engine_load(options.use_openssl_engine); - - /* perform the key exchange */ - /* authenticate user and start session */ - if (compat20) { - do_ssh2_kex(); - authctxt = do_authentication2(); - } else { - do_ssh1_kex(); - authctxt = do_authentication(); - } - - /* Authentication complete */ - (void) alarm(0); - /* we no longer need an alarm handler */ - (void) signal(SIGALRM, SIG_DFL); - - if (startup_pipe != -1) { - (void) close(startup_pipe); - startup_pipe = -1; - } - - /* ALTPRIVSEP Child */ - - /* - * Drop privileges, access to privileged resources. - * - * Destroy private host keys, if any. - * - * No need to release any GSS credentials -- sshd only acquires - * creds to determine what mechs it can negotiate then releases - * them right away and uses GSS_C_NO_CREDENTIAL to accept - * contexts. - */ - debug2("Unprivileged server process dropping privileges"); - permanently_set_uid(authctxt->pw, options.chroot_directory); - destroy_sensitive_data(); - - /* Just another safety check. */ - if (getuid() != authctxt->pw->pw_uid || - geteuid() != authctxt->pw->pw_uid) { - fatal("Failed to set uids to %u.", (u_int)authctxt->pw->pw_uid); - } - - ssh_gssapi_server_mechs(NULL); /* release cached mechs list */ - packet_set_server(); - - /* now send the authentication context to the monitor */ - altprivsep_send_auth_context(authctxt); - - mpipe = altprivsep_get_pipe_fd(); - if (fcntl(mpipe, F_SETFL, O_NONBLOCK) < 0) - error("fcntl O_NONBLOCK: %.100s", strerror(errno)); - -#ifdef HAVE_BSM - fatal_remove_cleanup( - (void (*)(void *))audit_failed_login_cleanup, - (void *)authctxt); -#endif /* HAVE_BSM */ - - if (compat20) { - debug3("setting handler to forward re-key packets to the monitor"); - dispatch_range(SSH2_MSG_KEXINIT, SSH2_MSG_TRANSPORT_MAX, - &altprivsep_rekey); - } - - /* Logged-in session. */ - do_authenticated(authctxt); - - /* The connection has been terminated. */ - verbose("Closing connection to %.100s", remote_ip); - - packet_close(); - -#ifdef USE_PAM - finish_pam(authctxt); -#endif /* USE_PAM */ - - return (0); -} - -/* - * Decrypt session_key_int using our private server key and private host key - * (key with larger modulus first). - */ -int -ssh1_session_key(BIGNUM *session_key_int) -{ - int rsafail = 0; - - if (BN_cmp(sensitive_data.server_key->rsa->n, sensitive_data.ssh1_host_key->rsa->n) > 0) { - /* Server key has bigger modulus. */ - if (BN_num_bits(sensitive_data.server_key->rsa->n) < - BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) + SSH_KEY_BITS_RESERVED) { - fatal("do_connection: %s: server_key %d < host_key %d + SSH_KEY_BITS_RESERVED %d", - get_remote_ipaddr(), - BN_num_bits(sensitive_data.server_key->rsa->n), - BN_num_bits(sensitive_data.ssh1_host_key->rsa->n), - SSH_KEY_BITS_RESERVED); - } - if (rsa_private_decrypt(session_key_int, session_key_int, - sensitive_data.server_key->rsa) <= 0) - rsafail++; - if (rsa_private_decrypt(session_key_int, session_key_int, - sensitive_data.ssh1_host_key->rsa) <= 0) - rsafail++; - } else { - /* Host key has bigger modulus (or they are equal). */ - if (BN_num_bits(sensitive_data.ssh1_host_key->rsa->n) < - BN_num_bits(sensitive_data.server_key->rsa->n) + SSH_KEY_BITS_RESERVED) { - fatal("do_connection: %s: host_key %d < server_key %d + SSH_KEY_BITS_RESERVED %d", - get_remote_ipaddr(), - BN_num_bits(sensitive_data.ssh1_host_key->rsa->n), - BN_num_bits(sensitive_data.server_key->rsa->n), - SSH_KEY_BITS_RESERVED); - } - if (rsa_private_decrypt(session_key_int, session_key_int, - sensitive_data.ssh1_host_key->rsa) < 0) - rsafail++; - if (rsa_private_decrypt(session_key_int, session_key_int, - sensitive_data.server_key->rsa) < 0) - rsafail++; - } - return (rsafail); -} -/* - * SSH1 key exchange - */ -static void -do_ssh1_kex(void) -{ - int i, len; - int rsafail = 0; - BIGNUM *session_key_int; - u_char session_key[SSH_SESSION_KEY_LENGTH]; - u_char cookie[8]; - u_int cipher_type, auth_mask, protocol_flags; - u_int32_t rnd = 0; - - /* - * Generate check bytes that the client must send back in the user - * packet in order for it to be accepted; this is used to defy ip - * spoofing attacks. Note that this only works against somebody - * doing IP spoofing from a remote machine; any machine on the local - * network can still see outgoing packets and catch the random - * cookie. This only affects rhosts authentication, and this is one - * of the reasons why it is inherently insecure. - */ - for (i = 0; i < 8; i++) { - if (i % 4 == 0) - rnd = arc4random(); - cookie[i] = rnd & 0xff; - rnd >>= 8; - } - - /* - * Send our public key. We include in the packet 64 bits of random - * data that must be matched in the reply in order to prevent IP - * spoofing. - */ - packet_start(SSH_SMSG_PUBLIC_KEY); - for (i = 0; i < 8; i++) - packet_put_char(cookie[i]); - - /* Store our public server RSA key. */ - packet_put_int(BN_num_bits(sensitive_data.server_key->rsa->n)); - packet_put_bignum(sensitive_data.server_key->rsa->e); - packet_put_bignum(sensitive_data.server_key->rsa->n); - - /* Store our public host RSA key. */ - packet_put_int(BN_num_bits(sensitive_data.ssh1_host_key->rsa->n)); - packet_put_bignum(sensitive_data.ssh1_host_key->rsa->e); - packet_put_bignum(sensitive_data.ssh1_host_key->rsa->n); - - /* Put protocol flags. */ - packet_put_int(SSH_PROTOFLAG_HOST_IN_FWD_OPEN); - - /* Declare which ciphers we support. */ - packet_put_int(cipher_mask_ssh1(0)); - - /* Declare supported authentication types. */ - auth_mask = 0; - if (options.rhosts_authentication) - auth_mask |= 1 << SSH_AUTH_RHOSTS; - if (options.rhosts_rsa_authentication) - auth_mask |= 1 << SSH_AUTH_RHOSTS_RSA; - if (options.rsa_authentication) - auth_mask |= 1 << SSH_AUTH_RSA; -#if defined(KRB4) || defined(KRB5) - if (options.kerberos_authentication) - auth_mask |= 1 << SSH_AUTH_KERBEROS; -#endif -#if defined(AFS) || defined(KRB5) - if (options.kerberos_tgt_passing) - auth_mask |= 1 << SSH_PASS_KERBEROS_TGT; -#endif -#ifdef AFS - if (options.afs_token_passing) - auth_mask |= 1 << SSH_PASS_AFS_TOKEN; -#endif - if (options.challenge_response_authentication == 1) - auth_mask |= 1 << SSH_AUTH_TIS; - if (options.password_authentication) - auth_mask |= 1 << SSH_AUTH_PASSWORD; - packet_put_int(auth_mask); - - /* Send the packet and wait for it to be sent. */ - packet_send(); - packet_write_wait(); - - debug("Sent %d bit server key and %d bit host key.", - BN_num_bits(sensitive_data.server_key->rsa->n), - BN_num_bits(sensitive_data.ssh1_host_key->rsa->n)); - - /* Read clients reply (cipher type and session key). */ - packet_read_expect(SSH_CMSG_SESSION_KEY); - - /* Get cipher type and check whether we accept this. */ - cipher_type = packet_get_char(); - - if (!(cipher_mask_ssh1(0) & (1 << cipher_type))) { - packet_disconnect("Warning: client selects unsupported cipher."); - } - - /* Get check bytes from the packet. These must match those we - sent earlier with the public key packet. */ - for (i = 0; i < 8; i++) { - if (cookie[i] != packet_get_char()) { - packet_disconnect("IP Spoofing check bytes do not match."); - } - } - - debug("Encryption type: %.200s", cipher_name(cipher_type)); - - /* Get the encrypted integer. */ - if ((session_key_int = BN_new()) == NULL) - fatal("do_ssh1_kex: BN_new failed"); - packet_get_bignum(session_key_int); - - protocol_flags = packet_get_int(); - packet_set_protocol_flags(protocol_flags); - packet_check_eom(); - - /* Decrypt session_key_int using host/server keys */ - rsafail = ssh1_session_key(session_key_int); - - /* - * Extract session key from the decrypted integer. The key is in the - * least significant 256 bits of the integer; the first byte of the - * key is in the highest bits. - */ - if (!rsafail) { - (void) BN_mask_bits(session_key_int, sizeof(session_key) * 8); - len = BN_num_bytes(session_key_int); - if (len < 0 || len > sizeof(session_key)) { - error("do_connection: bad session key len from %s: " - "session_key_int %d > sizeof(session_key) %lu", - get_remote_ipaddr(), len, (u_long)sizeof(session_key)); - rsafail++; - } else { - (void) memset(session_key, 0, sizeof(session_key)); - (void) BN_bn2bin(session_key_int, - session_key + sizeof(session_key) - len); - - compute_session_id(session_id, cookie, - sensitive_data.ssh1_host_key->rsa->n, - sensitive_data.server_key->rsa->n); - /* - * Xor the first 16 bytes of the session key with the - * session id. - */ - for (i = 0; i < 16; i++) - session_key[i] ^= session_id[i]; - } - } - if (rsafail) { - int bytes = BN_num_bytes(session_key_int); - u_char *buf = xmalloc(bytes); - MD5_CTX md; - - log("do_connection: generating a fake encryption key"); - (void) BN_bn2bin(session_key_int, buf); - MD5_Init(&md); - MD5_Update(&md, buf, bytes); - MD5_Update(&md, sensitive_data.ssh1_cookie, SSH_SESSION_KEY_LENGTH); - MD5_Final(session_key, &md); - MD5_Init(&md); - MD5_Update(&md, session_key, 16); - MD5_Update(&md, buf, bytes); - MD5_Update(&md, sensitive_data.ssh1_cookie, SSH_SESSION_KEY_LENGTH); - MD5_Final(session_key + 16, &md); - (void) memset(buf, 0, bytes); - xfree(buf); - for (i = 0; i < 16; i++) - session_id[i] = session_key[i] ^ session_key[i + 16]; - } - /* Destroy the private and public keys. No longer. */ - destroy_sensitive_data(); - - /* Destroy the decrypted integer. It is no longer needed. */ - BN_clear_free(session_key_int); - - /* Set the session key. From this on all communications will be encrypted. */ - packet_set_encryption_key(session_key, SSH_SESSION_KEY_LENGTH, cipher_type); - - /* Destroy our copy of the session key. It is no longer needed. */ - (void) memset(session_key, 0, sizeof(session_key)); - - debug("Received session key; encryption turned on."); - - /* Send an acknowledgment packet. Note that this packet is sent encrypted. */ - packet_start(SSH_SMSG_SUCCESS); - packet_send(); - packet_write_wait(); -} - -/* - * Prepare for SSH2 key exchange. - */ -Kex * -prepare_for_ssh2_kex(void) -{ - Kex *kex; - Kex_hook_func kex_hook = NULL; - char **locales; - static char **myproposal; - - myproposal = my_srv_proposal; - - if (options.ciphers != NULL) { - myproposal[PROPOSAL_ENC_ALGS_CTOS] = - myproposal[PROPOSAL_ENC_ALGS_STOC] = options.ciphers; - } - myproposal[PROPOSAL_ENC_ALGS_CTOS] = - compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_CTOS]); - myproposal[PROPOSAL_ENC_ALGS_STOC] = - compat_cipher_proposal(myproposal[PROPOSAL_ENC_ALGS_STOC]); - - if (options.macs != NULL) { - myproposal[PROPOSAL_MAC_ALGS_CTOS] = - myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs; - } - if (!options.compression) { - myproposal[PROPOSAL_COMP_ALGS_CTOS] = - myproposal[PROPOSAL_COMP_ALGS_STOC] = "none"; - } - - /* - * Prepare kex algs / hostkey algs (excluding GSS, which is - * handled in the kex hook. - * - * XXX This should probably move to the kex hook as well, where - * all non-constant kex offer material belongs. - */ - myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = list_hostkey_types(); - - /* If we have no host key algs we can't offer KEXDH/KEX_DH_GEX */ - if (myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] == NULL || - *myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] == '\0') - myproposal[PROPOSAL_KEX_ALGS] = ""; - - if ((locales = g11n_getlocales()) != NULL) { - /* Solaris 9 SSH expects a list of locales */ - if (datafellows & SSH_BUG_LOCALES_NOT_LANGTAGS) - myproposal[PROPOSAL_LANG_STOC] = xjoin(locales, ','); - else - myproposal[PROPOSAL_LANG_STOC] = - g11n_locales2langs(locales); - } - - if (locales != NULL) - g11n_freelist(locales); - - if ((myproposal[PROPOSAL_LANG_STOC] != NULL) && - (strcmp(myproposal[PROPOSAL_LANG_STOC], "")) != 0) - myproposal[PROPOSAL_LANG_CTOS] = - xstrdup(myproposal[PROPOSAL_LANG_STOC]); - -#ifdef GSSAPI - if (options.gss_keyex) - kex_hook = ssh_gssapi_server_kex_hook; -#endif /* GSSAPI */ - - kex = kex_setup(NULL, myproposal, kex_hook); - - /* - * Note that the my_srv_proposal variable (ie., myproposal) is staticly - * initialized with "" for the language fields; we must not xfree such - * strings. - */ - if (myproposal[PROPOSAL_LANG_STOC] != NULL && - strcmp(myproposal[PROPOSAL_LANG_STOC], "") != 0) - xfree(myproposal[PROPOSAL_LANG_STOC]); - if (myproposal[PROPOSAL_LANG_CTOS] != NULL && - strcmp(myproposal[PROPOSAL_LANG_STOC], "") != 0) - xfree(myproposal[PROPOSAL_LANG_CTOS]); - - kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server; - kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; -#ifdef GSSAPI - kex->kex[KEX_GSS_GRP1_SHA1] = kexgss_server; -#endif /* GSSAPI */ - kex->server = 1; - kex->client_version_string = client_version_string; - kex->server_version_string = server_version_string; - kex->load_host_key = &get_hostkey_by_type; - kex->host_key_index = &get_hostkey_index; - - xxx_kex = kex; - return (kex); -} - -/* - * Do SSH2 key exchange. - */ -static void -do_ssh2_kex(void) -{ - Kex *kex; - - kex = prepare_for_ssh2_kex(); - kex_start(kex); - - dispatch_run(DISPATCH_BLOCK, &kex->done, kex); - - if (kex->name) { - xfree(kex->name); - kex->name = NULL; - } - session_id2 = kex->session_id; - session_id2_len = kex->session_id_len; - -#ifdef DEBUG_KEXDH - /* send 1st encrypted/maced/compressed message */ - packet_start(SSH2_MSG_IGNORE); - packet_put_cstring("markus"); - packet_send(); - packet_write_wait(); -#endif - debug("KEX done"); -} diff --git a/usr/src/cmd/ssh/sshd/sshlogin.c b/usr/src/cmd/ssh/sshd/sshlogin.c deleted file mode 100644 index c21877355c..0000000000 --- a/usr/src/cmd/ssh/sshd/sshlogin.c +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Author: Tatu Ylonen <ylo@cs.hut.fi> - * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland - * All rights reserved - * This file performs some of the things login(1) normally does. We cannot - * easily use something like login -p -h host -f user, because there are - * several different logins around, and it is hard to determined what kind of - * login the current system has. Also, we want to be able to execute commands - * on a tty. - * - * As far as I am concerned, the code I have written for this software - * can be used freely for any purpose. Any derived versions of this - * software must be clearly marked as such, and if the derived work is - * incompatible with the protocol description in the RFC file, it must be - * called by a name other than "ssh" or "Secure Shell". - * - * Copyright (c) 1999 Theo de Raadt. All rights reserved. - * Copyright (c) 1999 Markus Friedl. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -/* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - */ - -#include "includes.h" -RCSID("$OpenBSD: sshlogin.c,v 1.5 2002/08/29 15:57:25 stevesk Exp $"); - -#include "loginrec.h" -#include "log.h" -#include "buffer.h" -#include "servconf.h" -#include "canohost.h" -#include "packet.h" - -extern u_int utmp_len; -extern ServerOptions options; - -/* - * Returns the time when the user last logged in. Returns 0 if the - * information is not available. This must be called before record_login. - * The host the user logged in from will be returned in buf. - */ -u_long -get_last_login_time(uid_t uid, const char *logname, - char *buf, u_int bufsize) -{ - struct logininfo li; - - (void) login_get_lastlog(&li, uid); - (void) strlcpy(buf, li.hostname, bufsize); - return li.tv_sec; -} - -/* - * Records that the user has logged in. If only these parts of operating - * systems were more standardized. - */ -void -record_login(pid_t pid, const char *ttyname, const char *progname, - const char *user) -{ - struct logininfo *li; - static int initialized = 0; - static socklen_t fromlen; - static struct sockaddr_storage from; - static const char *remote_name_or_ip; - - if (pid == 0) - pid = getpid(); - /* - * Get IP address of client. If the connection is not a socket, let - * the address be 0.0.0.0. - */ - if (!initialized) { - (void) memset(&from, 0, sizeof(from)); - if (packet_connection_is_on_socket()) { - fromlen = sizeof(from); - if (getpeername(packet_get_connection_in(), - (struct sockaddr *) &from, &fromlen) < 0) { - debug("getpeername: %.100s", strerror(errno)); - fatal_cleanup(); - } - } - remote_name_or_ip = get_remote_name_or_ip(utmp_len, - options.verify_reverse_mapping); - - initialized = 1; - } - - li = login_alloc_entry(pid, user, remote_name_or_ip, ttyname, progname); - login_set_addr(li, (struct sockaddr*) &from, sizeof(struct sockaddr)); - (void) login_login(li); - login_free_entry(li); -} - -/* Records that the user has logged out. */ -void -record_logout(pid_t pid, const char *ttyname, const char *progname, - const char *user) -{ - struct logininfo *li; - - li = login_alloc_entry(pid, user, NULL, ttyname, progname); - (void) login_logout(li); - login_free_entry(li); -} diff --git a/usr/src/cmd/ssh/sshd/sshpty.c b/usr/src/cmd/ssh/sshd/sshpty.c deleted file mode 100644 index b421798cb5..0000000000 --- a/usr/src/cmd/ssh/sshd/sshpty.c +++ /dev/null @@ -1,420 +0,0 @@ -/* - * Author: Tatu Ylonen <ylo@cs.hut.fi> - * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland - * All rights reserved - * Allocating a pseudo-terminal, and making it the controlling tty. - * - * As far as I am concerned, the code I have written for this software - * can be used freely for any purpose. Any derived versions of this - * software must be clearly marked as such, and if the derived work is - * incompatible with the protocol description in the RFC file, it must be - * called by a name other than "ssh" or "Secure Shell". - */ - -#include "includes.h" -RCSID("$OpenBSD: sshpty.c,v 1.7 2002/06/24 17:57:20 deraadt Exp $"); - -#pragma ident "%Z%%M% %I% %E% SMI" - -#ifdef HAVE_UTIL_H -# include <util.h> -#endif /* HAVE_UTIL_H */ - -#include "sshpty.h" -#include "log.h" -#include "misc.h" - -/* Pty allocated with _getpty gets broken if we do I_PUSH:es to it. */ -#if defined(HAVE__GETPTY) || defined(HAVE_OPENPTY) -#undef HAVE_DEV_PTMX -#endif - -#ifdef HAVE_PTY_H -# include <pty.h> -#endif -#if defined(HAVE_DEV_PTMX) && defined(HAVE_SYS_STROPTS_H) -# include <sys/stropts.h> -#endif - -#ifndef O_NOCTTY -#define O_NOCTTY 0 -#endif - -/* - * Allocates and opens a pty. Returns 0 if no pty could be allocated, or - * nonzero if a pty was successfully allocated. On success, open file - * descriptors for the pty and tty sides and the name of the tty side are - * returned (the buffer must be able to hold at least 64 characters). - */ - -int -pty_allocate(int *ptyfd, int *ttyfd, char *namebuf, int namebuflen) -{ -#if defined(HAVE_OPENPTY) || defined(BSD4_4) - /* openpty(3) exists in OSF/1 and some other os'es */ - char *name; - int i; - - i = openpty(ptyfd, ttyfd, NULL, NULL, NULL); - if (i < 0) { - error("openpty: %.100s", strerror(errno)); - return 0; - } - name = ttyname(*ttyfd); - if (!name) - fatal("openpty returns device for which ttyname fails."); - - strlcpy(namebuf, name, namebuflen); /* possible truncation */ - return 1; -#else /* HAVE_OPENPTY */ -#ifdef HAVE__GETPTY - /* - * _getpty(3) exists in SGI Irix 4.x, 5.x & 6.x -- it generates more - * pty's automagically when needed - */ - char *slave; - - slave = _getpty(ptyfd, O_RDWR, 0622, 0); - if (slave == NULL) { - error("_getpty: %.100s", strerror(errno)); - return 0; - } - strlcpy(namebuf, slave, namebuflen); - /* Open the slave side. */ - *ttyfd = open(namebuf, O_RDWR | O_NOCTTY); - if (*ttyfd < 0) { - error("%.200s: %.100s", namebuf, strerror(errno)); - close(*ptyfd); - return 0; - } - return 1; -#else /* HAVE__GETPTY */ -#if defined(HAVE_DEV_PTMX) - /* - * This code is used e.g. on Solaris 2.x. (Note that Solaris 2.3 - * also has bsd-style ptys, but they simply do not work.) - */ - int ptm; - char *pts; - mysig_t old_signal; - - ptm = open("/dev/ptmx", O_RDWR | O_NOCTTY); - if (ptm < 0) { - error("/dev/ptmx: %.100s", strerror(errno)); - return 0; - } - old_signal = mysignal(SIGCHLD, SIG_DFL); - if (grantpt(ptm) < 0) { - error("grantpt: %.100s", strerror(errno)); - return 0; - } - mysignal(SIGCHLD, old_signal); - if (unlockpt(ptm) < 0) { - error("unlockpt: %.100s", strerror(errno)); - return 0; - } - pts = ptsname(ptm); - if (pts == NULL) - error("Slave pty side name could not be obtained."); - strlcpy(namebuf, pts, namebuflen); - *ptyfd = ptm; - - /* Open the slave side. */ - *ttyfd = open(namebuf, O_RDWR | O_NOCTTY); - if (*ttyfd < 0) { - error("%.100s: %.100s", namebuf, strerror(errno)); - close(*ptyfd); - return 0; - } -#ifndef HAVE_CYGWIN - /* - * Push the appropriate streams modules, as described in Solaris pts(7). - * HP-UX pts(7) doesn't have ttcompat module. - */ - if (ioctl(*ttyfd, I_PUSH, "ptem") < 0) - error("ioctl I_PUSH ptem: %.100s", strerror(errno)); - if (ioctl(*ttyfd, I_PUSH, "ldterm") < 0) - error("ioctl I_PUSH ldterm: %.100s", strerror(errno)); -#ifndef __hpux - if (ioctl(*ttyfd, I_PUSH, "ttcompat") < 0) - error("ioctl I_PUSH ttcompat: %.100s", strerror(errno)); -#endif -#endif - return 1; -#else /* HAVE_DEV_PTMX */ -#ifdef HAVE_DEV_PTS_AND_PTC - /* AIX-style pty code. */ - const char *name; - - *ptyfd = open("/dev/ptc", O_RDWR | O_NOCTTY); - if (*ptyfd < 0) { - error("Could not open /dev/ptc: %.100s", strerror(errno)); - return 0; - } - name = ttyname(*ptyfd); - if (!name) - fatal("Open of /dev/ptc returns device for which ttyname fails."); - strlcpy(namebuf, name, namebuflen); - *ttyfd = open(name, O_RDWR | O_NOCTTY); - if (*ttyfd < 0) { - error("Could not open pty slave side %.100s: %.100s", - name, strerror(errno)); - close(*ptyfd); - return 0; - } - return 1; -#else /* HAVE_DEV_PTS_AND_PTC */ -#ifdef _UNICOS - char buf[64]; - int i; - int highpty; - -#ifdef _SC_CRAY_NPTY - highpty = sysconf(_SC_CRAY_NPTY); - if (highpty == -1) - highpty = 128; -#else - highpty = 128; -#endif - - for (i = 0; i < highpty; i++) { - snprintf(buf, sizeof(buf), "/dev/pty/%03d", i); - *ptyfd = open(buf, O_RDWR|O_NOCTTY); - if (*ptyfd < 0) - continue; - snprintf(namebuf, namebuflen, "/dev/ttyp%03d", i); - /* Open the slave side. */ - *ttyfd = open(namebuf, O_RDWR|O_NOCTTY); - if (*ttyfd < 0) { - error("%.100s: %.100s", namebuf, strerror(errno)); - close(*ptyfd); - return 0; - } - return 1; - } - return 0; -#else - /* BSD-style pty code. */ - char buf[64]; - int i; - const char *ptymajors = "pqrstuvwxyzabcdefghijklmnoABCDEFGHIJKLMNOPQRSTUVWXYZ"; - const char *ptyminors = "0123456789abcdef"; - int num_minors = strlen(ptyminors); - int num_ptys = strlen(ptymajors) * num_minors; - struct termios tio; - - for (i = 0; i < num_ptys; i++) { - snprintf(buf, sizeof buf, "/dev/pty%c%c", ptymajors[i / num_minors], - ptyminors[i % num_minors]); - snprintf(namebuf, namebuflen, "/dev/tty%c%c", - ptymajors[i / num_minors], ptyminors[i % num_minors]); - - *ptyfd = open(buf, O_RDWR | O_NOCTTY); - if (*ptyfd < 0) { - /* Try SCO style naming */ - snprintf(buf, sizeof buf, "/dev/ptyp%d", i); - snprintf(namebuf, namebuflen, "/dev/ttyp%d", i); - *ptyfd = open(buf, O_RDWR | O_NOCTTY); - if (*ptyfd < 0) - continue; - } - - /* Open the slave side. */ - *ttyfd = open(namebuf, O_RDWR | O_NOCTTY); - if (*ttyfd < 0) { - error("%.100s: %.100s", namebuf, strerror(errno)); - close(*ptyfd); - return 0; - } - /* set tty modes to a sane state for broken clients */ - if (tcgetattr(*ptyfd, &tio) < 0) - log("Getting tty modes for pty failed: %.100s", strerror(errno)); - else { - tio.c_lflag |= (ECHO | ISIG | ICANON); - tio.c_oflag |= (OPOST | ONLCR); - tio.c_iflag |= ICRNL; - - /* Set the new modes for the terminal. */ - if (tcsetattr(*ptyfd, TCSANOW, &tio) < 0) - log("Setting tty modes for pty failed: %.100s", strerror(errno)); - } - - return 1; - } - return 0; -#endif /* CRAY */ -#endif /* HAVE_DEV_PTS_AND_PTC */ -#endif /* HAVE_DEV_PTMX */ -#endif /* HAVE__GETPTY */ -#endif /* HAVE_OPENPTY */ -} - -/* Releases the tty. Its ownership is returned to root, and permissions to 0666. */ - -void -pty_release(const char *ttyname) -{ - if (chown(ttyname, (uid_t) 0, (gid_t) 0) < 0) - error("chown %.100s 0 0 failed: %.100s", ttyname, strerror(errno)); - if (chmod(ttyname, (mode_t) 0666) < 0) - error("chmod %.100s 0666 failed: %.100s", ttyname, strerror(errno)); -} - -/* Makes the tty the processes controlling tty and sets it to sane modes. */ - -void -pty_make_controlling_tty(int *ttyfd, const char *ttyname) -{ - int fd; -#ifdef USE_VHANGUP - void *old; -#endif /* USE_VHANGUP */ - -#ifdef _UNICOS - if (setsid() < 0) - error("setsid: %.100s", strerror(errno)); - - fd = open(ttyname, O_RDWR|O_NOCTTY); - if (fd != -1) { - mysignal(SIGHUP, SIG_IGN); - ioctl(fd, TCVHUP, (char *)NULL); - mysignal(SIGHUP, SIG_DFL); - setpgid(0, 0); - close(fd); - } else { - error("Failed to disconnect from controlling tty."); - } - - debug("Setting controlling tty using TCSETCTTY."); - ioctl(*ttyfd, TCSETCTTY, NULL); - fd = open("/dev/tty", O_RDWR); - if (fd < 0) - error("%.100s: %.100s", ttyname, strerror(errno)); - close(*ttyfd); - *ttyfd = fd; -#else /* _UNICOS */ - - /* First disconnect from the old controlling tty. */ -#ifdef TIOCNOTTY - fd = open(_PATH_TTY, O_RDWR | O_NOCTTY); - if (fd >= 0) { - (void) ioctl(fd, TIOCNOTTY, NULL); - close(fd); - } -#endif /* TIOCNOTTY */ - if (setsid() < 0) - error("setsid: %.100s", strerror(errno)); - - /* - * Verify that we are successfully disconnected from the controlling - * tty. - */ - fd = open(_PATH_TTY, O_RDWR | O_NOCTTY); - if (fd >= 0) { - error("Failed to disconnect from controlling tty."); - close(fd); - } - /* Make it our controlling tty. */ -#ifdef TIOCSCTTY - debug("Setting controlling tty using TIOCSCTTY."); - if (ioctl(*ttyfd, TIOCSCTTY, NULL) < 0) - error("ioctl(TIOCSCTTY): %.100s", strerror(errno)); -#endif /* TIOCSCTTY */ -#ifdef HAVE_NEWS4 - if (setpgrp(0,0) < 0) - error("SETPGRP %s",strerror(errno)); -#endif /* HAVE_NEWS4 */ -#ifdef USE_VHANGUP - old = mysignal(SIGHUP, SIG_IGN); - vhangup(); - mysignal(SIGHUP, old); -#endif /* USE_VHANGUP */ - fd = open(ttyname, O_RDWR); - if (fd < 0) { - error("%.100s: %.100s", ttyname, strerror(errno)); - } else { -#ifdef USE_VHANGUP - close(*ttyfd); - *ttyfd = fd; -#else /* USE_VHANGUP */ - close(fd); -#endif /* USE_VHANGUP */ - } - /* Verify that we now have a controlling tty. */ - fd = open(_PATH_TTY, O_WRONLY); - if (fd < 0) - error("open /dev/tty failed - could not set controlling tty: %.100s", - strerror(errno)); - else - close(fd); -#endif /* _UNICOS */ -} - -/* Changes the window size associated with the pty. */ - -void -pty_change_window_size(int ptyfd, int row, int col, - int xpixel, int ypixel) -{ - struct winsize w; - - w.ws_row = row; - w.ws_col = col; - w.ws_xpixel = xpixel; - w.ws_ypixel = ypixel; - (void) ioctl(ptyfd, TIOCSWINSZ, &w); -} - -void -pty_setowner(struct passwd *pw, const char *ttyname) -{ - struct group *grp; - gid_t gid; - mode_t mode; - struct stat st; - - /* Determine the group to make the owner of the tty. */ - grp = getgrnam("tty"); - if (grp) { - gid = grp->gr_gid; - mode = S_IRUSR | S_IWUSR | S_IWGRP; - } else { - gid = pw->pw_gid; - mode = S_IRUSR | S_IWUSR | S_IWGRP | S_IWOTH; - } - - /* - * Change owner and mode of the tty as required. - * Warn but continue if filesystem is read-only and the uids match/ - * tty is owned by root. - */ - if (stat(ttyname, &st)) - fatal("stat(%.100s) failed: %.100s", ttyname, - strerror(errno)); - - if (st.st_uid != pw->pw_uid || st.st_gid != gid) { - if (chown(ttyname, pw->pw_uid, gid) < 0) { - if (errno == EROFS && - (st.st_uid == pw->pw_uid || st.st_uid == 0)) - error("chown(%.100s, %u, %u) failed: %.100s", - ttyname, (u_int)pw->pw_uid, (u_int)gid, - strerror(errno)); - else - fatal("chown(%.100s, %u, %u) failed: %.100s", - ttyname, (u_int)pw->pw_uid, (u_int)gid, - strerror(errno)); - } - } - - if ((st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) != mode) { - if (chmod(ttyname, mode) < 0) { - if (errno == EROFS && - (st.st_mode & (S_IRGRP | S_IROTH)) == 0) - error("chmod(%.100s, 0%o) failed: %.100s", - ttyname, (int)mode, strerror(errno)); - else - fatal("chmod(%.100s, 0%o) failed: %.100s", - ttyname, (int)mode, strerror(errno)); - } - } -} |