diff options
author | Damien Miller <djm@mindrot.org> | 1999-11-25 00:26:21 +1100 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 1999-11-25 00:26:21 +1100 |
commit | 95def09838fc61b37b6ea7cd5c234a465b4b129b (patch) | |
tree | 042744f76f40a326b873cb1c3690a6d7d966bc3e | |
parent | 4d2f15f895f4c795afc008aeff3fd2ceffbc44f4 (diff) |
- Merged very large OpenBSD source code reformat
- OpenBSD CVS updates
- [channels.c cipher.c compat.c log-client.c scp.c serverloop.c]
[ssh.h sshd.8 sshd.c]
syslog changes:
* Unified Logmessage for all auth-types, for success and for failed
* Standard connections get only ONE line in the LOG when level==LOG:
Auth-attempts are logged only, if authentication is:
a) successfull or
b) with passwd or
c) we had more than AUTH_FAIL_LOG failues
* many log() became verbose()
* old behaviour with level=VERBOSE
- [readconf.c readconf.h ssh.1 ssh.h sshconnect.c sshd.c]
tranfer s/key challenge/response data in SSH_SMSG_AUTH_TIS_CHALLENGE
messages. allows use of s/key in windows (ttssh, securecrt) and
ssh-1.2.27 clients without 'ssh -v', ok: niels@
- [sshd.8]
-V, for fallback to openssh in SSH2 compatibility mode
- [sshd.c]
fix sigchld race; cjc5@po.cwru.edu
-rw-r--r-- | ChangeLog | 23 | ||||
-rw-r--r-- | auth-krb4.c | 408 | ||||
-rw-r--r-- | auth-passwd.c | 344 | ||||
-rw-r--r-- | auth-rh-rsa.c | 174 | ||||
-rw-r--r-- | auth-rhosts.c | 476 | ||||
-rw-r--r-- | auth-rsa.c | 805 | ||||
-rw-r--r-- | auth-skey.c | 8 | ||||
-rw-r--r-- | authfd.c | 921 | ||||
-rw-r--r-- | authfd.h | 89 | ||||
-rw-r--r-- | authfile.c | 597 | ||||
-rw-r--r-- | bufaux.c | 228 | ||||
-rw-r--r-- | bufaux.h | 44 | ||||
-rw-r--r-- | buffer.c | 179 | ||||
-rw-r--r-- | buffer.h | 68 | ||||
-rw-r--r-- | canohost.c | 371 | ||||
-rw-r--r-- | channels.c | 2427 | ||||
-rw-r--r-- | channels.h | 62 | ||||
-rw-r--r-- | cipher.c | 409 | ||||
-rw-r--r-- | cipher.h | 97 | ||||
-rw-r--r-- | clientloop.c | 1524 | ||||
-rw-r--r-- | compat.c | 13 | ||||
-rw-r--r-- | compat.h | 4 | ||||
-rw-r--r-- | compress.c | 233 | ||||
-rw-r--r-- | compress.h | 40 | ||||
-rw-r--r-- | configure.in | 2 | ||||
-rw-r--r-- | crc32.c | 139 | ||||
-rw-r--r-- | crc32.h | 32 | ||||
-rw-r--r-- | deattack.c | 238 | ||||
-rw-r--r-- | deattack.h | 7 | ||||
-rw-r--r-- | fingerprint.c | 23 | ||||
-rw-r--r-- | fingerprint.h | 4 | ||||
-rw-r--r-- | getput.h | 33 | ||||
-rw-r--r-- | hostfile.c | 457 | ||||
-rw-r--r-- | includes.h | 28 | ||||
-rw-r--r-- | log-client.c | 81 | ||||
-rw-r--r-- | log-server.c | 238 | ||||
-rw-r--r-- | log.c | 219 | ||||
-rw-r--r-- | login.c | 193 | ||||
-rw-r--r-- | match.c | 125 | ||||
-rw-r--r-- | mpaux.c | 68 | ||||
-rw-r--r-- | mpaux.h | 42 | ||||
-rw-r--r-- | nchan.c | 119 | ||||
-rw-r--r-- | nchan.h | 26 | ||||
-rw-r--r-- | packet.c | 859 | ||||
-rw-r--r-- | packet.h | 103 | ||||
-rw-r--r-- | pty.c | 398 | ||||
-rw-r--r-- | pty.h | 48 | ||||
-rw-r--r-- | radix.c | 369 | ||||
-rw-r--r-- | readconf.c | 1097 | ||||
-rw-r--r-- | readconf.h | 170 | ||||
-rw-r--r-- | readpass.c | 179 | ||||
-rw-r--r-- | rsa.c | 231 | ||||
-rw-r--r-- | rsa.h | 41 | ||||
-rw-r--r-- | scp.c | 637 | ||||
-rw-r--r-- | servconf.c | 935 | ||||
-rw-r--r-- | servconf.h | 142 | ||||
-rw-r--r-- | serverloop.c | 1112 | ||||
-rw-r--r-- | ssh-add.c | 515 | ||||
-rw-r--r-- | ssh-agent.c | 1124 | ||||
-rw-r--r-- | ssh-keygen.c | 975 | ||||
-rw-r--r-- | ssh.1 | 12 | ||||
-rw-r--r-- | ssh.c | 1387 | ||||
-rw-r--r-- | ssh.h | 351 | ||||
-rw-r--r-- | sshconnect.c | 2851 | ||||
-rw-r--r-- | sshd.8 | 12 | ||||
-rw-r--r-- | sshd.c | 4503 | ||||
-rw-r--r-- | tildexpand.c | 114 | ||||
-rw-r--r-- | ttymodes.c | 479 | ||||
-rw-r--r-- | ttymodes.h | 188 | ||||
-rw-r--r-- | uidswap.c | 115 | ||||
-rw-r--r-- | uidswap.h | 50 | ||||
-rw-r--r-- | xmalloc.c | 73 | ||||
-rw-r--r-- | xmalloc.h | 42 |
73 files changed, 15163 insertions, 15567 deletions
@@ -1,3 +1,26 @@ | |||
1 | 19991124 | ||
2 | - Merged very large OpenBSD source code reformat | ||
3 | - OpenBSD CVS updates | ||
4 | - [channels.c cipher.c compat.c log-client.c scp.c serverloop.c] | ||
5 | [ssh.h sshd.8 sshd.c] | ||
6 | syslog changes: | ||
7 | * Unified Logmessage for all auth-types, for success and for failed | ||
8 | * Standard connections get only ONE line in the LOG when level==LOG: | ||
9 | Auth-attempts are logged only, if authentication is: | ||
10 | a) successfull or | ||
11 | b) with passwd or | ||
12 | c) we had more than AUTH_FAIL_LOG failues | ||
13 | * many log() became verbose() | ||
14 | * old behaviour with level=VERBOSE | ||
15 | - [readconf.c readconf.h ssh.1 ssh.h sshconnect.c sshd.c] | ||
16 | tranfer s/key challenge/response data in SSH_SMSG_AUTH_TIS_CHALLENGE | ||
17 | messages. allows use of s/key in windows (ttssh, securecrt) and | ||
18 | ssh-1.2.27 clients without 'ssh -v', ok: niels@ | ||
19 | - [sshd.8] | ||
20 | -V, for fallback to openssh in SSH2 compatibility mode | ||
21 | - [sshd.c] | ||
22 | fix sigchld race; cjc5@po.cwru.edu | ||
23 | |||
1 | 19991123 | 24 | 19991123 |
2 | - Added SuSE package files from Chris Saia <csaia@wtower.com> | 25 | - Added SuSE package files from Chris Saia <csaia@wtower.com> |
3 | - Restructured package-related files under packages/* | 26 | - Restructured package-related files under packages/* |
diff --git a/auth-krb4.c b/auth-krb4.c index 72acd47db..9f99533b1 100644 --- a/auth-krb4.c +++ b/auth-krb4.c | |||
@@ -1,13 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * Dug Song <dugsong@UMICH.EDU> | |
3 | auth-kerberos.c | 3 | * Kerberos v4 authentication and ticket-passing routines. |
4 | 4 | */ | |
5 | Dug Song <dugsong@UMICH.EDU> | ||
6 | |||
7 | Kerberos v4 authentication and ticket-passing routines. | ||
8 | |||
9 | $Id: auth-krb4.c,v 1.4 1999/11/15 04:25:10 damien Exp $ | ||
10 | */ | ||
11 | 5 | ||
12 | #include "includes.h" | 6 | #include "includes.h" |
13 | #include "packet.h" | 7 | #include "packet.h" |
@@ -20,216 +14,222 @@ char *ticket = NULL; | |||
20 | void | 14 | void |
21 | krb4_cleanup_proc(void *ignore) | 15 | krb4_cleanup_proc(void *ignore) |
22 | { | 16 | { |
23 | debug("krb4_cleanup_proc called"); | 17 | debug("krb4_cleanup_proc called"); |
24 | 18 | if (ticket) { | |
25 | if (ticket) { | 19 | (void) dest_tkt(); |
26 | (void) dest_tkt(); | 20 | xfree(ticket); |
27 | xfree(ticket); | 21 | ticket = NULL; |
28 | ticket = NULL; | 22 | } |
29 | } | ||
30 | } | 23 | } |
31 | 24 | ||
32 | int krb4_init(uid_t uid) | 25 | int |
26 | krb4_init(uid_t uid) | ||
33 | { | 27 | { |
34 | static int cleanup_registered = 0; | 28 | static int cleanup_registered = 0; |
35 | char *tkt_root = TKT_ROOT; | 29 | char *tkt_root = TKT_ROOT; |
36 | struct stat st; | 30 | struct stat st; |
37 | int fd; | 31 | int fd; |
38 | 32 | ||
39 | if (!ticket) { | 33 | if (!ticket) { |
40 | /* Set unique ticket string manually since we're still root. */ | 34 | /* Set unique ticket string manually since we're still root. */ |
41 | ticket = xmalloc(MAXPATHLEN); | 35 | ticket = xmalloc(MAXPATHLEN); |
42 | #ifdef AFS | 36 | #ifdef AFS |
43 | if (lstat("/ticket", &st) != -1) | 37 | if (lstat("/ticket", &st) != -1) |
44 | tkt_root = "/ticket/"; | 38 | tkt_root = "/ticket/"; |
45 | #endif /* AFS */ | 39 | #endif /* AFS */ |
46 | snprintf(ticket, MAXPATHLEN, "%s%d_%d", tkt_root, uid, getpid()); | 40 | snprintf(ticket, MAXPATHLEN, "%s%d_%d", tkt_root, uid, getpid()); |
47 | (void) krb_set_tkt_string(ticket); | 41 | (void) krb_set_tkt_string(ticket); |
48 | } | 42 | } |
49 | /* Register ticket cleanup in case of fatal error. */ | 43 | /* Register ticket cleanup in case of fatal error. */ |
50 | if (!cleanup_registered) { | 44 | if (!cleanup_registered) { |
51 | fatal_add_cleanup(krb4_cleanup_proc, NULL); | 45 | fatal_add_cleanup(krb4_cleanup_proc, NULL); |
52 | cleanup_registered = 1; | 46 | cleanup_registered = 1; |
53 | } | 47 | } |
54 | /* Try to create our ticket file. */ | 48 | /* Try to create our ticket file. */ |
55 | if ((fd = mkstemp(ticket)) != -1) { | 49 | if ((fd = mkstemp(ticket)) != -1) { |
56 | close(fd); | 50 | close(fd); |
57 | return 1; | 51 | return 1; |
58 | } | 52 | } |
59 | /* Ticket file exists - make sure user owns it (just passed ticket). */ | 53 | /* Ticket file exists - make sure user owns it (just passed ticket). */ |
60 | if (lstat(ticket, &st) != -1) { | 54 | if (lstat(ticket, &st) != -1) { |
61 | if (st.st_mode == (S_IFREG|S_IRUSR|S_IWUSR) && st.st_uid == uid) | 55 | if (st.st_mode == (S_IFREG | S_IRUSR | S_IWUSR) && |
62 | return 1; | 56 | st.st_uid == uid) |
63 | } | 57 | return 1; |
64 | /* Failure - cancel cleanup function, leaving bad ticket for inspection. */ | 58 | } |
65 | log("WARNING: bad ticket file %s", ticket); | 59 | /* Failure - cancel cleanup function, leaving bad ticket for inspection. */ |
66 | fatal_remove_cleanup(krb4_cleanup_proc, NULL); | 60 | log("WARNING: bad ticket file %s", ticket); |
67 | cleanup_registered = 0; | 61 | fatal_remove_cleanup(krb4_cleanup_proc, NULL); |
68 | xfree(ticket); | 62 | cleanup_registered = 0; |
69 | ticket = NULL; | 63 | xfree(ticket); |
70 | 64 | ticket = NULL; | |
71 | return 0; | 65 | |
66 | return 0; | ||
72 | } | 67 | } |
73 | 68 | ||
74 | int auth_krb4(const char *server_user, KTEXT auth, char **client) | 69 | int |
70 | auth_krb4(const char *server_user, KTEXT auth, char **client) | ||
75 | { | 71 | { |
76 | AUTH_DAT adat = { 0 }; | 72 | AUTH_DAT adat = {0}; |
77 | KTEXT_ST reply; | 73 | KTEXT_ST reply; |
78 | char instance[INST_SZ]; | 74 | char instance[INST_SZ]; |
79 | int r, s; | 75 | int r, s; |
80 | u_int cksum; | 76 | u_int cksum; |
81 | Key_schedule schedule; | 77 | Key_schedule schedule; |
82 | struct sockaddr_in local, foreign; | 78 | struct sockaddr_in local, foreign; |
83 | 79 | ||
84 | s = packet_get_connection_in(); | 80 | s = packet_get_connection_in(); |
85 | 81 | ||
86 | r = sizeof(local); | 82 | r = sizeof(local); |
87 | memset(&local, 0, sizeof(local)); | 83 | memset(&local, 0, sizeof(local)); |
88 | if (getsockname(s, (struct sockaddr *) &local, &r) < 0) | 84 | if (getsockname(s, (struct sockaddr *) & local, &r) < 0) |
89 | debug("getsockname failed: %.100s", strerror(errno)); | 85 | debug("getsockname failed: %.100s", strerror(errno)); |
90 | r = sizeof(foreign); | 86 | r = sizeof(foreign); |
91 | memset(&foreign, 0, sizeof(foreign)); | 87 | memset(&foreign, 0, sizeof(foreign)); |
92 | if (getpeername(s, (struct sockaddr *)&foreign, &r) < 0) { | 88 | if (getpeername(s, (struct sockaddr *) & foreign, &r) < 0) { |
93 | debug("getpeername failed: %.100s", strerror(errno)); | 89 | debug("getpeername failed: %.100s", strerror(errno)); |
94 | fatal_cleanup(); | 90 | fatal_cleanup(); |
95 | } | 91 | } |
96 | 92 | instance[0] = '*'; | |
97 | instance[0] = '*'; instance[1] = 0; | 93 | instance[1] = 0; |
98 | 94 | ||
99 | /* Get the encrypted request, challenge, and session key. */ | 95 | /* Get the encrypted request, challenge, and session key. */ |
100 | if ((r = krb_rd_req(auth, KRB4_SERVICE_NAME, instance, 0, &adat, ""))) { | 96 | if ((r = krb_rd_req(auth, KRB4_SERVICE_NAME, instance, 0, &adat, ""))) { |
101 | packet_send_debug("Kerberos V4 krb_rd_req: %.100s", krb_err_txt[r]); | 97 | packet_send_debug("Kerberos V4 krb_rd_req: %.100s", krb_err_txt[r]); |
102 | return 0; | 98 | return 0; |
103 | } | 99 | } |
104 | des_key_sched((des_cblock *)adat.session, schedule); | 100 | des_key_sched((des_cblock *) adat.session, schedule); |
105 | 101 | ||
106 | *client = xmalloc(MAX_K_NAME_SZ); | 102 | *client = xmalloc(MAX_K_NAME_SZ); |
107 | (void) snprintf(*client, MAX_K_NAME_SZ, "%s%s%s@%s", adat.pname, | 103 | (void) snprintf(*client, MAX_K_NAME_SZ, "%s%s%s@%s", adat.pname, |
108 | *adat.pinst ? "." : "", adat.pinst, adat.prealm); | 104 | *adat.pinst ? "." : "", adat.pinst, adat.prealm); |
109 | 105 | ||
110 | /* Check ~/.klogin authorization now. */ | 106 | /* Check ~/.klogin authorization now. */ |
111 | if (kuserok(&adat, (char *)server_user) != KSUCCESS) { | 107 | if (kuserok(&adat, (char *) server_user) != KSUCCESS) { |
112 | packet_send_debug("Kerberos V4 .klogin authorization failed!"); | 108 | packet_send_debug("Kerberos V4 .klogin authorization failed!"); |
113 | log("Kerberos V4 .klogin authorization failed for %s to account %s", | 109 | log("Kerberos V4 .klogin authorization failed for %s to account %s", |
114 | *client, server_user); | 110 | *client, server_user); |
115 | xfree(*client); | 111 | xfree(*client); |
116 | return 0; | 112 | return 0; |
117 | } | 113 | } |
118 | /* Increment the checksum, and return it encrypted with the session key. */ | 114 | /* Increment the checksum, and return it encrypted with the |
119 | cksum = adat.checksum + 1; | 115 | session key. */ |
120 | cksum = htonl(cksum); | 116 | cksum = adat.checksum + 1; |
121 | 117 | cksum = htonl(cksum); | |
122 | /* If we can't successfully encrypt the checksum, we send back an empty | 118 | |
123 | message, admitting our failure. */ | 119 | /* If we can't successfully encrypt the checksum, we send back an |
124 | if ((r = krb_mk_priv((u_char *)&cksum, reply.dat, sizeof(cksum)+1, | 120 | empty message, admitting our failure. */ |
125 | schedule, &adat.session, &local, &foreign)) < 0) { | 121 | if ((r = krb_mk_priv((u_char *) & cksum, reply.dat, sizeof(cksum) + 1, |
126 | packet_send_debug("Kerberos V4 mk_priv: (%d) %s", r, krb_err_txt[r]); | 122 | schedule, &adat.session, &local, &foreign)) < 0) { |
127 | reply.dat[0] = 0; | 123 | packet_send_debug("Kerberos V4 mk_priv: (%d) %s", r, krb_err_txt[r]); |
128 | reply.length = 0; | 124 | reply.dat[0] = 0; |
129 | } | 125 | reply.length = 0; |
130 | else reply.length = r; | 126 | } else |
131 | 127 | reply.length = r; | |
132 | /* Clear session key. */ | 128 | |
133 | memset(&adat.session, 0, sizeof(&adat.session)); | 129 | /* Clear session key. */ |
134 | 130 | memset(&adat.session, 0, sizeof(&adat.session)); | |
135 | packet_start(SSH_SMSG_AUTH_KERBEROS_RESPONSE); | 131 | |
136 | packet_put_string((char *) reply.dat, reply.length); | 132 | packet_start(SSH_SMSG_AUTH_KERBEROS_RESPONSE); |
137 | packet_send(); | 133 | packet_put_string((char *) reply.dat, reply.length); |
138 | packet_write_wait(); | 134 | packet_send(); |
139 | return 1; | 135 | packet_write_wait(); |
136 | return 1; | ||
140 | } | 137 | } |
141 | #endif /* KRB4 */ | 138 | #endif /* KRB4 */ |
142 | 139 | ||
143 | #ifdef AFS | 140 | #ifdef AFS |
144 | int auth_kerberos_tgt(struct passwd *pw, const char *string) | 141 | int |
142 | auth_kerberos_tgt(struct passwd *pw, const char *string) | ||
145 | { | 143 | { |
146 | CREDENTIALS creds; | 144 | CREDENTIALS creds; |
147 | 145 | ||
148 | if (!radix_to_creds(string, &creds)) { | 146 | if (!radix_to_creds(string, &creds)) { |
149 | log("Protocol error decoding Kerberos V4 tgt"); | 147 | log("Protocol error decoding Kerberos V4 tgt"); |
150 | packet_send_debug("Protocol error decoding Kerberos V4 tgt"); | 148 | packet_send_debug("Protocol error decoding Kerberos V4 tgt"); |
151 | goto auth_kerberos_tgt_failure; | 149 | goto auth_kerberos_tgt_failure; |
152 | } | 150 | } |
153 | if (strncmp(creds.service, "", 1) == 0) /* backward compatibility */ | 151 | if (strncmp(creds.service, "", 1) == 0) /* backward compatibility */ |
154 | strlcpy(creds.service, "krbtgt", sizeof creds.service); | 152 | strlcpy(creds.service, "krbtgt", sizeof creds.service); |
155 | 153 | ||
156 | if (strcmp(creds.service, "krbtgt")) { | 154 | if (strcmp(creds.service, "krbtgt")) { |
157 | log("Kerberos V4 tgt (%s%s%s@%s) rejected for %s", creds.pname, | 155 | log("Kerberos V4 tgt (%s%s%s@%s) rejected for %s", creds.pname, |
158 | creds.pinst[0] ? "." : "", creds.pinst, creds.realm, pw->pw_name); | 156 | creds.pinst[0] ? "." : "", creds.pinst, creds.realm, |
159 | packet_send_debug("Kerberos V4 tgt (%s%s%s@%s) rejected for %s", | 157 | pw->pw_name); |
160 | creds.pname, creds.pinst[0] ? "." : "", creds.pinst, | 158 | packet_send_debug("Kerberos V4 tgt (%s%s%s@%s) rejected for %s", |
161 | creds.realm, pw->pw_name); | 159 | creds.pname, creds.pinst[0] ? "." : "", creds.pinst, |
162 | goto auth_kerberos_tgt_failure; | 160 | creds.realm, pw->pw_name); |
163 | } | 161 | goto auth_kerberos_tgt_failure; |
164 | if (!krb4_init(pw->pw_uid)) | 162 | } |
165 | goto auth_kerberos_tgt_failure; | 163 | if (!krb4_init(pw->pw_uid)) |
166 | 164 | goto auth_kerberos_tgt_failure; | |
167 | if (in_tkt(creds.pname, creds.pinst) != KSUCCESS) | 165 | |
168 | goto auth_kerberos_tgt_failure; | 166 | if (in_tkt(creds.pname, creds.pinst) != KSUCCESS) |
169 | 167 | goto auth_kerberos_tgt_failure; | |
170 | if (save_credentials(creds.service, creds.instance, creds.realm, | 168 | |
171 | creds.session, creds.lifetime, creds.kvno, | 169 | if (save_credentials(creds.service, creds.instance, creds.realm, |
172 | &creds.ticket_st, creds.issue_date) != KSUCCESS) { | 170 | creds.session, creds.lifetime, creds.kvno, |
173 | packet_send_debug("Kerberos V4 tgt refused: couldn't save credentials"); | 171 | &creds.ticket_st, creds.issue_date) != KSUCCESS) { |
174 | goto auth_kerberos_tgt_failure; | 172 | packet_send_debug("Kerberos V4 tgt refused: couldn't save credentials"); |
175 | } | 173 | goto auth_kerberos_tgt_failure; |
176 | /* Successful authentication, passed all checks. */ | 174 | } |
177 | chown(tkt_string(), pw->pw_uid, pw->pw_gid); | 175 | /* Successful authentication, passed all checks. */ |
178 | 176 | chown(tkt_string(), pw->pw_uid, pw->pw_gid); | |
179 | packet_send_debug("Kerberos V4 tgt accepted (%s.%s@%s, %s%s%s@%s)", | 177 | |
180 | creds.service, creds.instance, creds.realm, creds.pname, | 178 | packet_send_debug("Kerberos V4 tgt accepted (%s.%s@%s, %s%s%s@%s)", |
181 | creds.pinst[0] ? "." : "", creds.pinst, creds.realm); | 179 | creds.service, creds.instance, creds.realm, creds.pname, |
182 | memset(&creds, 0, sizeof(creds)); | 180 | creds.pinst[0] ? "." : "", creds.pinst, creds.realm); |
183 | packet_start(SSH_SMSG_SUCCESS); | 181 | memset(&creds, 0, sizeof(creds)); |
184 | packet_send(); | 182 | packet_start(SSH_SMSG_SUCCESS); |
185 | packet_write_wait(); | 183 | packet_send(); |
186 | return 1; | 184 | packet_write_wait(); |
187 | 185 | return 1; | |
188 | auth_kerberos_tgt_failure: | 186 | |
189 | krb4_cleanup_proc(NULL); | 187 | auth_kerberos_tgt_failure: |
190 | memset(&creds, 0, sizeof(creds)); | 188 | krb4_cleanup_proc(NULL); |
191 | packet_start(SSH_SMSG_FAILURE); | 189 | memset(&creds, 0, sizeof(creds)); |
192 | packet_send(); | 190 | packet_start(SSH_SMSG_FAILURE); |
193 | packet_write_wait(); | 191 | packet_send(); |
194 | return 0; | 192 | packet_write_wait(); |
193 | return 0; | ||
195 | } | 194 | } |
196 | 195 | ||
197 | int auth_afs_token(struct passwd *pw, const char *token_string) | 196 | int |
197 | auth_afs_token(struct passwd *pw, const char *token_string) | ||
198 | { | 198 | { |
199 | CREDENTIALS creds; | 199 | CREDENTIALS creds; |
200 | uid_t uid = pw->pw_uid; | 200 | uid_t uid = pw->pw_uid; |
201 | 201 | ||
202 | if (!radix_to_creds(token_string, &creds)) { | 202 | if (!radix_to_creds(token_string, &creds)) { |
203 | log("Protocol error decoding AFS token"); | 203 | log("Protocol error decoding AFS token"); |
204 | packet_send_debug("Protocol error decoding AFS token"); | 204 | packet_send_debug("Protocol error decoding AFS token"); |
205 | packet_start(SSH_SMSG_FAILURE); | 205 | packet_start(SSH_SMSG_FAILURE); |
206 | packet_send(); | 206 | packet_send(); |
207 | packet_write_wait(); | 207 | packet_write_wait(); |
208 | return 0; | 208 | return 0; |
209 | } | 209 | } |
210 | if (strncmp(creds.service, "", 1) == 0) /* backward compatibility */ | 210 | if (strncmp(creds.service, "", 1) == 0) /* backward compatibility */ |
211 | strlcpy(creds.service, "afs", sizeof creds.service); | 211 | strlcpy(creds.service, "afs", sizeof creds.service); |
212 | 212 | ||
213 | if (strncmp(creds.pname, "AFS ID ", 7) == 0) | 213 | if (strncmp(creds.pname, "AFS ID ", 7) == 0) |
214 | uid = atoi(creds.pname + 7); | 214 | uid = atoi(creds.pname + 7); |
215 | 215 | ||
216 | if (kafs_settoken(creds.realm, uid, &creds)) { | 216 | if (kafs_settoken(creds.realm, uid, &creds)) { |
217 | log("AFS token (%s@%s) rejected for %s", creds.pname, creds.realm, | 217 | log("AFS token (%s@%s) rejected for %s", creds.pname, creds.realm, |
218 | pw->pw_name); | 218 | pw->pw_name); |
219 | packet_send_debug("AFS token (%s@%s) rejected for %s", creds.pname, | 219 | packet_send_debug("AFS token (%s@%s) rejected for %s", creds.pname, |
220 | creds.realm, pw->pw_name); | 220 | creds.realm, pw->pw_name); |
221 | memset(&creds, 0, sizeof(creds)); | 221 | memset(&creds, 0, sizeof(creds)); |
222 | packet_start(SSH_SMSG_FAILURE); | 222 | packet_start(SSH_SMSG_FAILURE); |
223 | packet_send(); | 223 | packet_send(); |
224 | packet_write_wait(); | 224 | packet_write_wait(); |
225 | return 0; | 225 | return 0; |
226 | } | 226 | } |
227 | packet_send_debug("AFS token accepted (%s@%s, %s@%s)", creds.service, | 227 | packet_send_debug("AFS token accepted (%s@%s, %s@%s)", creds.service, |
228 | creds.realm, creds.pname, creds.realm); | 228 | creds.realm, creds.pname, creds.realm); |
229 | memset(&creds, 0, sizeof(creds)); | 229 | memset(&creds, 0, sizeof(creds)); |
230 | packet_start(SSH_SMSG_SUCCESS); | 230 | packet_start(SSH_SMSG_SUCCESS); |
231 | packet_send(); | 231 | packet_send(); |
232 | packet_write_wait(); | 232 | packet_write_wait(); |
233 | return 1; | 233 | return 1; |
234 | } | 234 | } |
235 | #endif /* AFS */ | 235 | #endif /* AFS */ |
diff --git a/auth-passwd.c b/auth-passwd.c index a08bab3af..d3914fca3 100644 --- a/auth-passwd.c +++ b/auth-passwd.c | |||
@@ -1,30 +1,22 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | |
3 | auth-passwd.c | 3 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
4 | 4 | * All rights reserved | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Created: Sat Mar 18 05:11:38 1995 ylo |
6 | 6 | * Password authentication. This file contains the functions to check whether | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * the password is valid for the user. |
8 | All rights reserved | 8 | */ |
9 | |||
10 | Created: Sat Mar 18 05:11:38 1995 ylo | ||
11 | |||
12 | Password authentication. This file contains the functions to check whether | ||
13 | the password is valid for the user. | ||
14 | |||
15 | */ | ||
16 | 9 | ||
17 | #include "includes.h" | 10 | #include "includes.h" |
18 | 11 | ||
19 | #ifndef HAVE_PAM | 12 | #ifndef HAVE_PAM |
20 | 13 | ||
21 | RCSID("$Id: auth-passwd.c,v 1.5 1999/11/19 04:53:20 damien Exp $"); | 14 | RCSID("$Id: auth-passwd.c,v 1.6 1999/11/24 13:26:21 damien Exp $"); |
22 | 15 | ||
23 | #include "packet.h" | 16 | #include "packet.h" |
24 | #include "ssh.h" | 17 | #include "ssh.h" |
25 | #include "servconf.h" | 18 | #include "servconf.h" |
26 | #include "xmalloc.h" | 19 | #include "xmalloc.h" |
27 | #include "config.h" | ||
28 | 20 | ||
29 | #ifdef HAVE_SHADOW_H | 21 | #ifdef HAVE_SHADOW_H |
30 | #include <shadow.h> | 22 | #include <shadow.h> |
@@ -34,185 +26,189 @@ RCSID("$Id: auth-passwd.c,v 1.5 1999/11/19 04:53:20 damien Exp $"); | |||
34 | #include "md5crypt.h" | 26 | #include "md5crypt.h" |
35 | #endif | 27 | #endif |
36 | 28 | ||
37 | /* Don't need anything from here if we are using PAM */ | 29 | /* |
38 | 30 | * Tries to authenticate the user using password. Returns true if | |
39 | /* Tries to authenticate the user using password. Returns true if | 31 | * authentication succeeds. |
40 | authentication succeeds. */ | 32 | */ |
41 | 33 | int | |
42 | int auth_password(struct passwd *pw, const char *password) | 34 | auth_password(struct passwd * pw, const char *password) |
43 | { | 35 | { |
44 | extern ServerOptions options; | 36 | extern ServerOptions options; |
45 | char *encrypted_password; | 37 | char *encrypted_password; |
46 | #ifdef HAVE_SHADOW_H | 38 | #ifdef HAVE_SHADOW_H |
47 | struct spwd *spw; | 39 | struct spwd *spw; |
48 | #endif | 40 | #endif |
49 | 41 | ||
50 | if (pw->pw_uid == 0 && options.permit_root_login == 2) | 42 | if (pw->pw_uid == 0 && options.permit_root_login == 2) { |
51 | { | 43 | /* Server does not permit root login with password */ |
52 | /*packet_send_debug("Server does not permit root login with password.");*/ | 44 | return 0; |
53 | return 0; | 45 | } |
54 | } | 46 | if (*password == '\0' && options.permit_empty_passwd == 0) { |
55 | 47 | /* Server does not permit empty password login */ | |
56 | if (*password == '\0' && options.permit_empty_passwd == 0) | 48 | return 0; |
57 | { | 49 | } |
58 | /*packet_send_debug("Server does not permit empty password login.");*/ | 50 | /* deny if no user. */ |
59 | return 0; | 51 | if (pw == NULL) |
60 | } | 52 | return 0; |
61 | |||
62 | /* deny if no user. */ | ||
63 | if (pw == NULL) | ||
64 | return 0; | ||
65 | 53 | ||
66 | #ifdef SKEY | 54 | #ifdef SKEY |
67 | if (options.skey_authentication == 1) { | 55 | if (options.skey_authentication == 1) { |
68 | if (strncasecmp(password, "s/key", 5) == 0) { | 56 | if (strncasecmp(password, "s/key", 5) == 0) { |
69 | char *skeyinfo = skey_keyinfo(pw->pw_name); | 57 | char *skeyinfo = skey_keyinfo(pw->pw_name); |
70 | if(skeyinfo == NULL){ | 58 | if (skeyinfo == NULL) { |
71 | debug("generating fake skeyinfo for %.100s.", pw->pw_name); | 59 | debug("generating fake skeyinfo for %.100s.", |
72 | skeyinfo = skey_fake_keyinfo(pw->pw_name); | 60 | pw->pw_name); |
73 | } | 61 | skeyinfo = skey_fake_keyinfo(pw->pw_name); |
74 | if(skeyinfo != NULL) | 62 | } |
75 | packet_send_debug(skeyinfo); | 63 | if (skeyinfo != NULL) |
76 | /* Try again. */ | 64 | packet_send_debug(skeyinfo); |
77 | return 0; | 65 | /* Try again. */ |
78 | } | 66 | return 0; |
79 | else if (skey_haskey(pw->pw_name) == 0 && | 67 | } else if (skey_haskey(pw->pw_name) == 0 && |
80 | skey_passcheck(pw->pw_name, (char *)password) != -1) { | 68 | skey_passcheck(pw->pw_name, (char *) password) != -1) { |
81 | /* Authentication succeeded. */ | 69 | /* Authentication succeeded. */ |
82 | return 1; | 70 | return 1; |
83 | } | 71 | } |
84 | /* Fall back to ordinary passwd authentication. */ | 72 | /* Fall back to ordinary passwd authentication. */ |
85 | } | 73 | } |
86 | #endif | 74 | #endif |
87 | 75 | ||
88 | #if defined(KRB4) | 76 | #if defined(KRB4) |
89 | /* Support for Kerberos v4 authentication - Dug Song <dugsong@UMICH.EDU> */ | 77 | /* Support for Kerberos v4 authentication - Dug Song |
90 | if (options.kerberos_authentication) | 78 | <dugsong@UMICH.EDU> */ |
91 | { | 79 | if (options.kerberos_authentication) { |
92 | AUTH_DAT adata; | 80 | AUTH_DAT adata; |
93 | KTEXT_ST tkt; | 81 | KTEXT_ST tkt; |
94 | struct hostent *hp; | 82 | struct hostent *hp; |
95 | unsigned long faddr; | 83 | unsigned long faddr; |
96 | char localhost[MAXHOSTNAMELEN]; | 84 | char localhost[MAXHOSTNAMELEN]; |
97 | char phost[INST_SZ]; | 85 | char phost[INST_SZ]; |
98 | char realm[REALM_SZ]; | 86 | char realm[REALM_SZ]; |
99 | int r; | 87 | int r; |
100 | 88 | ||
101 | /* Try Kerberos password authentication only for non-root | 89 | /* Try Kerberos password authentication only for non-root |
102 | users and only if Kerberos is installed. */ | 90 | users and only if Kerberos is installed. */ |
103 | if (pw->pw_uid != 0 && krb_get_lrealm(realm, 1) == KSUCCESS) { | 91 | if (pw->pw_uid != 0 && krb_get_lrealm(realm, 1) == KSUCCESS) { |
104 | 92 | ||
105 | /* Set up our ticket file. */ | 93 | /* Set up our ticket file. */ |
106 | if (!krb4_init(pw->pw_uid)) { | 94 | if (!krb4_init(pw->pw_uid)) { |
107 | log("Couldn't initialize Kerberos ticket file for %s!", pw->pw_name); | 95 | log("Couldn't initialize Kerberos ticket file for %s!", |
108 | goto kerberos_auth_failure; | 96 | pw->pw_name); |
109 | } | 97 | goto kerberos_auth_failure; |
110 | /* Try to get TGT using our password. */ | 98 | } |
111 | r = krb_get_pw_in_tkt((char *)pw->pw_name, "", realm, "krbtgt", realm, | 99 | /* Try to get TGT using our password. */ |
112 | DEFAULT_TKT_LIFE, (char *)password); | 100 | r = krb_get_pw_in_tkt((char *) pw->pw_name, "", |
113 | if (r != INTK_OK) { | 101 | realm, "krbtgt", realm, |
114 | packet_send_debug("Kerberos V4 password authentication for %s " | 102 | DEFAULT_TKT_LIFE, (char *) password); |
115 | "failed: %s", pw->pw_name, krb_err_txt[r]); | 103 | if (r != INTK_OK) { |
116 | goto kerberos_auth_failure; | 104 | packet_send_debug("Kerberos V4 password " |
105 | "authentication for %s failed: %s", | ||
106 | pw->pw_name, krb_err_txt[r]); | ||
107 | goto kerberos_auth_failure; | ||
108 | } | ||
109 | /* Successful authentication. */ | ||
110 | chown(tkt_string(), pw->pw_uid, pw->pw_gid); | ||
111 | |||
112 | /* | ||
113 | * Now that we have a TGT, try to get a local | ||
114 | * "rcmd" ticket to ensure that we are not talking | ||
115 | * to a bogus Kerberos server. | ||
116 | */ | ||
117 | (void) gethostname(localhost, sizeof(localhost)); | ||
118 | (void) strlcpy(phost, (char *) krb_get_phost(localhost), | ||
119 | INST_SZ); | ||
120 | r = krb_mk_req(&tkt, KRB4_SERVICE_NAME, phost, realm, 33); | ||
121 | |||
122 | if (r == KSUCCESS) { | ||
123 | if (!(hp = gethostbyname(localhost))) { | ||
124 | log("Couldn't get local host address!"); | ||
125 | goto kerberos_auth_failure; | ||
126 | } | ||
127 | memmove((void *) &faddr, (void *) hp->h_addr, | ||
128 | sizeof(faddr)); | ||
129 | |||
130 | /* Verify our "rcmd" ticket. */ | ||
131 | r = krb_rd_req(&tkt, KRB4_SERVICE_NAME, phost, | ||
132 | faddr, &adata, ""); | ||
133 | if (r == RD_AP_UNDEC) { | ||
134 | /* | ||
135 | * Probably didn't have a srvtab on | ||
136 | * localhost. Allow login. | ||
137 | */ | ||
138 | log("Kerberos V4 TGT for %s unverifiable, " | ||
139 | "no srvtab installed? krb_rd_req: %s", | ||
140 | pw->pw_name, krb_err_txt[r]); | ||
141 | } else if (r != KSUCCESS) { | ||
142 | log("Kerberos V4 %s ticket unverifiable: %s", | ||
143 | KRB4_SERVICE_NAME, krb_err_txt[r]); | ||
144 | goto kerberos_auth_failure; | ||
145 | } | ||
146 | } else if (r == KDC_PR_UNKNOWN) { | ||
147 | /* Allow login if no rcmd service exists, | ||
148 | but log the error. */ | ||
149 | log("Kerberos V4 TGT for %s unverifiable: %s; %s.%s " | ||
150 | "not registered, or srvtab is wrong?", pw->pw_name, | ||
151 | krb_err_txt[r], KRB4_SERVICE_NAME, phost); | ||
152 | } else { | ||
153 | /* TGT is bad, forget it. Possibly | ||
154 | spoofed! */ | ||
155 | packet_send_debug("WARNING: Kerberos V4 TGT " | ||
156 | "possibly spoofed for %s: %s", | ||
157 | pw->pw_name, krb_err_txt[r]); | ||
158 | goto kerberos_auth_failure; | ||
159 | } | ||
160 | |||
161 | /* Authentication succeeded. */ | ||
162 | return 1; | ||
163 | |||
164 | kerberos_auth_failure: | ||
165 | krb4_cleanup_proc(NULL); | ||
166 | |||
167 | if (!options.kerberos_or_local_passwd) | ||
168 | return 0; | ||
169 | } else { | ||
170 | /* Logging in as root or no local Kerberos realm. */ | ||
171 | packet_send_debug("Unable to authenticate to Kerberos."); | ||
172 | } | ||
173 | /* Fall back to ordinary passwd authentication. */ | ||
117 | } | 174 | } |
118 | /* Successful authentication. */ | 175 | #endif /* KRB4 */ |
119 | chown(tkt_string(), pw->pw_uid, pw->pw_gid); | 176 | |
120 | 177 | /* Check for users with no password. */ | |
121 | /* Now that we have a TGT, try to get a local "rcmd" ticket to | 178 | if (strcmp(password, "") == 0 && strcmp(pw->pw_passwd, "") == 0) { |
122 | ensure that we are not talking to a bogus Kerberos server. */ | 179 | packet_send_debug("Login permitted without a password " |
123 | (void) gethostname(localhost, sizeof(localhost)); | 180 | "because the account has no password."); |
124 | (void) strlcpy(phost, (char *)krb_get_phost(localhost), INST_SZ); | 181 | return 1; |
125 | r = krb_mk_req(&tkt, KRB4_SERVICE_NAME, phost, realm, 33); | ||
126 | |||
127 | if (r == KSUCCESS) { | ||
128 | if (!(hp = gethostbyname(localhost))) { | ||
129 | log("Couldn't get local host address!"); | ||
130 | goto kerberos_auth_failure; | ||
131 | } | ||
132 | memmove((void *)&faddr, (void *)hp->h_addr, sizeof(faddr)); | ||
133 | |||
134 | /* Verify our "rcmd" ticket. */ | ||
135 | r = krb_rd_req(&tkt, KRB4_SERVICE_NAME, phost, faddr, &adata, ""); | ||
136 | if (r == RD_AP_UNDEC) { | ||
137 | /* Probably didn't have a srvtab on localhost. Allow login. */ | ||
138 | log("Kerberos V4 TGT for %s unverifiable, no srvtab installed? " | ||
139 | "krb_rd_req: %s", pw->pw_name, krb_err_txt[r]); | ||
140 | } | ||
141 | else if (r != KSUCCESS) { | ||
142 | log("Kerberos V4 %s ticket unverifiable: %s", | ||
143 | KRB4_SERVICE_NAME, krb_err_txt[r]); | ||
144 | goto kerberos_auth_failure; | ||
145 | } | ||
146 | } | ||
147 | else if (r == KDC_PR_UNKNOWN) { | ||
148 | /* Allow login if no rcmd service exists, but log the error. */ | ||
149 | log("Kerberos V4 TGT for %s unverifiable: %s; %s.%s " | ||
150 | "not registered, or srvtab is wrong?", pw->pw_name, | ||
151 | krb_err_txt[r], KRB4_SERVICE_NAME, phost); | ||
152 | } | ||
153 | else { | ||
154 | /* TGT is bad, forget it. Possibly spoofed! */ | ||
155 | packet_send_debug("WARNING: Kerberos V4 TGT possibly spoofed for" | ||
156 | "%s: %s", pw->pw_name, krb_err_txt[r]); | ||
157 | goto kerberos_auth_failure; | ||
158 | } | 182 | } |
159 | |||
160 | /* Authentication succeeded. */ | ||
161 | return 1; | ||
162 | |||
163 | kerberos_auth_failure: | ||
164 | krb4_cleanup_proc(NULL); | ||
165 | |||
166 | if (!options.kerberos_or_local_passwd) | ||
167 | return 0; | ||
168 | } | ||
169 | else { | ||
170 | /* Logging in as root or no local Kerberos realm. */ | ||
171 | packet_send_debug("Unable to authenticate to Kerberos."); | ||
172 | } | ||
173 | /* Fall back to ordinary passwd authentication. */ | ||
174 | } | ||
175 | #endif /* KRB4 */ | ||
176 | |||
177 | /* Check for users with no password. */ | ||
178 | if (strcmp(password, "") == 0 && strcmp(pw->pw_passwd, "") == 0) | ||
179 | { | ||
180 | packet_send_debug("Login permitted without a password because the account has no password."); | ||
181 | return 1; /* The user has no password and an empty password was tried. */ | ||
182 | } | ||
183 | 183 | ||
184 | #ifdef HAVE_SHADOW_H | 184 | #ifdef HAVE_SHADOW_H |
185 | spw = getspnam(pw->pw_name); | 185 | spw = getspnam(pw->pw_name); |
186 | if (spw == NULL) | 186 | if (spw == NULL) |
187 | return(0); | 187 | return(0); |
188 | 188 | ||
189 | if ((spw->sp_namp == NULL) || (strcmp(pw->pw_name, spw->sp_namp) != 0)) | 189 | if ((spw->sp_namp == NULL) || (strcmp(pw->pw_name, spw->sp_namp) != 0)) |
190 | fatal("Shadow lookup returned garbage."); | 190 | fatal("Shadow lookup returned garbage."); |
191 | 191 | ||
192 | if (strlen(spw->sp_pwdp) < 3) | 192 | if (strlen(spw->sp_pwdp) < 3) |
193 | return(0); | 193 | return(0); |
194 | 194 | ||
195 | /* Encrypt the candidate password using the proper salt. */ | 195 | /* Encrypt the candidate password using the proper salt. */ |
196 | #ifdef HAVE_MD5_PASSWORDS | 196 | #ifdef HAVE_MD5_PASSWORDS |
197 | if (is_md5_salt(spw->sp_pwdp)) | 197 | if (is_md5_salt(spw->sp_pwdp)) |
198 | encrypted_password = md5_crypt(password, spw->sp_pwdp); | 198 | encrypted_password = md5_crypt(password, spw->sp_pwdp); |
199 | else | 199 | else |
200 | encrypted_password = crypt(password, spw->sp_pwdp); | 200 | encrypted_password = crypt(password, spw->sp_pwdp); |
201 | #else /* HAVE_MD5_PASSWORDS */ | 201 | #else /* HAVE_MD5_PASSWORDS */ |
202 | encrypted_password = crypt(password, spw->sp_pwdp); | 202 | encrypted_password = crypt(password, spw->sp_pwdp); |
203 | #endif /* HAVE_MD5_PASSWORDS */ | 203 | #endif /* HAVE_MD5_PASSWORDS */ |
204 | 204 | /* Authentication is accepted if the encrypted passwords are identical. */ | |
205 | /* Authentication is accepted if the encrypted passwords are identical. */ | 205 | return (strcmp(encrypted_password, spw->sp_pwdp) == 0); |
206 | return (strcmp(encrypted_password, spw->sp_pwdp) == 0); | ||
207 | #else /* !HAVE_SHADOW_H */ | 206 | #else /* !HAVE_SHADOW_H */ |
207 | encrypted_password = crypt(password, | ||
208 | (pw->pw_passwd[0] && pw->pw_passwd[1]) ? pw->pw_passwd : "xx"); | ||
208 | 209 | ||
209 | /* Encrypt the candidate password using the proper salt. */ | 210 | /* Authentication is accepted if the encrypted passwords are identical. */ |
210 | encrypted_password = crypt(password, | 211 | return (strcmp(encrypted_password, pw->pw_passwd) == 0); |
211 | (pw->pw_passwd[0] && pw->pw_passwd[1]) ? | ||
212 | pw->pw_passwd : "xx"); | ||
213 | /* Authentication is accepted if the encrypted passwords are identical. */ | ||
214 | return (strcmp(encrypted_password, pw->pw_passwd) == 0); | ||
215 | #endif /* !HAVE_SHADOW_H */ | 212 | #endif /* !HAVE_SHADOW_H */ |
216 | } | 213 | } |
217 | |||
218 | #endif /* !HAVE_PAM */ | 214 | #endif /* !HAVE_PAM */ |
diff --git a/auth-rh-rsa.c b/auth-rh-rsa.c index 68e0b829e..4e9a383a2 100644 --- a/auth-rh-rsa.c +++ b/auth-rh-rsa.c | |||
@@ -1,21 +1,21 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * | |
3 | auth-rh-rsa.c | 3 | * auth-rh-rsa.c |
4 | 4 | * | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
6 | 6 | * | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
8 | All rights reserved | 8 | * All rights reserved |
9 | 9 | * | |
10 | Created: Sun May 7 03:08:06 1995 ylo | 10 | * Created: Sun May 7 03:08:06 1995 ylo |
11 | 11 | * | |
12 | Rhosts or /etc/hosts.equiv authentication combined with RSA host | 12 | * Rhosts or /etc/hosts.equiv authentication combined with RSA host |
13 | authentication. | 13 | * authentication. |
14 | 14 | * | |
15 | */ | 15 | */ |
16 | 16 | ||
17 | #include "includes.h" | 17 | #include "includes.h" |
18 | RCSID("$Id: auth-rh-rsa.c,v 1.5 1999/11/16 02:37:16 damien Exp $"); | 18 | RCSID("$Id: auth-rh-rsa.c,v 1.6 1999/11/24 13:26:21 damien Exp $"); |
19 | 19 | ||
20 | #include "packet.h" | 20 | #include "packet.h" |
21 | #include "ssh.h" | 21 | #include "ssh.h" |
@@ -26,78 +26,76 @@ RCSID("$Id: auth-rh-rsa.c,v 1.5 1999/11/16 02:37:16 damien Exp $"); | |||
26 | /* Tries to authenticate the user using the .rhosts file and the host using | 26 | /* Tries to authenticate the user using the .rhosts file and the host using |
27 | its host key. Returns true if authentication succeeds. */ | 27 | its host key. Returns true if authentication succeeds. */ |
28 | 28 | ||
29 | int auth_rhosts_rsa(struct passwd *pw, const char *client_user, | 29 | int |
30 | BIGNUM *client_host_key_e, BIGNUM *client_host_key_n) | 30 | auth_rhosts_rsa(struct passwd *pw, const char *client_user, |
31 | BIGNUM *client_host_key_e, BIGNUM *client_host_key_n) | ||
31 | { | 32 | { |
32 | extern ServerOptions options; | 33 | extern ServerOptions options; |
33 | const char *canonical_hostname; | 34 | const char *canonical_hostname; |
34 | HostStatus host_status; | 35 | HostStatus host_status; |
35 | BIGNUM *ke, *kn; | 36 | BIGNUM *ke, *kn; |
36 | 37 | ||
37 | debug("Trying rhosts with RSA host authentication for %.100s", client_user); | 38 | debug("Trying rhosts with RSA host authentication for %.100s", client_user); |
38 | 39 | ||
39 | /* Check if we would accept it using rhosts authentication. */ | 40 | /* Check if we would accept it using rhosts authentication. */ |
40 | if (!auth_rhosts(pw, client_user)) | 41 | if (!auth_rhosts(pw, client_user)) |
41 | return 0; | 42 | return 0; |
42 | 43 | ||
43 | canonical_hostname = get_canonical_hostname(); | 44 | canonical_hostname = get_canonical_hostname(); |
44 | 45 | ||
45 | debug("Rhosts RSA authentication: canonical host %.900s", | 46 | debug("Rhosts RSA authentication: canonical host %.900s", |
46 | canonical_hostname); | 47 | canonical_hostname); |
47 | 48 | ||
48 | /* Check if we know the host and its host key. */ | 49 | /* Check if we know the host and its host key. */ |
49 | /* Check system-wide host file. */ | 50 | ke = BN_new(); |
50 | ke = BN_new(); | 51 | kn = BN_new(); |
51 | kn = BN_new(); | 52 | host_status = check_host_in_hostfile(SSH_SYSTEM_HOSTFILE, canonical_hostname, |
52 | host_status = check_host_in_hostfile(SSH_SYSTEM_HOSTFILE, canonical_hostname, | 53 | client_host_key_e, client_host_key_n, |
53 | client_host_key_e, client_host_key_n, ke, kn); | 54 | ke, kn); |
54 | 55 | ||
55 | /* Check user host file unless ignored. */ | 56 | /* Check user host file unless ignored. */ |
56 | if (host_status != HOST_OK && !options.ignore_user_known_hosts) { | 57 | if (host_status != HOST_OK && !options.ignore_user_known_hosts) { |
57 | struct stat st; | 58 | struct stat st; |
58 | char *user_hostfile = tilde_expand_filename(SSH_USER_HOSTFILE, pw->pw_uid); | 59 | char *user_hostfile = tilde_expand_filename(SSH_USER_HOSTFILE, pw->pw_uid); |
59 | /* Check file permissions of SSH_USER_HOSTFILE, | 60 | /* Check file permissions of SSH_USER_HOSTFILE, auth_rsa() |
60 | auth_rsa() did already check pw->pw_dir, but there is a race XXX */ | 61 | did already check pw->pw_dir, but there is a race XXX */ |
61 | if (options.strict_modes && | 62 | if (options.strict_modes && |
62 | (stat(user_hostfile, &st) == 0) && | 63 | (stat(user_hostfile, &st) == 0) && |
63 | ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || | 64 | ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || |
64 | (st.st_mode & 022) != 0)) { | 65 | (st.st_mode & 022) != 0)) { |
65 | log("Rhosts RSA authentication refused for %.100s: bad owner or modes for %.200s", | 66 | log("Rhosts RSA authentication refused for %.100s: bad owner or modes for %.200s", |
66 | pw->pw_name, user_hostfile); | 67 | pw->pw_name, user_hostfile); |
67 | } else { | 68 | } else { |
68 | /* XXX race between stat and the following open() */ | 69 | /* XXX race between stat and the following open() */ |
69 | temporarily_use_uid(pw->pw_uid); | 70 | temporarily_use_uid(pw->pw_uid); |
70 | host_status = check_host_in_hostfile(user_hostfile, canonical_hostname, | 71 | host_status = check_host_in_hostfile(user_hostfile, canonical_hostname, |
71 | client_host_key_e, client_host_key_n, ke, kn); | 72 | client_host_key_e, client_host_key_n, |
72 | restore_uid(); | 73 | ke, kn); |
73 | } | 74 | restore_uid(); |
74 | xfree(user_hostfile); | 75 | } |
75 | } | 76 | xfree(user_hostfile); |
76 | BN_free(ke); | 77 | } |
77 | BN_free(kn); | 78 | BN_free(ke); |
78 | 79 | BN_free(kn); | |
79 | if (host_status != HOST_OK) { | 80 | |
80 | /* The host key was not found. */ | 81 | if (host_status != HOST_OK) { |
81 | debug("Rhosts with RSA host authentication denied: unknown or invalid host key"); | 82 | debug("Rhosts with RSA host authentication denied: unknown or invalid host key"); |
82 | packet_send_debug("Your host key cannot be verified: unknown or invalid host key."); | 83 | packet_send_debug("Your host key cannot be verified: unknown or invalid host key."); |
83 | return 0; | 84 | return 0; |
84 | } | 85 | } |
85 | 86 | /* A matching host key was found and is known. */ | |
86 | /* A matching host key was found and is known. */ | 87 | |
87 | 88 | /* Perform the challenge-response dialog with the client for the host key. */ | |
88 | /* Perform the challenge-response dialog with the client for the host key. */ | 89 | if (!auth_rsa_challenge_dialog(client_host_key_e, client_host_key_n)) { |
89 | if (!auth_rsa_challenge_dialog(client_host_key_e, client_host_key_n)) | 90 | log("Client on %.800s failed to respond correctly to host authentication.", |
90 | { | 91 | canonical_hostname); |
91 | log("Client on %.800s failed to respond correctly to host authentication.", | 92 | return 0; |
92 | canonical_hostname); | 93 | } |
93 | return 0; | 94 | /* We have authenticated the user using .rhosts or /etc/hosts.equiv, and the host using RSA. |
94 | } | 95 | We accept the authentication. */ |
95 | 96 | ||
96 | /* We have authenticated the user using .rhosts or /etc/hosts.equiv, and | 97 | verbose("Rhosts with RSA host authentication accepted for %.100s, %.100s on %.700s.", |
97 | the host using RSA. We accept the authentication. */ | 98 | pw->pw_name, client_user, canonical_hostname); |
98 | 99 | packet_send_debug("Rhosts with RSA host authentication accepted."); | |
99 | log("Rhosts with RSA host authentication accepted for %.100s, %.100s on %.700s.", | 100 | return 1; |
100 | pw->pw_name, client_user, canonical_hostname); | ||
101 | packet_send_debug("Rhosts with RSA host authentication accepted."); | ||
102 | return 1; | ||
103 | } | 101 | } |
diff --git a/auth-rhosts.c b/auth-rhosts.c index dc82849be..500dcebb1 100644 --- a/auth-rhosts.c +++ b/auth-rhosts.c | |||
@@ -1,22 +1,22 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * | |
3 | auth-rhosts.c | 3 | * auth-rhosts.c |
4 | 4 | * | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
6 | 6 | * | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
8 | All rights reserved | 8 | * All rights reserved |
9 | 9 | * | |
10 | Created: Fri Mar 17 05:12:18 1995 ylo | 10 | * Created: Fri Mar 17 05:12:18 1995 ylo |
11 | 11 | * | |
12 | Rhosts authentication. This file contains code to check whether to admit | 12 | * Rhosts authentication. This file contains code to check whether to admit |
13 | the login based on rhosts authentication. This file also processes | 13 | * the login based on rhosts authentication. This file also processes |
14 | /etc/hosts.equiv. | 14 | * /etc/hosts.equiv. |
15 | 15 | * | |
16 | */ | 16 | */ |
17 | 17 | ||
18 | #include "includes.h" | 18 | #include "includes.h" |
19 | RCSID("$Id: auth-rhosts.c,v 1.4 1999/11/18 21:25:48 damien Exp $"); | 19 | RCSID("$Id: auth-rhosts.c,v 1.5 1999/11/24 13:26:21 damien Exp $"); |
20 | 20 | ||
21 | #include "packet.h" | 21 | #include "packet.h" |
22 | #include "ssh.h" | 22 | #include "ssh.h" |
@@ -28,256 +28,228 @@ RCSID("$Id: auth-rhosts.c,v 1.4 1999/11/18 21:25:48 damien Exp $"); | |||
28 | /etc/hosts.equiv). This returns true if authentication can be granted | 28 | /etc/hosts.equiv). This returns true if authentication can be granted |
29 | based on the file, and returns zero otherwise. */ | 29 | based on the file, and returns zero otherwise. */ |
30 | 30 | ||
31 | int check_rhosts_file(const char *filename, const char *hostname, | 31 | int |
32 | const char *ipaddr, const char *client_user, | 32 | check_rhosts_file(const char *filename, const char *hostname, |
33 | const char *server_user) | 33 | const char *ipaddr, const char *client_user, |
34 | const char *server_user) | ||
34 | { | 35 | { |
35 | FILE *f; | 36 | FILE *f; |
36 | char buf[1024]; /* Must not be larger than host, user, dummy below. */ | 37 | char buf[1024]; /* Must not be larger than host, user, dummy below. */ |
37 | 38 | ||
38 | /* Open the .rhosts file. */ | 39 | /* Open the .rhosts file, deny if unreadable */ |
39 | f = fopen(filename, "r"); | 40 | f = fopen(filename, "r"); |
40 | if (!f) | 41 | if (!f) |
41 | return 0; /* Cannot read the .rhosts - deny access. */ | 42 | return 0; |
42 | 43 | ||
43 | /* Go through the file, checking every entry. */ | 44 | /* Go through the file, checking every entry. */ |
44 | while (fgets(buf, sizeof(buf), f)) | 45 | while (fgets(buf, sizeof(buf), f)) { |
45 | { | 46 | /* All three must be at least as big as buf to avoid overflows. */ |
46 | /* All three must be at least as big as buf to avoid overflows. */ | 47 | char hostbuf[1024], userbuf[1024], dummy[1024], *host, *user, *cp; |
47 | char hostbuf[1024], userbuf[1024], dummy[1024], *host, *user, *cp; | 48 | int negated; |
48 | int negated; | 49 | |
49 | 50 | for (cp = buf; *cp == ' ' || *cp == '\t'; cp++) | |
50 | for (cp = buf; *cp == ' ' || *cp == '\t'; cp++) | 51 | ; |
51 | ; | 52 | if (*cp == '#' || *cp == '\n' || !*cp) |
52 | if (*cp == '#' || *cp == '\n' || !*cp) | 53 | continue; |
53 | continue; | 54 | |
54 | 55 | /* NO_PLUS is supported at least on OSF/1. We skip it (we | |
55 | /* NO_PLUS is supported at least on OSF/1. We skip it (we don't ever | 56 | don't ever support the plus syntax). */ |
56 | support the plus syntax). */ | 57 | if (strncmp(cp, "NO_PLUS", 7) == 0) |
57 | if (strncmp(cp, "NO_PLUS", 7) == 0) | 58 | continue; |
58 | continue; | 59 | |
59 | 60 | /* This should be safe because each buffer is as big as | |
60 | /* This should be safe because each buffer is as big as the whole | 61 | the whole string, and thus cannot be overwritten. */ |
61 | string, and thus cannot be overwritten. */ | 62 | switch (sscanf(buf, "%s %s %s", hostbuf, userbuf, dummy)) { |
62 | switch (sscanf(buf, "%s %s %s", hostbuf, userbuf, dummy)) | 63 | case 0: |
63 | { | 64 | packet_send_debug("Found empty line in %.100s.", filename); |
64 | case 0: | 65 | continue; |
65 | packet_send_debug("Found empty line in %.100s.", filename); | 66 | case 1: |
66 | continue; /* Empty line? */ | 67 | /* Host name only. */ |
67 | case 1: | 68 | strlcpy(userbuf, server_user, sizeof(userbuf)); |
68 | /* Host name only. */ | 69 | break; |
69 | strlcpy(userbuf, server_user, sizeof(userbuf)); | 70 | case 2: |
70 | break; | 71 | /* Got both host and user name. */ |
71 | case 2: | 72 | break; |
72 | /* Got both host and user name. */ | 73 | case 3: |
73 | break; | 74 | packet_send_debug("Found garbage in %.100s.", filename); |
74 | case 3: | 75 | continue; |
75 | packet_send_debug("Found garbage in %.100s.", filename); | 76 | default: |
76 | continue; /* Extra garbage */ | 77 | /* Weird... */ |
77 | default: | 78 | continue; |
78 | continue; /* Weird... */ | 79 | } |
79 | } | 80 | |
80 | 81 | host = hostbuf; | |
81 | host = hostbuf; | 82 | user = userbuf; |
82 | user = userbuf; | 83 | negated = 0; |
83 | negated = 0; | 84 | |
84 | 85 | /* Process negated host names, or positive netgroups. */ | |
85 | /* Process negated host names, or positive netgroups. */ | 86 | if (host[0] == '-') { |
86 | if (host[0] == '-') | 87 | negated = 1; |
87 | { | 88 | host++; |
88 | negated = 1; | 89 | } else if (host[0] == '+') |
89 | host++; | 90 | host++; |
90 | } | 91 | |
91 | else | 92 | if (user[0] == '-') { |
92 | if (host[0] == '+') | 93 | negated = 1; |
93 | host++; | 94 | user++; |
94 | 95 | } else if (user[0] == '+') | |
95 | if (user[0] == '-') | 96 | user++; |
96 | { | 97 | |
97 | negated = 1; | 98 | /* Check for empty host/user names (particularly '+'). */ |
98 | user++; | 99 | if (!host[0] || !user[0]) { |
99 | } | 100 | /* We come here if either was '+' or '-'. */ |
100 | else | 101 | packet_send_debug("Ignoring wild host/user names in %.100s.", |
101 | if (user[0] == '+') | 102 | filename); |
102 | user++; | 103 | continue; |
103 | 104 | } | |
104 | /* Check for empty host/user names (particularly '+'). */ | 105 | /* Verify that host name matches. */ |
105 | if (!host[0] || !user[0]) | 106 | if (host[0] == '@') { |
106 | { | 107 | if (!innetgr(host + 1, hostname, NULL, NULL) && |
107 | /* We come here if either was '+' or '-'. */ | 108 | !innetgr(host + 1, ipaddr, NULL, NULL)) |
108 | packet_send_debug("Ignoring wild host/user names in %.100s.", | 109 | continue; |
109 | filename); | 110 | } else if (strcasecmp(host, hostname) && strcmp(host, ipaddr) != 0) |
110 | continue; | 111 | continue; /* Different hostname. */ |
111 | } | 112 | |
112 | 113 | /* Verify that user name matches. */ | |
113 | /* Verify that host name matches. */ | 114 | if (user[0] == '@') { |
114 | if (host[0] == '@') | 115 | if (!innetgr(user + 1, NULL, client_user, NULL)) |
115 | { | 116 | continue; |
116 | if (!innetgr(host + 1, hostname, NULL, NULL) && | 117 | } else if (strcmp(user, client_user) != 0) |
117 | !innetgr(host + 1, ipaddr, NULL, NULL)) | 118 | continue; /* Different username. */ |
118 | continue; | 119 | |
119 | } | 120 | /* Found the user and host. */ |
120 | else | 121 | fclose(f); |
121 | if (strcasecmp(host, hostname) && strcmp(host, ipaddr) != 0) | 122 | |
122 | continue; /* Different hostname. */ | 123 | /* If the entry was negated, deny access. */ |
123 | 124 | if (negated) { | |
124 | /* Verify that user name matches. */ | 125 | packet_send_debug("Matched negative entry in %.100s.", |
125 | if (user[0] == '@') | 126 | filename); |
126 | { | 127 | return 0; |
127 | if (!innetgr(user + 1, NULL, client_user, NULL)) | 128 | } |
128 | continue; | 129 | /* Accept authentication. */ |
130 | return 1; | ||
129 | } | 131 | } |
130 | else | ||
131 | if (strcmp(user, client_user) != 0) | ||
132 | continue; /* Different username. */ | ||
133 | 132 | ||
134 | /* Found the user and host. */ | 133 | /* Authentication using this file denied. */ |
135 | fclose(f); | 134 | fclose(f); |
136 | 135 | return 0; | |
137 | /* If the entry was negated, deny access. */ | ||
138 | if (negated) | ||
139 | { | ||
140 | packet_send_debug("Matched negative entry in %.100s.", | ||
141 | filename); | ||
142 | return 0; | ||
143 | } | ||
144 | |||
145 | /* Accept authentication. */ | ||
146 | return 1; | ||
147 | } | ||
148 | |||
149 | /* Authentication using this file denied. */ | ||
150 | fclose(f); | ||
151 | return 0; | ||
152 | } | 136 | } |
153 | 137 | ||
154 | /* Tries to authenticate the user using the .shosts or .rhosts file. | 138 | /* Tries to authenticate the user using the .shosts or .rhosts file. |
155 | Returns true if authentication succeeds. If ignore_rhosts is | 139 | Returns true if authentication succeeds. If ignore_rhosts is |
156 | true, only /etc/hosts.equiv will be considered (.rhosts and .shosts | 140 | true, only /etc/hosts.equiv will be considered (.rhosts and .shosts |
157 | are ignored). */ | 141 | are ignored). */ |
158 | 142 | ||
159 | int auth_rhosts(struct passwd *pw, const char *client_user) | 143 | int |
144 | auth_rhosts(struct passwd *pw, const char *client_user) | ||
160 | { | 145 | { |
161 | extern ServerOptions options; | 146 | extern ServerOptions options; |
162 | char buf[1024]; | 147 | char buf[1024]; |
163 | const char *hostname, *ipaddr; | 148 | const char *hostname, *ipaddr; |
164 | struct stat st; | 149 | struct stat st; |
165 | static const char *rhosts_files[] = { ".shosts", ".rhosts", NULL }; | 150 | static const char *rhosts_files[] = {".shosts", ".rhosts", NULL}; |
166 | unsigned int rhosts_file_index; | 151 | unsigned int rhosts_file_index; |
167 | 152 | ||
168 | /* Quick check: if the user has no .shosts or .rhosts files, return failure | 153 | /* Quick check: if the user has no .shosts or .rhosts files, |
169 | immediately without doing costly lookups from name servers. */ | 154 | return failure immediately without doing costly lookups from |
170 | /* Switch to the user's uid. */ | 155 | name servers. */ |
171 | temporarily_use_uid(pw->pw_uid); | 156 | /* Switch to the user's uid. */ |
172 | for (rhosts_file_index = 0; rhosts_files[rhosts_file_index]; | 157 | temporarily_use_uid(pw->pw_uid); |
173 | rhosts_file_index++) | 158 | for (rhosts_file_index = 0; rhosts_files[rhosts_file_index]; |
174 | { | 159 | rhosts_file_index++) { |
175 | /* Check users .rhosts or .shosts. */ | 160 | /* Check users .rhosts or .shosts. */ |
176 | snprintf(buf, sizeof buf, "%.500s/%.100s", | 161 | snprintf(buf, sizeof buf, "%.500s/%.100s", |
177 | pw->pw_dir, rhosts_files[rhosts_file_index]); | 162 | pw->pw_dir, rhosts_files[rhosts_file_index]); |
178 | if (stat(buf, &st) >= 0) | 163 | if (stat(buf, &st) >= 0) |
179 | break; | 164 | break; |
180 | } | ||
181 | /* Switch back to privileged uid. */ | ||
182 | restore_uid(); | ||
183 | |||
184 | if (!rhosts_files[rhosts_file_index] && stat("/etc/hosts.equiv", &st) < 0 && | ||
185 | stat(SSH_HOSTS_EQUIV, &st) < 0) | ||
186 | return 0; /* The user has no .shosts or .rhosts file and there are no | ||
187 | system-wide files. */ | ||
188 | |||
189 | /* Get the name, address, and port of the remote host. */ | ||
190 | hostname = get_canonical_hostname(); | ||
191 | ipaddr = get_remote_ipaddr(); | ||
192 | |||
193 | /* If not logging in as superuser, try /etc/hosts.equiv and shosts.equiv. */ | ||
194 | if (pw->pw_uid != 0) | ||
195 | { | ||
196 | if (check_rhosts_file("/etc/hosts.equiv", hostname, ipaddr, client_user, | ||
197 | pw->pw_name)) | ||
198 | { | ||
199 | packet_send_debug("Accepted for %.100s [%.100s] by /etc/hosts.equiv.", | ||
200 | hostname, ipaddr); | ||
201 | return 1; | ||
202 | } | 165 | } |
203 | if (check_rhosts_file(SSH_HOSTS_EQUIV, hostname, ipaddr, client_user, | 166 | /* Switch back to privileged uid. */ |
204 | pw->pw_name)) | 167 | restore_uid(); |
205 | { | 168 | |
206 | packet_send_debug("Accepted for %.100s [%.100s] by %.100s.", | 169 | /* Deny if The user has no .shosts or .rhosts file and there are no system-wide files. */ |
207 | hostname, ipaddr, SSH_HOSTS_EQUIV); | 170 | if (!rhosts_files[rhosts_file_index] && |
208 | return 1; | 171 | stat("/etc/hosts.equiv", &st) < 0 && |
172 | stat(SSH_HOSTS_EQUIV, &st) < 0) | ||
173 | return 0; | ||
174 | |||
175 | /* Get the name, address, and port of the remote host. */ | ||
176 | hostname = get_canonical_hostname(); | ||
177 | ipaddr = get_remote_ipaddr(); | ||
178 | |||
179 | /* If not logging in as superuser, try /etc/hosts.equiv and shosts.equiv. */ | ||
180 | if (pw->pw_uid != 0) { | ||
181 | if (check_rhosts_file("/etc/hosts.equiv", hostname, ipaddr, client_user, | ||
182 | pw->pw_name)) { | ||
183 | packet_send_debug("Accepted for %.100s [%.100s] by /etc/hosts.equiv.", | ||
184 | hostname, ipaddr); | ||
185 | return 1; | ||
186 | } | ||
187 | if (check_rhosts_file(SSH_HOSTS_EQUIV, hostname, ipaddr, client_user, | ||
188 | pw->pw_name)) { | ||
189 | packet_send_debug("Accepted for %.100s [%.100s] by %.100s.", | ||
190 | hostname, ipaddr, SSH_HOSTS_EQUIV); | ||
191 | return 1; | ||
192 | } | ||
209 | } | 193 | } |
210 | } | 194 | /* Check that the home directory is owned by root or the user, and |
211 | 195 | is not group or world writable. */ | |
212 | /* Check that the home directory is owned by root or the user, and is not | 196 | if (stat(pw->pw_dir, &st) < 0) { |
213 | group or world writable. */ | 197 | log("Rhosts authentication refused for %.100s: no home directory %.200s", |
214 | if (stat(pw->pw_dir, &st) < 0) | 198 | pw->pw_name, pw->pw_dir); |
215 | { | 199 | packet_send_debug("Rhosts authentication refused for %.100: no home directory %.200s", |
216 | log("Rhosts authentication refused for %.100s: no home directory %.200s", | 200 | pw->pw_name, pw->pw_dir); |
217 | pw->pw_name, pw->pw_dir); | 201 | return 0; |
218 | packet_send_debug("Rhosts authentication refused for %.100: no home directory %.200s", | ||
219 | pw->pw_name, pw->pw_dir); | ||
220 | return 0; | ||
221 | } | ||
222 | if (options.strict_modes && | ||
223 | ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || | ||
224 | (st.st_mode & 022) != 0)) | ||
225 | { | ||
226 | log("Rhosts authentication refused for %.100s: bad ownership or modes for home directory.", | ||
227 | pw->pw_name); | ||
228 | packet_send_debug("Rhosts authentication refused for %.100s: bad ownership or modes for home directory.", | ||
229 | pw->pw_name); | ||
230 | return 0; | ||
231 | } | ||
232 | |||
233 | /* Check all .rhosts files (currently .shosts and .rhosts). */ | ||
234 | /* Temporarily use the user's uid. */ | ||
235 | temporarily_use_uid(pw->pw_uid); | ||
236 | for (rhosts_file_index = 0; rhosts_files[rhosts_file_index]; | ||
237 | rhosts_file_index++) | ||
238 | { | ||
239 | /* Check users .rhosts or .shosts. */ | ||
240 | snprintf(buf, sizeof buf, "%.500s/%.100s", | ||
241 | pw->pw_dir, rhosts_files[rhosts_file_index]); | ||
242 | if (stat(buf, &st) < 0) | ||
243 | continue; /* No such file. */ | ||
244 | |||
245 | /* Make sure that the file is either owned by the user or by root, | ||
246 | and make sure it is not writable by anyone but the owner. This is | ||
247 | to help avoid novices accidentally allowing access to their account | ||
248 | by anyone. */ | ||
249 | if (options.strict_modes && | ||
250 | ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || | ||
251 | (st.st_mode & 022) != 0)) | ||
252 | { | ||
253 | log("Rhosts authentication refused for %.100s: bad modes for %.200s", | ||
254 | pw->pw_name, buf); | ||
255 | packet_send_debug("Bad file modes for %.200s", buf); | ||
256 | continue; | ||
257 | } | 202 | } |
258 | 203 | if (options.strict_modes && | |
259 | /* Check if we have been configured to ignore .rhosts and .shosts | 204 | ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || |
260 | files. */ | 205 | (st.st_mode & 022) != 0)) { |
261 | if (options.ignore_rhosts) | 206 | log("Rhosts authentication refused for %.100s: bad ownership or modes for home directory.", |
262 | { | 207 | pw->pw_name); |
263 | packet_send_debug("Server has been configured to ignore %.100s.", | 208 | packet_send_debug("Rhosts authentication refused for %.100s: bad ownership or modes for home directory.", |
264 | rhosts_files[rhosts_file_index]); | 209 | pw->pw_name); |
265 | continue; | 210 | return 0; |
266 | } | 211 | } |
267 | 212 | /* Temporarily use the user's uid. */ | |
268 | /* Check if authentication is permitted by the file. */ | 213 | temporarily_use_uid(pw->pw_uid); |
269 | if (check_rhosts_file(buf, hostname, ipaddr, client_user, pw->pw_name)) | 214 | |
270 | { | 215 | /* Check all .rhosts files (currently .shosts and .rhosts). */ |
271 | packet_send_debug("Accepted by %.100s.", | 216 | for (rhosts_file_index = 0; rhosts_files[rhosts_file_index]; |
272 | rhosts_files[rhosts_file_index]); | 217 | rhosts_file_index++) { |
273 | /* Restore the privileged uid. */ | 218 | /* Check users .rhosts or .shosts. */ |
274 | restore_uid(); | 219 | snprintf(buf, sizeof buf, "%.500s/%.100s", |
275 | return 1; | 220 | pw->pw_dir, rhosts_files[rhosts_file_index]); |
221 | if (stat(buf, &st) < 0) | ||
222 | continue; | ||
223 | |||
224 | /* Make sure that the file is either owned by the user or | ||
225 | by root, and make sure it is not writable by anyone but | ||
226 | the owner. This is to help avoid novices accidentally | ||
227 | allowing access to their account by anyone. */ | ||
228 | if (options.strict_modes && | ||
229 | ((st.st_uid != 0 && st.st_uid != pw->pw_uid) || | ||
230 | (st.st_mode & 022) != 0)) { | ||
231 | log("Rhosts authentication refused for %.100s: bad modes for %.200s", | ||
232 | pw->pw_name, buf); | ||
233 | packet_send_debug("Bad file modes for %.200s", buf); | ||
234 | continue; | ||
235 | } | ||
236 | /* Check if we have been configured to ignore .rhosts and .shosts files. */ | ||
237 | if (options.ignore_rhosts) { | ||
238 | packet_send_debug("Server has been configured to ignore %.100s.", | ||
239 | rhosts_files[rhosts_file_index]); | ||
240 | continue; | ||
241 | } | ||
242 | /* Check if authentication is permitted by the file. */ | ||
243 | if (check_rhosts_file(buf, hostname, ipaddr, client_user, pw->pw_name)) { | ||
244 | packet_send_debug("Accepted by %.100s.", | ||
245 | rhosts_files[rhosts_file_index]); | ||
246 | /* Restore the privileged uid. */ | ||
247 | restore_uid(); | ||
248 | return 1; | ||
249 | } | ||
276 | } | 250 | } |
277 | } | ||
278 | 251 | ||
279 | /* Rhosts authentication denied. */ | 252 | /* Restore the privileged uid. */ |
280 | /* Restore the privileged uid. */ | 253 | restore_uid(); |
281 | restore_uid(); | 254 | return 0; |
282 | return 0; | ||
283 | } | 255 | } |
diff --git a/auth-rsa.c b/auth-rsa.c index f2295078b..88841482b 100644 --- a/auth-rsa.c +++ b/auth-rsa.c | |||
@@ -1,22 +1,22 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * | |
3 | auth-rsa.c | 3 | * auth-rsa.c |
4 | 4 | * | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
6 | 6 | * | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
8 | All rights reserved | 8 | * All rights reserved |
9 | 9 | * | |
10 | Created: Mon Mar 27 01:46:52 1995 ylo | 10 | * Created: Mon Mar 27 01:46:52 1995 ylo |
11 | 11 | * | |
12 | RSA-based authentication. This code determines whether to admit a login | 12 | * RSA-based authentication. This code determines whether to admit a login |
13 | based on RSA authentication. This file also contains functions to check | 13 | * based on RSA authentication. This file also contains functions to check |
14 | validity of the host key. | 14 | * validity of the host key. |
15 | 15 | * | |
16 | */ | 16 | */ |
17 | 17 | ||
18 | #include "includes.h" | 18 | #include "includes.h" |
19 | RCSID("$Id: auth-rsa.c,v 1.8 1999/11/18 21:25:48 damien Exp $"); | 19 | RCSID("$Id: auth-rsa.c,v 1.9 1999/11/24 13:26:21 damien Exp $"); |
20 | 20 | ||
21 | #include "rsa.h" | 21 | #include "rsa.h" |
22 | #include "packet.h" | 22 | #include "packet.h" |
@@ -50,7 +50,7 @@ extern unsigned char session_id[16]; | |||
50 | /* The .ssh/authorized_keys file contains public keys, one per line, in the | 50 | /* The .ssh/authorized_keys file contains public keys, one per line, in the |
51 | following format: | 51 | following format: |
52 | options bits e n comment | 52 | options bits e n comment |
53 | where bits, e and n are decimal numbers, | 53 | where bits, e and n are decimal numbers, |
54 | and comment is any string of characters up to newline. The maximum | 54 | and comment is any string of characters up to newline. The maximum |
55 | length of a line is 8000 characters. See the documentation for a | 55 | length of a line is 8000 characters. See the documentation for a |
56 | description of the options. | 56 | description of the options. |
@@ -63,71 +63,69 @@ extern unsigned char session_id[16]; | |||
63 | int | 63 | int |
64 | auth_rsa_challenge_dialog(BIGNUM *e, BIGNUM *n) | 64 | auth_rsa_challenge_dialog(BIGNUM *e, BIGNUM *n) |
65 | { | 65 | { |
66 | BIGNUM *challenge, *encrypted_challenge, *aux; | 66 | BIGNUM *challenge, *encrypted_challenge, *aux; |
67 | RSA *pk; | 67 | RSA *pk; |
68 | BN_CTX *ctx = BN_CTX_new(); | 68 | BN_CTX *ctx = BN_CTX_new(); |
69 | unsigned char buf[32], mdbuf[16], response[16]; | 69 | unsigned char buf[32], mdbuf[16], response[16]; |
70 | MD5_CTX md; | 70 | MD5_CTX md; |
71 | unsigned int i; | 71 | unsigned int i; |
72 | int plen, len; | 72 | int plen, len; |
73 | 73 | ||
74 | encrypted_challenge = BN_new(); | 74 | encrypted_challenge = BN_new(); |
75 | challenge = BN_new(); | 75 | challenge = BN_new(); |
76 | aux = BN_new(); | 76 | aux = BN_new(); |
77 | 77 | ||
78 | /* Generate a random challenge. */ | 78 | /* Generate a random challenge. */ |
79 | BN_rand(challenge, 256, 0, 0); | 79 | BN_rand(challenge, 256, 0, 0); |
80 | BN_mod(challenge, challenge, n, ctx); | 80 | BN_mod(challenge, challenge, n, ctx); |
81 | 81 | ||
82 | /* Create the public key data structure. */ | 82 | /* Create the public key data structure. */ |
83 | pk = RSA_new(); | 83 | pk = RSA_new(); |
84 | pk->e = BN_new(); | 84 | pk->e = BN_new(); |
85 | BN_copy(pk->e, e); | 85 | BN_copy(pk->e, e); |
86 | pk->n = BN_new(); | 86 | pk->n = BN_new(); |
87 | BN_copy(pk->n, n); | 87 | BN_copy(pk->n, n); |
88 | 88 | ||
89 | /* Encrypt the challenge with the public key. */ | 89 | /* Encrypt the challenge with the public key. */ |
90 | rsa_public_encrypt(encrypted_challenge, challenge, pk); | 90 | rsa_public_encrypt(encrypted_challenge, challenge, pk); |
91 | RSA_free(pk); | 91 | RSA_free(pk); |
92 | 92 | ||
93 | /* Send the encrypted challenge to the client. */ | 93 | /* Send the encrypted challenge to the client. */ |
94 | packet_start(SSH_SMSG_AUTH_RSA_CHALLENGE); | 94 | packet_start(SSH_SMSG_AUTH_RSA_CHALLENGE); |
95 | packet_put_bignum(encrypted_challenge); | 95 | packet_put_bignum(encrypted_challenge); |
96 | packet_send(); | 96 | packet_send(); |
97 | packet_write_wait(); | 97 | packet_write_wait(); |
98 | 98 | ||
99 | /* The response is MD5 of decrypted challenge plus session id. */ | 99 | /* The response is MD5 of decrypted challenge plus session id. */ |
100 | len = BN_num_bytes(challenge); | 100 | len = BN_num_bytes(challenge); |
101 | if (len <= 0 || len > 32) | 101 | if (len <= 0 || len > 32) |
102 | fatal("auth_rsa_challenge_dialog: bad challenge length %d", len); | 102 | fatal("auth_rsa_challenge_dialog: bad challenge length %d", len); |
103 | memset(buf, 0, 32); | 103 | memset(buf, 0, 32); |
104 | BN_bn2bin(challenge, buf + 32 - len); | 104 | BN_bn2bin(challenge, buf + 32 - len); |
105 | MD5_Init(&md); | 105 | MD5_Init(&md); |
106 | MD5_Update(&md, buf, 32); | 106 | MD5_Update(&md, buf, 32); |
107 | MD5_Update(&md, session_id, 16); | 107 | MD5_Update(&md, session_id, 16); |
108 | MD5_Final(mdbuf, &md); | 108 | MD5_Final(mdbuf, &md); |
109 | 109 | ||
110 | /* We will no longer need these. */ | 110 | /* We will no longer need these. */ |
111 | BN_clear_free(encrypted_challenge); | 111 | BN_clear_free(encrypted_challenge); |
112 | BN_clear_free(challenge); | 112 | BN_clear_free(challenge); |
113 | BN_clear_free(aux); | 113 | BN_clear_free(aux); |
114 | BN_CTX_free(ctx); | 114 | BN_CTX_free(ctx); |
115 | 115 | ||
116 | /* Wait for a response. */ | 116 | /* Wait for a response. */ |
117 | packet_read_expect(&plen, SSH_CMSG_AUTH_RSA_RESPONSE); | 117 | packet_read_expect(&plen, SSH_CMSG_AUTH_RSA_RESPONSE); |
118 | packet_integrity_check(plen, 16, SSH_CMSG_AUTH_RSA_RESPONSE); | 118 | packet_integrity_check(plen, 16, SSH_CMSG_AUTH_RSA_RESPONSE); |
119 | for (i = 0; i < 16; i++) | 119 | for (i = 0; i < 16; i++) |
120 | response[i] = packet_get_char(); | 120 | response[i] = packet_get_char(); |
121 | 121 | ||
122 | /* Verify that the response is the original challenge. */ | 122 | /* Verify that the response is the original challenge. */ |
123 | if (memcmp(response, mdbuf, 16) != 0) | 123 | if (memcmp(response, mdbuf, 16) != 0) { |
124 | { | 124 | /* Wrong answer. */ |
125 | /* Wrong answer. */ | 125 | return 0; |
126 | return 0; | 126 | } |
127 | } | 127 | /* Correct answer. */ |
128 | 128 | return 1; | |
129 | /* Correct answer. */ | ||
130 | return 1; | ||
131 | } | 129 | } |
132 | 130 | ||
133 | /* Performs the RSA authentication dialog with the client. This returns | 131 | /* Performs the RSA authentication dialog with the client. This returns |
@@ -137,357 +135,324 @@ auth_rsa_challenge_dialog(BIGNUM *e, BIGNUM *n) | |||
137 | int | 135 | int |
138 | auth_rsa(struct passwd *pw, BIGNUM *client_n) | 136 | auth_rsa(struct passwd *pw, BIGNUM *client_n) |
139 | { | 137 | { |
140 | extern ServerOptions options; | 138 | extern ServerOptions options; |
141 | char line[8192], file[1024]; | 139 | char line[8192], file[1024]; |
142 | int authenticated; | 140 | int authenticated; |
143 | unsigned int bits; | 141 | unsigned int bits; |
144 | FILE *f; | 142 | FILE *f; |
145 | unsigned long linenum = 0; | 143 | unsigned long linenum = 0; |
146 | struct stat st; | 144 | struct stat st; |
147 | BIGNUM *e, *n; | 145 | BIGNUM *e, *n; |
148 | 146 | ||
149 | /* Temporarily use the user's uid. */ | 147 | /* Temporarily use the user's uid. */ |
150 | temporarily_use_uid(pw->pw_uid); | 148 | temporarily_use_uid(pw->pw_uid); |
151 | 149 | ||
152 | /* The authorized keys. */ | 150 | /* The authorized keys. */ |
153 | snprintf(file, sizeof file, "%.500s/%.100s", pw->pw_dir, | 151 | snprintf(file, sizeof file, "%.500s/%.100s", pw->pw_dir, |
154 | SSH_USER_PERMITTED_KEYS); | 152 | SSH_USER_PERMITTED_KEYS); |
155 | 153 | ||
156 | /* Fail quietly if file does not exist */ | 154 | /* Fail quietly if file does not exist */ |
157 | if (stat(file, &st) < 0) | 155 | if (stat(file, &st) < 0) { |
158 | { | 156 | /* Restore the privileged uid. */ |
159 | /* Restore the privileged uid. */ | 157 | restore_uid(); |
160 | restore_uid(); | 158 | return 0; |
161 | return 0; | ||
162 | } | ||
163 | |||
164 | /* Open the file containing the authorized keys. */ | ||
165 | f = fopen(file, "r"); | ||
166 | if (!f) | ||
167 | { | ||
168 | /* Restore the privileged uid. */ | ||
169 | restore_uid(); | ||
170 | packet_send_debug("Could not open %.900s for reading.", file); | ||
171 | packet_send_debug("If your home is on an NFS volume, it may need to be world-readable."); | ||
172 | return 0; | ||
173 | } | ||
174 | |||
175 | if (options.strict_modes) { | ||
176 | int fail=0; | ||
177 | char buf[1024]; | ||
178 | /* Check open file in order to avoid open/stat races */ | ||
179 | if (fstat(fileno(f), &st) < 0 || | ||
180 | (st.st_uid != 0 && st.st_uid != pw->pw_uid) || | ||
181 | (st.st_mode & 022) != 0) { | ||
182 | snprintf(buf, sizeof buf, "RSA authentication refused for %.100s: " | ||
183 | "bad ownership or modes for '%s'.", pw->pw_name, file); | ||
184 | fail=1; | ||
185 | }else{ | ||
186 | /* Check path to SSH_USER_PERMITTED_KEYS */ | ||
187 | int i; | ||
188 | static const char *check[] = { | ||
189 | "", SSH_USER_DIR, NULL | ||
190 | }; | ||
191 | for (i=0; check[i]; i++) { | ||
192 | snprintf(line, sizeof line, "%.500s/%.100s", pw->pw_dir, check[i]); | ||
193 | if (stat(line, &st) < 0 || | ||
194 | (st.st_uid != 0 && st.st_uid != pw->pw_uid) || | ||
195 | (st.st_mode & 022) != 0) { | ||
196 | snprintf(buf, sizeof buf, "RSA authentication refused for %.100s: " | ||
197 | "bad ownership or modes for '%s'.", pw->pw_name, line); | ||
198 | fail=1; | ||
199 | break; | ||
200 | } | ||
201 | } | ||
202 | } | ||
203 | if (fail) { | ||
204 | log(buf); | ||
205 | packet_send_debug(buf); | ||
206 | restore_uid(); | ||
207 | return 0; | ||
208 | } | ||
209 | } | ||
210 | |||
211 | /* Flag indicating whether authentication has succeeded. */ | ||
212 | authenticated = 0; | ||
213 | |||
214 | /* Initialize mp-int variables. */ | ||
215 | e = BN_new(); | ||
216 | n = BN_new(); | ||
217 | |||
218 | /* Go though the accepted keys, looking for the current key. If found, | ||
219 | perform a challenge-response dialog to verify that the user really has | ||
220 | the corresponding private key. */ | ||
221 | while (fgets(line, sizeof(line), f)) | ||
222 | { | ||
223 | char *cp; | ||
224 | char *options; | ||
225 | |||
226 | linenum++; | ||
227 | |||
228 | /* Skip leading whitespace. */ | ||
229 | for (cp = line; *cp == ' ' || *cp == '\t'; cp++) | ||
230 | ; | ||
231 | |||
232 | /* Skip empty and comment lines. */ | ||
233 | if (!*cp || *cp == '\n' || *cp == '#') | ||
234 | continue; | ||
235 | |||
236 | /* Check if there are options for this key, and if so, save their | ||
237 | starting address and skip the option part for now. If there are no | ||
238 | options, set the starting address to NULL. */ | ||
239 | if (*cp < '0' || *cp > '9') | ||
240 | { | ||
241 | int quoted = 0; | ||
242 | options = cp; | ||
243 | for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) | ||
244 | { | ||
245 | if (*cp == '\\' && cp[1] == '"') | ||
246 | cp++; /* Skip both */ | ||
247 | else | ||
248 | if (*cp == '"') | ||
249 | quoted = !quoted; | ||
250 | } | ||
251 | } | 159 | } |
252 | else | 160 | /* Open the file containing the authorized keys. */ |
253 | options = NULL; | 161 | f = fopen(file, "r"); |
254 | 162 | if (!f) { | |
255 | /* Parse the key from the line. */ | 163 | /* Restore the privileged uid. */ |
256 | if (!auth_rsa_read_key(&cp, &bits, e, n)) | 164 | restore_uid(); |
257 | { | 165 | packet_send_debug("Could not open %.900s for reading.", file); |
258 | debug("%.100s, line %lu: bad key syntax", | 166 | packet_send_debug("If your home is on an NFS volume, it may need to be world-readable."); |
259 | SSH_USER_PERMITTED_KEYS, linenum); | 167 | return 0; |
260 | packet_send_debug("%.100s, line %lu: bad key syntax", | ||
261 | SSH_USER_PERMITTED_KEYS, linenum); | ||
262 | continue; | ||
263 | } | 168 | } |
264 | /* cp now points to the comment part. */ | 169 | if (options.strict_modes) { |
265 | 170 | int fail = 0; | |
266 | /* check the real bits */ | 171 | char buf[1024]; |
267 | if (bits != BN_num_bits(n)) | 172 | /* Check open file in order to avoid open/stat races */ |
268 | error("Warning: error in %s, line %ld: keysize mismatch: " | 173 | if (fstat(fileno(f), &st) < 0 || |
269 | "actual size %d vs. announced %d.", | 174 | (st.st_uid != 0 && st.st_uid != pw->pw_uid) || |
270 | file, linenum, BN_num_bits(n), bits); | 175 | (st.st_mode & 022) != 0) { |
271 | 176 | snprintf(buf, sizeof buf, "RSA authentication refused for %.100s: " | |
272 | /* Check if the we have found the desired key (identified by its | 177 | "bad ownership or modes for '%s'.", pw->pw_name, file); |
273 | modulus). */ | 178 | fail = 1; |
274 | if (BN_cmp(n, client_n) != 0) | 179 | } else { |
275 | continue; /* Wrong key. */ | 180 | /* Check path to SSH_USER_PERMITTED_KEYS */ |
276 | 181 | int i; | |
277 | /* We have found the desired key. */ | 182 | static const char *check[] = { |
278 | 183 | "", SSH_USER_DIR, NULL | |
279 | /* Perform the challenge-response dialog for this key. */ | 184 | }; |
280 | if (!auth_rsa_challenge_dialog(e, n)) | 185 | for (i = 0; check[i]; i++) { |
281 | { | 186 | snprintf(line, sizeof line, "%.500s/%.100s", pw->pw_dir, check[i]); |
282 | /* Wrong response. */ | 187 | if (stat(line, &st) < 0 || |
283 | log("Wrong response to RSA authentication challenge."); | 188 | (st.st_uid != 0 && st.st_uid != pw->pw_uid) || |
284 | packet_send_debug("Wrong response to RSA authentication challenge."); | 189 | (st.st_mode & 022) != 0) { |
285 | continue; | 190 | snprintf(buf, sizeof buf, "RSA authentication refused for %.100s: " |
286 | } | 191 | "bad ownership or modes for '%s'.", pw->pw_name, line); |
287 | 192 | fail = 1; | |
288 | /* Correct response. The client has been successfully authenticated. | 193 | break; |
289 | Note that we have not yet processed the options; this will be reset | 194 | } |
290 | if the options cause the authentication to be rejected. */ | 195 | } |
291 | authenticated = 1; | ||
292 | |||
293 | /* RSA part of authentication was accepted. Now process the options. */ | ||
294 | if (options) | ||
295 | { | ||
296 | while (*options && *options != ' ' && *options != '\t') | ||
297 | { | ||
298 | cp = "no-port-forwarding"; | ||
299 | if (strncmp(options, cp, strlen(cp)) == 0) | ||
300 | { | ||
301 | packet_send_debug("Port forwarding disabled."); | ||
302 | no_port_forwarding_flag = 1; | ||
303 | options += strlen(cp); | ||
304 | goto next_option; | ||
305 | } | ||
306 | cp = "no-agent-forwarding"; | ||
307 | if (strncmp(options, cp, strlen(cp)) == 0) | ||
308 | { | ||
309 | packet_send_debug("Agent forwarding disabled."); | ||
310 | no_agent_forwarding_flag = 1; | ||
311 | options += strlen(cp); | ||
312 | goto next_option; | ||
313 | } | ||
314 | cp = "no-X11-forwarding"; | ||
315 | if (strncmp(options, cp, strlen(cp)) == 0) | ||
316 | { | ||
317 | packet_send_debug("X11 forwarding disabled."); | ||
318 | no_x11_forwarding_flag = 1; | ||
319 | options += strlen(cp); | ||
320 | goto next_option; | ||
321 | } | 196 | } |
322 | cp = "no-pty"; | 197 | if (fail) { |
323 | if (strncmp(options, cp, strlen(cp)) == 0) | 198 | log(buf); |
324 | { | 199 | packet_send_debug(buf); |
325 | packet_send_debug("Pty allocation disabled."); | 200 | restore_uid(); |
326 | no_pty_flag = 1; | 201 | return 0; |
327 | options += strlen(cp); | ||
328 | goto next_option; | ||
329 | } | 202 | } |
330 | cp = "command=\""; | 203 | } |
331 | if (strncmp(options, cp, strlen(cp)) == 0) | 204 | /* Flag indicating whether authentication has succeeded. */ |
332 | { | 205 | authenticated = 0; |
333 | int i; | 206 | |
334 | options += strlen(cp); | 207 | /* Initialize mp-int variables. */ |
335 | forced_command = xmalloc(strlen(options) + 1); | 208 | e = BN_new(); |
336 | i = 0; | 209 | n = BN_new(); |
337 | while (*options) | 210 | |
338 | { | 211 | /* Go though the accepted keys, looking for the current key. If |
339 | if (*options == '"') | 212 | found, perform a challenge-response dialog to verify that the |
340 | break; | 213 | user really has the corresponding private key. */ |
341 | if (*options == '\\' && options[1] == '"') | 214 | while (fgets(line, sizeof(line), f)) { |
342 | { | 215 | char *cp; |
343 | options += 2; | 216 | char *options; |
344 | forced_command[i++] = '"'; | 217 | |
345 | continue; | 218 | linenum++; |
219 | |||
220 | /* Skip leading whitespace. */ | ||
221 | for (cp = line; *cp == ' ' || *cp == '\t'; cp++); | ||
222 | |||
223 | /* Skip empty and comment lines. */ | ||
224 | if (!*cp || *cp == '\n' || *cp == '#') | ||
225 | continue; | ||
226 | |||
227 | /* Check if there are options for this key, and if so, | ||
228 | save their starting address and skip the option part | ||
229 | for now. If there are no options, set the starting | ||
230 | address to NULL. */ | ||
231 | if (*cp < '0' || *cp > '9') { | ||
232 | int quoted = 0; | ||
233 | options = cp; | ||
234 | for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) { | ||
235 | if (*cp == '\\' && cp[1] == '"') | ||
236 | cp++; /* Skip both */ | ||
237 | else if (*cp == '"') | ||
238 | quoted = !quoted; | ||
346 | } | 239 | } |
347 | forced_command[i++] = *options++; | 240 | } else |
348 | } | 241 | options = NULL; |
349 | if (!*options) | 242 | |
350 | { | 243 | /* Parse the key from the line. */ |
351 | debug("%.100s, line %lu: missing end quote", | 244 | if (!auth_rsa_read_key(&cp, &bits, e, n)) { |
352 | SSH_USER_PERMITTED_KEYS, linenum); | 245 | debug("%.100s, line %lu: bad key syntax", |
353 | packet_send_debug("%.100s, line %lu: missing end quote", | 246 | SSH_USER_PERMITTED_KEYS, linenum); |
354 | SSH_USER_PERMITTED_KEYS, linenum); | 247 | packet_send_debug("%.100s, line %lu: bad key syntax", |
355 | continue; | 248 | SSH_USER_PERMITTED_KEYS, linenum); |
356 | } | 249 | continue; |
357 | forced_command[i] = 0; | ||
358 | packet_send_debug("Forced command: %.900s", forced_command); | ||
359 | options++; | ||
360 | goto next_option; | ||
361 | } | 250 | } |
362 | cp = "environment=\""; | 251 | /* cp now points to the comment part. */ |
363 | if (strncmp(options, cp, strlen(cp)) == 0) | 252 | |
364 | { | 253 | /* check the real bits */ |
365 | int i; | 254 | if (bits != BN_num_bits(n)) |
366 | char *s; | 255 | error("Warning: error in %s, line %ld: keysize mismatch: " |
367 | struct envstring *new_envstring; | 256 | "actual size %d vs. announced %d.", |
368 | options += strlen(cp); | 257 | file, linenum, BN_num_bits(n), bits); |
369 | s = xmalloc(strlen(options) + 1); | 258 | |
370 | i = 0; | 259 | /* Check if the we have found the desired key (identified by its modulus). */ |
371 | while (*options) | 260 | if (BN_cmp(n, client_n) != 0) |
372 | { | 261 | continue; /* Wrong key. */ |
373 | if (*options == '"') | 262 | |
374 | break; | 263 | /* We have found the desired key. */ |
375 | if (*options == '\\' && options[1] == '"') | 264 | |
376 | { | 265 | /* Perform the challenge-response dialog for this key. */ |
377 | options += 2; | 266 | if (!auth_rsa_challenge_dialog(e, n)) { |
378 | s[i++] = '"'; | 267 | /* Wrong response. */ |
379 | continue; | 268 | verbose("Wrong response to RSA authentication challenge."); |
380 | } | 269 | packet_send_debug("Wrong response to RSA authentication challenge."); |
381 | s[i++] = *options++; | 270 | continue; |
382 | } | ||
383 | if (!*options) | ||
384 | { | ||
385 | debug("%.100s, line %lu: missing end quote", | ||
386 | SSH_USER_PERMITTED_KEYS, linenum); | ||
387 | packet_send_debug("%.100s, line %lu: missing end quote", | ||
388 | SSH_USER_PERMITTED_KEYS, linenum); | ||
389 | continue; | ||
390 | } | ||
391 | s[i] = 0; | ||
392 | packet_send_debug("Adding to environment: %.900s", s); | ||
393 | debug("Adding to environment: %.900s", s); | ||
394 | options++; | ||
395 | new_envstring = xmalloc(sizeof(struct envstring)); | ||
396 | new_envstring->s = s; | ||
397 | new_envstring->next = custom_environment; | ||
398 | custom_environment = new_envstring; | ||
399 | goto next_option; | ||
400 | } | 271 | } |
401 | cp = "from=\""; | 272 | /* Correct response. The client has been successfully |
402 | if (strncmp(options, cp, strlen(cp)) == 0) | 273 | authenticated. Note that we have not yet processed the |
403 | { | 274 | options; this will be reset if the options cause the |
404 | char *patterns = xmalloc(strlen(options) + 1); | 275 | authentication to be rejected. */ |
405 | int i; | 276 | authenticated = 1; |
406 | options += strlen(cp); | 277 | |
407 | i = 0; | 278 | /* RSA part of authentication was accepted. Now process the options. */ |
408 | while (*options) | 279 | if (options) { |
409 | { | 280 | while (*options && *options != ' ' && *options != '\t') { |
410 | if (*options == '"') | 281 | cp = "no-port-forwarding"; |
411 | break; | 282 | if (strncmp(options, cp, strlen(cp)) == 0) { |
412 | if (*options == '\\' && options[1] == '"') | 283 | packet_send_debug("Port forwarding disabled."); |
413 | { | 284 | no_port_forwarding_flag = 1; |
414 | options += 2; | 285 | options += strlen(cp); |
415 | patterns[i++] = '"'; | 286 | goto next_option; |
416 | continue; | 287 | } |
288 | cp = "no-agent-forwarding"; | ||
289 | if (strncmp(options, cp, strlen(cp)) == 0) { | ||
290 | packet_send_debug("Agent forwarding disabled."); | ||
291 | no_agent_forwarding_flag = 1; | ||
292 | options += strlen(cp); | ||
293 | goto next_option; | ||
294 | } | ||
295 | cp = "no-X11-forwarding"; | ||
296 | if (strncmp(options, cp, strlen(cp)) == 0) { | ||
297 | packet_send_debug("X11 forwarding disabled."); | ||
298 | no_x11_forwarding_flag = 1; | ||
299 | options += strlen(cp); | ||
300 | goto next_option; | ||
301 | } | ||
302 | cp = "no-pty"; | ||
303 | if (strncmp(options, cp, strlen(cp)) == 0) { | ||
304 | packet_send_debug("Pty allocation disabled."); | ||
305 | no_pty_flag = 1; | ||
306 | options += strlen(cp); | ||
307 | goto next_option; | ||
308 | } | ||
309 | cp = "command=\""; | ||
310 | if (strncmp(options, cp, strlen(cp)) == 0) { | ||
311 | int i; | ||
312 | options += strlen(cp); | ||
313 | forced_command = xmalloc(strlen(options) + 1); | ||
314 | i = 0; | ||
315 | while (*options) { | ||
316 | if (*options == '"') | ||
317 | break; | ||
318 | if (*options == '\\' && options[1] == '"') { | ||
319 | options += 2; | ||
320 | forced_command[i++] = '"'; | ||
321 | continue; | ||
322 | } | ||
323 | forced_command[i++] = *options++; | ||
324 | } | ||
325 | if (!*options) { | ||
326 | debug("%.100s, line %lu: missing end quote", | ||
327 | SSH_USER_PERMITTED_KEYS, linenum); | ||
328 | packet_send_debug("%.100s, line %lu: missing end quote", | ||
329 | SSH_USER_PERMITTED_KEYS, linenum); | ||
330 | continue; | ||
331 | } | ||
332 | forced_command[i] = 0; | ||
333 | packet_send_debug("Forced command: %.900s", forced_command); | ||
334 | options++; | ||
335 | goto next_option; | ||
336 | } | ||
337 | cp = "environment=\""; | ||
338 | if (strncmp(options, cp, strlen(cp)) == 0) { | ||
339 | int i; | ||
340 | char *s; | ||
341 | struct envstring *new_envstring; | ||
342 | options += strlen(cp); | ||
343 | s = xmalloc(strlen(options) + 1); | ||
344 | i = 0; | ||
345 | while (*options) { | ||
346 | if (*options == '"') | ||
347 | break; | ||
348 | if (*options == '\\' && options[1] == '"') { | ||
349 | options += 2; | ||
350 | s[i++] = '"'; | ||
351 | continue; | ||
352 | } | ||
353 | s[i++] = *options++; | ||
354 | } | ||
355 | if (!*options) { | ||
356 | debug("%.100s, line %lu: missing end quote", | ||
357 | SSH_USER_PERMITTED_KEYS, linenum); | ||
358 | packet_send_debug("%.100s, line %lu: missing end quote", | ||
359 | SSH_USER_PERMITTED_KEYS, linenum); | ||
360 | continue; | ||
361 | } | ||
362 | s[i] = 0; | ||
363 | packet_send_debug("Adding to environment: %.900s", s); | ||
364 | debug("Adding to environment: %.900s", s); | ||
365 | options++; | ||
366 | new_envstring = xmalloc(sizeof(struct envstring)); | ||
367 | new_envstring->s = s; | ||
368 | new_envstring->next = custom_environment; | ||
369 | custom_environment = new_envstring; | ||
370 | goto next_option; | ||
371 | } | ||
372 | cp = "from=\""; | ||
373 | if (strncmp(options, cp, strlen(cp)) == 0) { | ||
374 | char *patterns = xmalloc(strlen(options) + 1); | ||
375 | int i; | ||
376 | options += strlen(cp); | ||
377 | i = 0; | ||
378 | while (*options) { | ||
379 | if (*options == '"') | ||
380 | break; | ||
381 | if (*options == '\\' && options[1] == '"') { | ||
382 | options += 2; | ||
383 | patterns[i++] = '"'; | ||
384 | continue; | ||
385 | } | ||
386 | patterns[i++] = *options++; | ||
387 | } | ||
388 | if (!*options) { | ||
389 | debug("%.100s, line %lu: missing end quote", | ||
390 | SSH_USER_PERMITTED_KEYS, linenum); | ||
391 | packet_send_debug("%.100s, line %lu: missing end quote", | ||
392 | SSH_USER_PERMITTED_KEYS, linenum); | ||
393 | continue; | ||
394 | } | ||
395 | patterns[i] = 0; | ||
396 | options++; | ||
397 | if (!match_hostname(get_canonical_hostname(), patterns, | ||
398 | strlen(patterns)) && | ||
399 | !match_hostname(get_remote_ipaddr(), patterns, | ||
400 | strlen(patterns))) { | ||
401 | log("RSA authentication tried for %.100s with correct key but not from a permitted host (host=%.200s, ip=%.200s).", | ||
402 | pw->pw_name, get_canonical_hostname(), | ||
403 | get_remote_ipaddr()); | ||
404 | packet_send_debug("Your host '%.200s' is not permitted to use this key for login.", | ||
405 | get_canonical_hostname()); | ||
406 | xfree(patterns); | ||
407 | authenticated = 0; | ||
408 | break; | ||
409 | } | ||
410 | xfree(patterns); | ||
411 | /* Host name matches. */ | ||
412 | goto next_option; | ||
413 | } | ||
414 | bad_option: | ||
415 | /* Unknown option. */ | ||
416 | log("Bad options in %.100s file, line %lu: %.50s", | ||
417 | SSH_USER_PERMITTED_KEYS, linenum, options); | ||
418 | packet_send_debug("Bad options in %.100s file, line %lu: %.50s", | ||
419 | SSH_USER_PERMITTED_KEYS, linenum, options); | ||
420 | authenticated = 0; | ||
421 | break; | ||
422 | |||
423 | next_option: | ||
424 | /* Skip the comma, and move to the next option | ||
425 | (or break out if there are no more). */ | ||
426 | if (!*options) | ||
427 | fatal("Bugs in auth-rsa.c option processing."); | ||
428 | if (*options == ' ' || *options == '\t') | ||
429 | break; /* End of options. */ | ||
430 | if (*options != ',') | ||
431 | goto bad_option; | ||
432 | options++; | ||
433 | /* Process the next option. */ | ||
434 | continue; | ||
417 | } | 435 | } |
418 | patterns[i++] = *options++; | ||
419 | } | ||
420 | if (!*options) | ||
421 | { | ||
422 | debug("%.100s, line %lu: missing end quote", | ||
423 | SSH_USER_PERMITTED_KEYS, linenum); | ||
424 | packet_send_debug("%.100s, line %lu: missing end quote", | ||
425 | SSH_USER_PERMITTED_KEYS, linenum); | ||
426 | continue; | ||
427 | } | ||
428 | patterns[i] = 0; | ||
429 | options++; | ||
430 | if (!match_hostname(get_canonical_hostname(), patterns, | ||
431 | strlen(patterns)) && | ||
432 | !match_hostname(get_remote_ipaddr(), patterns, | ||
433 | strlen(patterns))) | ||
434 | { | ||
435 | log("RSA authentication tried for %.100s with correct key but not from a permitted host (host=%.200s, ip=%.200s).", | ||
436 | pw->pw_name, get_canonical_hostname(), | ||
437 | get_remote_ipaddr()); | ||
438 | packet_send_debug("Your host '%.200s' is not permitted to use this key for login.", | ||
439 | get_canonical_hostname()); | ||
440 | xfree(patterns); | ||
441 | authenticated = 0; | ||
442 | break; | ||
443 | } | ||
444 | xfree(patterns); | ||
445 | /* Host name matches. */ | ||
446 | goto next_option; | ||
447 | } | 436 | } |
448 | bad_option: | 437 | /* Break out of the loop if authentication was successful; |
449 | /* Unknown option. */ | 438 | otherwise continue searching. */ |
450 | log("Bad options in %.100s file, line %lu: %.50s", | 439 | if (authenticated) |
451 | SSH_USER_PERMITTED_KEYS, linenum, options); | 440 | break; |
452 | packet_send_debug("Bad options in %.100s file, line %lu: %.50s", | ||
453 | SSH_USER_PERMITTED_KEYS, linenum, options); | ||
454 | authenticated = 0; | ||
455 | break; | ||
456 | |||
457 | next_option: | ||
458 | /* Skip the comma, and move to the next option (or break out | ||
459 | if there are no more). */ | ||
460 | if (!*options) | ||
461 | fatal("Bugs in auth-rsa.c option processing."); | ||
462 | if (*options == ' ' || *options == '\t') | ||
463 | break; /* End of options. */ | ||
464 | if (*options != ',') | ||
465 | goto bad_option; | ||
466 | options++; | ||
467 | /* Process the next option. */ | ||
468 | continue; | ||
469 | } | ||
470 | } | 441 | } |
471 | 442 | ||
472 | /* Break out of the loop if authentication was successful; otherwise | 443 | /* Restore the privileged uid. */ |
473 | continue searching. */ | 444 | restore_uid(); |
474 | if (authenticated) | ||
475 | break; | ||
476 | } | ||
477 | 445 | ||
478 | /* Restore the privileged uid. */ | 446 | /* Close the file. */ |
479 | restore_uid(); | 447 | fclose(f); |
480 | 448 | ||
481 | /* Close the file. */ | 449 | /* Clear any mp-int variables. */ |
482 | fclose(f); | 450 | BN_clear_free(n); |
483 | 451 | BN_clear_free(e); | |
484 | /* Clear any mp-int variables. */ | ||
485 | BN_clear_free(n); | ||
486 | BN_clear_free(e); | ||
487 | 452 | ||
488 | if (authenticated) | 453 | if (authenticated) |
489 | packet_send_debug("RSA authentication accepted."); | 454 | packet_send_debug("RSA authentication accepted."); |
490 | 455 | ||
491 | /* Return authentication result. */ | 456 | /* Return authentication result. */ |
492 | return authenticated; | 457 | return authenticated; |
493 | } | 458 | } |
diff --git a/auth-skey.c b/auth-skey.c index 66e09bb59..457100ccc 100644 --- a/auth-skey.c +++ b/auth-skey.c | |||
@@ -1,7 +1,8 @@ | |||
1 | #include "includes.h" | ||
2 | |||
1 | #ifdef SKEY | 3 | #ifdef SKEY |
2 | 4 | ||
3 | #include "includes.h" | 5 | RCSID("$Id: auth-skey.c,v 1.3 1999/11/23 22:25:52 markus Exp $"); |
4 | RCSID("$Id: auth-skey.c,v 1.2 1999/10/16 20:57:52 deraadt Exp $"); | ||
5 | 6 | ||
6 | #include "ssh.h" | 7 | #include "ssh.h" |
7 | #include <sha1.h> | 8 | #include <sha1.h> |
@@ -32,6 +33,7 @@ hash_collapse(s) | |||
32 | 33 | ||
33 | return i; | 34 | return i; |
34 | } | 35 | } |
36 | |||
35 | char * | 37 | char * |
36 | skey_fake_keyinfo(char *username) | 38 | skey_fake_keyinfo(char *username) |
37 | { | 39 | { |
@@ -150,4 +152,4 @@ skey_fake_keyinfo(char *username) | |||
150 | return skeyprompt; | 152 | return skeyprompt; |
151 | } | 153 | } |
152 | 154 | ||
153 | #endif SKEY | 155 | #endif /* SKEY */ |
@@ -1,20 +1,20 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * | |
3 | authfd.c | 3 | * authfd.c |
4 | 4 | * | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
6 | 6 | * | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
8 | All rights reserved | 8 | * All rights reserved |
9 | 9 | * | |
10 | Created: Wed Mar 29 01:30:28 1995 ylo | 10 | * Created: Wed Mar 29 01:30:28 1995 ylo |
11 | 11 | * | |
12 | Functions for connecting the local authentication agent. | 12 | * Functions for connecting the local authentication agent. |
13 | 13 | * | |
14 | */ | 14 | */ |
15 | 15 | ||
16 | #include "includes.h" | 16 | #include "includes.h" |
17 | RCSID("$Id: authfd.c,v 1.6 1999/11/18 21:25:48 damien Exp $"); | 17 | RCSID("$Id: authfd.c,v 1.7 1999/11/24 13:26:21 damien Exp $"); |
18 | 18 | ||
19 | #include "ssh.h" | 19 | #include "ssh.h" |
20 | #include "rsa.h" | 20 | #include "rsa.h" |
@@ -36,45 +36,42 @@ RCSID("$Id: authfd.c,v 1.6 1999/11/18 21:25:48 damien Exp $"); | |||
36 | int | 36 | int |
37 | ssh_get_authentication_socket() | 37 | ssh_get_authentication_socket() |
38 | { | 38 | { |
39 | const char *authsocket; | 39 | const char *authsocket; |
40 | int sock; | 40 | int sock; |
41 | struct sockaddr_un sunaddr; | 41 | struct sockaddr_un sunaddr; |
42 | 42 | ||
43 | authsocket = getenv(SSH_AUTHSOCKET_ENV_NAME); | 43 | authsocket = getenv(SSH_AUTHSOCKET_ENV_NAME); |
44 | if (!authsocket) | 44 | if (!authsocket) |
45 | return -1; | 45 | return -1; |
46 | 46 | ||
47 | sunaddr.sun_family = AF_UNIX; | 47 | sunaddr.sun_family = AF_UNIX; |
48 | strlcpy(sunaddr.sun_path, authsocket, sizeof(sunaddr.sun_path)); | 48 | strlcpy(sunaddr.sun_path, authsocket, sizeof(sunaddr.sun_path)); |
49 | 49 | ||
50 | sock = socket(AF_UNIX, SOCK_STREAM, 0); | 50 | sock = socket(AF_UNIX, SOCK_STREAM, 0); |
51 | if (sock < 0) | 51 | if (sock < 0) |
52 | return -1; | 52 | return -1; |
53 | 53 | ||
54 | /* close on exec */ | 54 | /* close on exec */ |
55 | if (fcntl(sock, F_SETFD, 1) == -1) | 55 | if (fcntl(sock, F_SETFD, 1) == -1) { |
56 | { | 56 | close(sock); |
57 | close(sock); | 57 | return -1; |
58 | return -1; | 58 | } |
59 | } | 59 | if (connect(sock, (struct sockaddr *) & sunaddr, sizeof(sunaddr)) < 0) { |
60 | 60 | close(sock); | |
61 | if (connect(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0) | 61 | return -1; |
62 | { | 62 | } |
63 | close(sock); | 63 | return sock; |
64 | return -1; | ||
65 | } | ||
66 | |||
67 | return sock; | ||
68 | } | 64 | } |
69 | 65 | ||
70 | /* Closes the agent socket if it should be closed (depends on how it was | 66 | /* Closes the agent socket if it should be closed (depends on how it was |
71 | obtained). The argument must have been returned by | 67 | obtained). The argument must have been returned by |
72 | ssh_get_authentication_socket(). */ | 68 | ssh_get_authentication_socket(). */ |
73 | 69 | ||
74 | void ssh_close_authentication_socket(int sock) | 70 | void |
71 | ssh_close_authentication_socket(int sock) | ||
75 | { | 72 | { |
76 | if (getenv(SSH_AUTHSOCKET_ENV_NAME)) | 73 | if (getenv(SSH_AUTHSOCKET_ENV_NAME)) |
77 | close(sock); | 74 | close(sock); |
78 | } | 75 | } |
79 | 76 | ||
80 | /* Opens and connects a private socket for communication with the | 77 | /* Opens and connects a private socket for communication with the |
@@ -83,38 +80,39 @@ void ssh_close_authentication_socket(int sock) | |||
83 | Returns NULL if an error occurred and the connection could not be | 80 | Returns NULL if an error occurred and the connection could not be |
84 | opened. */ | 81 | opened. */ |
85 | 82 | ||
86 | AuthenticationConnection *ssh_get_authentication_connection() | 83 | AuthenticationConnection * |
84 | ssh_get_authentication_connection() | ||
87 | { | 85 | { |
88 | AuthenticationConnection *auth; | 86 | AuthenticationConnection *auth; |
89 | int sock; | 87 | int sock; |
90 | 88 | ||
91 | sock = ssh_get_authentication_socket(); | 89 | sock = ssh_get_authentication_socket(); |
92 | 90 | ||
93 | /* Fail if we couldn't obtain a connection. This happens if we exited | 91 | /* Fail if we couldn't obtain a connection. This happens if we |
94 | due to a timeout. */ | 92 | exited due to a timeout. */ |
95 | if (sock < 0) | 93 | if (sock < 0) |
96 | return NULL; | 94 | return NULL; |
97 | 95 | ||
98 | /* Applocate the connection structure and initialize it. */ | 96 | /* Applocate the connection structure and initialize it. */ |
99 | auth = xmalloc(sizeof(*auth)); | 97 | auth = xmalloc(sizeof(*auth)); |
100 | auth->fd = sock; | 98 | auth->fd = sock; |
101 | buffer_init(&auth->packet); | 99 | buffer_init(&auth->packet); |
102 | buffer_init(&auth->identities); | 100 | buffer_init(&auth->identities); |
103 | auth->howmany = 0; | 101 | auth->howmany = 0; |
104 | 102 | ||
105 | return auth; | 103 | return auth; |
106 | } | 104 | } |
107 | 105 | ||
108 | /* Closes the connection to the authentication agent and frees any associated | 106 | /* Closes the connection to the authentication agent and frees any associated |
109 | memory. */ | 107 | memory. */ |
110 | 108 | ||
111 | void ssh_close_authentication_connection(AuthenticationConnection *ac) | 109 | void |
110 | ssh_close_authentication_connection(AuthenticationConnection *ac) | ||
112 | { | 111 | { |
113 | buffer_free(&ac->packet); | 112 | buffer_free(&ac->packet); |
114 | buffer_free(&ac->identities); | 113 | buffer_free(&ac->identities); |
115 | close(ac->fd); | 114 | close(ac->fd); |
116 | /* Free the connection data structure. */ | 115 | xfree(ac); |
117 | xfree(ac); | ||
118 | } | 116 | } |
119 | 117 | ||
120 | /* Returns the first authentication identity held by the agent. | 118 | /* Returns the first authentication identity held by the agent. |
@@ -126,67 +124,62 @@ int | |||
126 | ssh_get_first_identity(AuthenticationConnection *auth, | 124 | ssh_get_first_identity(AuthenticationConnection *auth, |
127 | BIGNUM *e, BIGNUM *n, char **comment) | 125 | BIGNUM *e, BIGNUM *n, char **comment) |
128 | { | 126 | { |
129 | unsigned char msg[8192]; | 127 | unsigned char msg[8192]; |
130 | int len, l; | 128 | int len, l; |
131 | 129 | ||
132 | /* Send a message to the agent requesting for a list of the identities | 130 | /* Send a message to the agent requesting for a list of the |
133 | it can represent. */ | 131 | identities it can represent. */ |
134 | msg[0] = 0; | 132 | msg[0] = 0; |
135 | msg[1] = 0; | 133 | msg[1] = 0; |
136 | msg[2] = 0; | 134 | msg[2] = 0; |
137 | msg[3] = 1; | 135 | msg[3] = 1; |
138 | msg[4] = SSH_AGENTC_REQUEST_RSA_IDENTITIES; | 136 | msg[4] = SSH_AGENTC_REQUEST_RSA_IDENTITIES; |
139 | if (write(auth->fd, msg, 5) != 5) | 137 | if (write(auth->fd, msg, 5) != 5) { |
140 | { | 138 | error("write auth->fd: %.100s", strerror(errno)); |
141 | error("write auth->fd: %.100s", strerror(errno)); | 139 | return 0; |
142 | return 0; | 140 | } |
143 | } | 141 | /* Read the length of the response. XXX implement timeouts here. */ |
144 | 142 | len = 4; | |
145 | /* Read the length of the response. XXX implement timeouts here. */ | 143 | while (len > 0) { |
146 | len = 4; | 144 | l = read(auth->fd, msg + 4 - len, len); |
147 | while (len > 0) | 145 | if (l <= 0) { |
148 | { | 146 | error("read auth->fd: %.100s", strerror(errno)); |
149 | l = read(auth->fd, msg + 4 - len, len); | 147 | return 0; |
150 | if (l <= 0) | 148 | } |
151 | { | 149 | len -= l; |
152 | error("read auth->fd: %.100s", strerror(errno)); | 150 | } |
153 | return 0; | 151 | |
152 | /* Extract the length, and check it for sanity. (We cannot trust | ||
153 | authentication agents). */ | ||
154 | len = GET_32BIT(msg); | ||
155 | if (len < 1 || len > 256 * 1024) | ||
156 | fatal("Authentication reply message too long: %d\n", len); | ||
157 | |||
158 | /* Read the packet itself. */ | ||
159 | buffer_clear(&auth->identities); | ||
160 | while (len > 0) { | ||
161 | l = len; | ||
162 | if (l > sizeof(msg)) | ||
163 | l = sizeof(msg); | ||
164 | l = read(auth->fd, msg, l); | ||
165 | if (l <= 0) | ||
166 | fatal("Incomplete authentication reply."); | ||
167 | buffer_append(&auth->identities, (char *) msg, l); | ||
168 | len -= l; | ||
154 | } | 169 | } |
155 | len -= l; | 170 | |
156 | } | 171 | /* Get message type, and verify that we got a proper answer. */ |
157 | 172 | buffer_get(&auth->identities, (char *) msg, 1); | |
158 | /* Extract the length, and check it for sanity. (We cannot trust | 173 | if (msg[0] != SSH_AGENT_RSA_IDENTITIES_ANSWER) |
159 | authentication agents). */ | 174 | fatal("Bad authentication reply message type: %d", msg[0]); |
160 | len = GET_32BIT(msg); | 175 | |
161 | if (len < 1 || len > 256*1024) | 176 | /* Get the number of entries in the response and check it for sanity. */ |
162 | fatal("Authentication reply message too long: %d\n", len); | 177 | auth->howmany = buffer_get_int(&auth->identities); |
163 | 178 | if (auth->howmany > 1024) | |
164 | /* Read the packet itself. */ | 179 | fatal("Too many identities in authentication reply: %d\n", auth->howmany); |
165 | buffer_clear(&auth->identities); | 180 | |
166 | while (len > 0) | 181 | /* Return the first entry (if any). */ |
167 | { | 182 | return ssh_get_next_identity(auth, e, n, comment); |
168 | l = len; | ||
169 | if (l > sizeof(msg)) | ||
170 | l = sizeof(msg); | ||
171 | l = read(auth->fd, msg, l); | ||
172 | if (l <= 0) | ||
173 | fatal("Incomplete authentication reply."); | ||
174 | buffer_append(&auth->identities, (char *)msg, l); | ||
175 | len -= l; | ||
176 | } | ||
177 | |||
178 | /* Get message type, and verify that we got a proper answer. */ | ||
179 | buffer_get(&auth->identities, (char *)msg, 1); | ||
180 | if (msg[0] != SSH_AGENT_RSA_IDENTITIES_ANSWER) | ||
181 | fatal("Bad authentication reply message type: %d", msg[0]); | ||
182 | |||
183 | /* Get the number of entries in the response and check it for sanity. */ | ||
184 | auth->howmany = buffer_get_int(&auth->identities); | ||
185 | if (auth->howmany > 1024) | ||
186 | fatal("Too many identities in authentication reply: %d\n", auth->howmany); | ||
187 | |||
188 | /* Return the first entry (if any). */ | ||
189 | return ssh_get_next_identity(auth, e, n, comment); | ||
190 | } | 183 | } |
191 | 184 | ||
192 | /* Returns the next authentication identity for the agent. Other functions | 185 | /* Returns the next authentication identity for the agent. Other functions |
@@ -198,27 +191,27 @@ int | |||
198 | ssh_get_next_identity(AuthenticationConnection *auth, | 191 | ssh_get_next_identity(AuthenticationConnection *auth, |
199 | BIGNUM *e, BIGNUM *n, char **comment) | 192 | BIGNUM *e, BIGNUM *n, char **comment) |
200 | { | 193 | { |
201 | unsigned int bits; | 194 | unsigned int bits; |
202 | 195 | ||
203 | /* Return failure if no more entries. */ | 196 | /* Return failure if no more entries. */ |
204 | if (auth->howmany <= 0) | 197 | if (auth->howmany <= 0) |
205 | return 0; | 198 | return 0; |
206 | 199 | ||
207 | /* Get the next entry from the packet. These will abort with a fatal | 200 | /* Get the next entry from the packet. These will abort with a |
208 | error if the packet is too short or contains corrupt data. */ | 201 | fatal error if the packet is too short or contains corrupt data. */ |
209 | bits = buffer_get_int(&auth->identities); | 202 | bits = buffer_get_int(&auth->identities); |
210 | buffer_get_bignum(&auth->identities, e); | 203 | buffer_get_bignum(&auth->identities, e); |
211 | buffer_get_bignum(&auth->identities, n); | 204 | buffer_get_bignum(&auth->identities, n); |
212 | *comment = buffer_get_string(&auth->identities, NULL); | 205 | *comment = buffer_get_string(&auth->identities, NULL); |
213 | 206 | ||
214 | if (bits != BN_num_bits(n)) | 207 | if (bits != BN_num_bits(n)) |
215 | error("Warning: keysize mismatch: actual %d, announced %u", | 208 | error("Warning: keysize mismatch: actual %d, announced %u", |
216 | BN_num_bits(n), bits); | 209 | BN_num_bits(n), bits); |
217 | 210 | ||
218 | /* Decrement the number of remaining entries. */ | 211 | /* Decrement the number of remaining entries. */ |
219 | auth->howmany--; | 212 | auth->howmany--; |
220 | 213 | ||
221 | return 1; | 214 | return 1; |
222 | } | 215 | } |
223 | 216 | ||
224 | /* Generates a random challenge, sends it to the agent, and waits for response | 217 | /* Generates a random challenge, sends it to the agent, and waits for response |
@@ -229,355 +222,329 @@ ssh_get_next_identity(AuthenticationConnection *auth, | |||
229 | 222 | ||
230 | int | 223 | int |
231 | ssh_decrypt_challenge(AuthenticationConnection *auth, | 224 | ssh_decrypt_challenge(AuthenticationConnection *auth, |
232 | BIGNUM *e, BIGNUM *n, BIGNUM *challenge, | 225 | BIGNUM* e, BIGNUM *n, BIGNUM *challenge, |
233 | unsigned char session_id[16], | 226 | unsigned char session_id[16], |
234 | unsigned int response_type, | 227 | unsigned int response_type, |
235 | unsigned char response[16]) | 228 | unsigned char response[16]) |
236 | { | 229 | { |
237 | Buffer buffer; | 230 | Buffer buffer; |
238 | unsigned char buf[8192]; | 231 | unsigned char buf[8192]; |
239 | int len, l, i; | 232 | int len, l, i; |
240 | 233 | ||
241 | /* Response type 0 is no longer supported. */ | 234 | /* Response type 0 is no longer supported. */ |
242 | if (response_type == 0) | 235 | if (response_type == 0) |
243 | fatal("Compatibility with ssh protocol version 1.0 no longer supported."); | 236 | fatal("Compatibility with ssh protocol version 1.0 no longer supported."); |
244 | 237 | ||
245 | /* Format a message to the agent. */ | 238 | /* Format a message to the agent. */ |
246 | buf[0] = SSH_AGENTC_RSA_CHALLENGE; | 239 | buf[0] = SSH_AGENTC_RSA_CHALLENGE; |
247 | buffer_init(&buffer); | 240 | buffer_init(&buffer); |
248 | buffer_append(&buffer, (char *)buf, 1); | 241 | buffer_append(&buffer, (char *) buf, 1); |
249 | buffer_put_int(&buffer, BN_num_bits(n)); | 242 | buffer_put_int(&buffer, BN_num_bits(n)); |
250 | buffer_put_bignum(&buffer, e); | 243 | buffer_put_bignum(&buffer, e); |
251 | buffer_put_bignum(&buffer, n); | 244 | buffer_put_bignum(&buffer, n); |
252 | buffer_put_bignum(&buffer, challenge); | 245 | buffer_put_bignum(&buffer, challenge); |
253 | buffer_append(&buffer, (char *)session_id, 16); | 246 | buffer_append(&buffer, (char *) session_id, 16); |
254 | buffer_put_int(&buffer, response_type); | 247 | buffer_put_int(&buffer, response_type); |
255 | 248 | ||
256 | /* Get the length of the message, and format it in the buffer. */ | 249 | /* Get the length of the message, and format it in the buffer. */ |
257 | len = buffer_len(&buffer); | 250 | len = buffer_len(&buffer); |
258 | PUT_32BIT(buf, len); | 251 | PUT_32BIT(buf, len); |
259 | 252 | ||
260 | /* Send the length and then the packet to the agent. */ | 253 | /* Send the length and then the packet to the agent. */ |
261 | if (write(auth->fd, buf, 4) != 4 || | 254 | if (write(auth->fd, buf, 4) != 4 || |
262 | write(auth->fd, buffer_ptr(&buffer), buffer_len(&buffer)) != | 255 | write(auth->fd, buffer_ptr(&buffer), buffer_len(&buffer)) != |
263 | buffer_len(&buffer)) | 256 | buffer_len(&buffer)) { |
264 | { | 257 | error("Error writing to authentication socket."); |
265 | error("Error writing to authentication socket."); | 258 | error_cleanup: |
266 | error_cleanup: | 259 | buffer_free(&buffer); |
267 | buffer_free(&buffer); | 260 | return 0; |
268 | return 0; | 261 | } |
269 | } | 262 | /* Wait for response from the agent. First read the length of the |
270 | 263 | response packet. */ | |
271 | /* Wait for response from the agent. First read the length of the | 264 | len = 4; |
272 | response packet. */ | 265 | while (len > 0) { |
273 | len = 4; | 266 | l = read(auth->fd, buf + 4 - len, len); |
274 | while (len > 0) | 267 | if (l <= 0) { |
275 | { | 268 | error("Error reading response length from authentication socket."); |
276 | l = read(auth->fd, buf + 4 - len, len); | 269 | goto error_cleanup; |
277 | if (l <= 0) | 270 | } |
278 | { | 271 | len -= l; |
279 | error("Error reading response length from authentication socket."); | 272 | } |
280 | goto error_cleanup; | 273 | |
274 | /* Extract the length, and check it for sanity. */ | ||
275 | len = GET_32BIT(buf); | ||
276 | if (len > 256 * 1024) | ||
277 | fatal("Authentication response too long: %d", len); | ||
278 | |||
279 | /* Read the rest of the response in tothe buffer. */ | ||
280 | buffer_clear(&buffer); | ||
281 | while (len > 0) { | ||
282 | l = len; | ||
283 | if (l > sizeof(buf)) | ||
284 | l = sizeof(buf); | ||
285 | l = read(auth->fd, buf, l); | ||
286 | if (l <= 0) { | ||
287 | error("Error reading response from authentication socket."); | ||
288 | goto error_cleanup; | ||
289 | } | ||
290 | buffer_append(&buffer, (char *) buf, l); | ||
291 | len -= l; | ||
281 | } | 292 | } |
282 | len -= l; | 293 | |
283 | } | 294 | /* Get the type of the packet. */ |
284 | 295 | buffer_get(&buffer, (char *) buf, 1); | |
285 | /* Extract the length, and check it for sanity. */ | 296 | |
286 | len = GET_32BIT(buf); | 297 | /* Check for agent failure message. */ |
287 | if (len > 256*1024) | 298 | if (buf[0] == SSH_AGENT_FAILURE) { |
288 | fatal("Authentication response too long: %d", len); | 299 | log("Agent admitted failure to authenticate using the key."); |
289 | 300 | goto error_cleanup; | |
290 | /* Read the rest of the response in tothe buffer. */ | ||
291 | buffer_clear(&buffer); | ||
292 | while (len > 0) | ||
293 | { | ||
294 | l = len; | ||
295 | if (l > sizeof(buf)) | ||
296 | l = sizeof(buf); | ||
297 | l = read(auth->fd, buf, l); | ||
298 | if (l <= 0) | ||
299 | { | ||
300 | error("Error reading response from authentication socket."); | ||
301 | goto error_cleanup; | ||
302 | } | 301 | } |
303 | buffer_append(&buffer, (char *)buf, l); | 302 | /* Now it must be an authentication response packet. */ |
304 | len -= l; | 303 | if (buf[0] != SSH_AGENT_RSA_RESPONSE) |
305 | } | 304 | fatal("Bad authentication response: %d", buf[0]); |
306 | 305 | ||
307 | /* Get the type of the packet. */ | 306 | /* Get the response from the packet. This will abort with a fatal |
308 | buffer_get(&buffer, (char *)buf, 1); | 307 | error if the packet is corrupt. */ |
309 | 308 | for (i = 0; i < 16; i++) | |
310 | /* Check for agent failure message. */ | 309 | response[i] = buffer_get_char(&buffer); |
311 | if (buf[0] == SSH_AGENT_FAILURE) | 310 | |
312 | { | 311 | /* The buffer containing the packet is no longer needed. */ |
313 | log("Agent admitted failure to authenticate using the key."); | 312 | buffer_free(&buffer); |
314 | goto error_cleanup; | 313 | |
315 | } | 314 | /* Correct answer. */ |
316 | 315 | return 1; | |
317 | /* Now it must be an authentication response packet. */ | 316 | } |
318 | if (buf[0] != SSH_AGENT_RSA_RESPONSE) | ||
319 | fatal("Bad authentication response: %d", buf[0]); | ||
320 | |||
321 | /* Get the response from the packet. This will abort with a fatal error | ||
322 | if the packet is corrupt. */ | ||
323 | for (i = 0; i < 16; i++) | ||
324 | response[i] = buffer_get_char(&buffer); | ||
325 | |||
326 | /* The buffer containing the packet is no longer needed. */ | ||
327 | buffer_free(&buffer); | ||
328 | |||
329 | /* Correct answer. */ | ||
330 | return 1; | ||
331 | } | ||
332 | 317 | ||
333 | /* Adds an identity to the authentication server. This call is not meant to | 318 | /* Adds an identity to the authentication server. This call is not meant to |
334 | be used by normal applications. */ | 319 | be used by normal applications. */ |
335 | 320 | ||
336 | int ssh_add_identity(AuthenticationConnection *auth, | 321 | int |
337 | RSA *key, const char *comment) | 322 | ssh_add_identity(AuthenticationConnection *auth, |
323 | RSA * key, const char *comment) | ||
338 | { | 324 | { |
339 | Buffer buffer; | 325 | Buffer buffer; |
340 | unsigned char buf[8192]; | 326 | unsigned char buf[8192]; |
341 | int len, l, type; | 327 | int len, l, type; |
342 | 328 | ||
343 | /* Format a message to the agent. */ | 329 | /* Format a message to the agent. */ |
344 | buffer_init(&buffer); | 330 | buffer_init(&buffer); |
345 | buffer_put_char(&buffer, SSH_AGENTC_ADD_RSA_IDENTITY); | 331 | buffer_put_char(&buffer, SSH_AGENTC_ADD_RSA_IDENTITY); |
346 | buffer_put_int(&buffer, BN_num_bits(key->n)); | 332 | buffer_put_int(&buffer, BN_num_bits(key->n)); |
347 | buffer_put_bignum(&buffer, key->n); | 333 | buffer_put_bignum(&buffer, key->n); |
348 | buffer_put_bignum(&buffer, key->e); | 334 | buffer_put_bignum(&buffer, key->e); |
349 | buffer_put_bignum(&buffer, key->d); | 335 | buffer_put_bignum(&buffer, key->d); |
350 | /* To keep within the protocol: p < q for ssh. in SSL p > q */ | 336 | /* To keep within the protocol: p < q for ssh. in SSL p > q */ |
351 | buffer_put_bignum(&buffer, key->iqmp); /* ssh key->u */ | 337 | buffer_put_bignum(&buffer, key->iqmp); /* ssh key->u */ |
352 | buffer_put_bignum(&buffer, key->q); /* ssh key->p, SSL key->q */ | 338 | buffer_put_bignum(&buffer, key->q); /* ssh key->p, SSL key->q */ |
353 | buffer_put_bignum(&buffer, key->p); /* ssh key->q, SSL key->p */ | 339 | buffer_put_bignum(&buffer, key->p); /* ssh key->q, SSL key->p */ |
354 | buffer_put_string(&buffer, comment, strlen(comment)); | 340 | buffer_put_string(&buffer, comment, strlen(comment)); |
355 | 341 | ||
356 | /* Get the length of the message, and format it in the buffer. */ | 342 | /* Get the length of the message, and format it in the buffer. */ |
357 | len = buffer_len(&buffer); | 343 | len = buffer_len(&buffer); |
358 | PUT_32BIT(buf, len); | 344 | PUT_32BIT(buf, len); |
359 | 345 | ||
360 | /* Send the length and then the packet to the agent. */ | 346 | /* Send the length and then the packet to the agent. */ |
361 | if (write(auth->fd, buf, 4) != 4 || | 347 | if (write(auth->fd, buf, 4) != 4 || |
362 | write(auth->fd, buffer_ptr(&buffer), buffer_len(&buffer)) != | 348 | write(auth->fd, buffer_ptr(&buffer), buffer_len(&buffer)) != |
363 | buffer_len(&buffer)) | 349 | buffer_len(&buffer)) { |
364 | { | 350 | error("Error writing to authentication socket."); |
365 | error("Error writing to authentication socket."); | 351 | error_cleanup: |
366 | error_cleanup: | 352 | buffer_free(&buffer); |
367 | buffer_free(&buffer); | 353 | return 0; |
368 | return 0; | 354 | } |
369 | } | 355 | /* Wait for response from the agent. First read the length of the |
370 | 356 | response packet. */ | |
371 | /* Wait for response from the agent. First read the length of the | 357 | len = 4; |
372 | response packet. */ | 358 | while (len > 0) { |
373 | len = 4; | 359 | l = read(auth->fd, buf + 4 - len, len); |
374 | while (len > 0) | 360 | if (l <= 0) { |
375 | { | 361 | error("Error reading response length from authentication socket."); |
376 | l = read(auth->fd, buf + 4 - len, len); | 362 | goto error_cleanup; |
377 | if (l <= 0) | 363 | } |
378 | { | 364 | len -= l; |
379 | error("Error reading response length from authentication socket."); | 365 | } |
380 | goto error_cleanup; | 366 | |
367 | /* Extract the length, and check it for sanity. */ | ||
368 | len = GET_32BIT(buf); | ||
369 | if (len > 256 * 1024) | ||
370 | fatal("Add identity response too long: %d", len); | ||
371 | |||
372 | /* Read the rest of the response in tothe buffer. */ | ||
373 | buffer_clear(&buffer); | ||
374 | while (len > 0) { | ||
375 | l = len; | ||
376 | if (l > sizeof(buf)) | ||
377 | l = sizeof(buf); | ||
378 | l = read(auth->fd, buf, l); | ||
379 | if (l <= 0) { | ||
380 | error("Error reading response from authentication socket."); | ||
381 | goto error_cleanup; | ||
382 | } | ||
383 | buffer_append(&buffer, (char *) buf, l); | ||
384 | len -= l; | ||
381 | } | 385 | } |
382 | len -= l; | 386 | |
383 | } | 387 | /* Get the type of the packet. */ |
384 | 388 | type = buffer_get_char(&buffer); | |
385 | /* Extract the length, and check it for sanity. */ | 389 | switch (type) { |
386 | len = GET_32BIT(buf); | 390 | case SSH_AGENT_FAILURE: |
387 | if (len > 256*1024) | 391 | buffer_free(&buffer); |
388 | fatal("Add identity response too long: %d", len); | 392 | return 0; |
389 | 393 | case SSH_AGENT_SUCCESS: | |
390 | /* Read the rest of the response in tothe buffer. */ | 394 | buffer_free(&buffer); |
391 | buffer_clear(&buffer); | 395 | return 1; |
392 | while (len > 0) | 396 | default: |
393 | { | 397 | fatal("Bad response to add identity from authentication agent: %d", |
394 | l = len; | 398 | type); |
395 | if (l > sizeof(buf)) | ||
396 | l = sizeof(buf); | ||
397 | l = read(auth->fd, buf, l); | ||
398 | if (l <= 0) | ||
399 | { | ||
400 | error("Error reading response from authentication socket."); | ||
401 | goto error_cleanup; | ||
402 | } | 399 | } |
403 | buffer_append(&buffer, (char *)buf, l); | 400 | /* NOTREACHED */ |
404 | len -= l; | 401 | return 0; |
405 | } | 402 | } |
406 | 403 | ||
407 | /* Get the type of the packet. */ | 404 | /* Removes an identity from the authentication server. This call is not meant |
408 | type = buffer_get_char(&buffer); | ||
409 | switch (type) | ||
410 | { | ||
411 | case SSH_AGENT_FAILURE: | ||
412 | buffer_free(&buffer); | ||
413 | return 0; | ||
414 | case SSH_AGENT_SUCCESS: | ||
415 | buffer_free(&buffer); | ||
416 | return 1; | ||
417 | default: | ||
418 | fatal("Bad response to add identity from authentication agent: %d", | ||
419 | type); | ||
420 | } | ||
421 | /*NOTREACHED*/ | ||
422 | return 0; | ||
423 | } | ||
424 | |||
425 | /* Removes an identity from the authentication server. This call is not meant | ||
426 | to be used by normal applications. */ | 405 | to be used by normal applications. */ |
427 | 406 | ||
428 | int ssh_remove_identity(AuthenticationConnection *auth, RSA *key) | 407 | int |
408 | ssh_remove_identity(AuthenticationConnection *auth, RSA *key) | ||
429 | { | 409 | { |
430 | Buffer buffer; | 410 | Buffer buffer; |
431 | unsigned char buf[8192]; | 411 | unsigned char buf[8192]; |
432 | int len, l, type; | 412 | int len, l, type; |
433 | 413 | ||
434 | /* Format a message to the agent. */ | 414 | /* Format a message to the agent. */ |
435 | buffer_init(&buffer); | 415 | buffer_init(&buffer); |
436 | buffer_put_char(&buffer, SSH_AGENTC_REMOVE_RSA_IDENTITY); | 416 | buffer_put_char(&buffer, SSH_AGENTC_REMOVE_RSA_IDENTITY); |
437 | buffer_put_int(&buffer, BN_num_bits(key->n)); | 417 | buffer_put_int(&buffer, BN_num_bits(key->n)); |
438 | buffer_put_bignum(&buffer, key->e); | 418 | buffer_put_bignum(&buffer, key->e); |
439 | buffer_put_bignum(&buffer, key->n); | 419 | buffer_put_bignum(&buffer, key->n); |
440 | 420 | ||
441 | /* Get the length of the message, and format it in the buffer. */ | 421 | /* Get the length of the message, and format it in the buffer. */ |
442 | len = buffer_len(&buffer); | 422 | len = buffer_len(&buffer); |
443 | PUT_32BIT(buf, len); | 423 | PUT_32BIT(buf, len); |
444 | 424 | ||
445 | /* Send the length and then the packet to the agent. */ | 425 | /* Send the length and then the packet to the agent. */ |
446 | if (write(auth->fd, buf, 4) != 4 || | 426 | if (write(auth->fd, buf, 4) != 4 || |
447 | write(auth->fd, buffer_ptr(&buffer), buffer_len(&buffer)) != | 427 | write(auth->fd, buffer_ptr(&buffer), buffer_len(&buffer)) != |
448 | buffer_len(&buffer)) | 428 | buffer_len(&buffer)) { |
449 | { | 429 | error("Error writing to authentication socket."); |
450 | error("Error writing to authentication socket."); | 430 | error_cleanup: |
451 | error_cleanup: | 431 | buffer_free(&buffer); |
452 | buffer_free(&buffer); | 432 | return 0; |
453 | return 0; | 433 | } |
454 | } | 434 | /* Wait for response from the agent. First read the length of the |
455 | 435 | response packet. */ | |
456 | /* Wait for response from the agent. First read the length of the | 436 | len = 4; |
457 | response packet. */ | 437 | while (len > 0) { |
458 | len = 4; | 438 | l = read(auth->fd, buf + 4 - len, len); |
459 | while (len > 0) | 439 | if (l <= 0) { |
460 | { | 440 | error("Error reading response length from authentication socket."); |
461 | l = read(auth->fd, buf + 4 - len, len); | 441 | goto error_cleanup; |
462 | if (l <= 0) | 442 | } |
463 | { | 443 | len -= l; |
464 | error("Error reading response length from authentication socket."); | 444 | } |
465 | goto error_cleanup; | 445 | |
446 | /* Extract the length, and check it for sanity. */ | ||
447 | len = GET_32BIT(buf); | ||
448 | if (len > 256 * 1024) | ||
449 | fatal("Remove identity response too long: %d", len); | ||
450 | |||
451 | /* Read the rest of the response in tothe buffer. */ | ||
452 | buffer_clear(&buffer); | ||
453 | while (len > 0) { | ||
454 | l = len; | ||
455 | if (l > sizeof(buf)) | ||
456 | l = sizeof(buf); | ||
457 | l = read(auth->fd, buf, l); | ||
458 | if (l <= 0) { | ||
459 | error("Error reading response from authentication socket."); | ||
460 | goto error_cleanup; | ||
461 | } | ||
462 | buffer_append(&buffer, (char *) buf, l); | ||
463 | len -= l; | ||
466 | } | 464 | } |
467 | len -= l; | 465 | |
468 | } | 466 | /* Get the type of the packet. */ |
469 | 467 | type = buffer_get_char(&buffer); | |
470 | /* Extract the length, and check it for sanity. */ | 468 | switch (type) { |
471 | len = GET_32BIT(buf); | 469 | case SSH_AGENT_FAILURE: |
472 | if (len > 256*1024) | 470 | buffer_free(&buffer); |
473 | fatal("Remove identity response too long: %d", len); | 471 | return 0; |
474 | 472 | case SSH_AGENT_SUCCESS: | |
475 | /* Read the rest of the response in tothe buffer. */ | 473 | buffer_free(&buffer); |
476 | buffer_clear(&buffer); | 474 | return 1; |
477 | while (len > 0) | 475 | default: |
478 | { | 476 | fatal("Bad response to remove identity from authentication agent: %d", |
479 | l = len; | 477 | type); |
480 | if (l > sizeof(buf)) | ||
481 | l = sizeof(buf); | ||
482 | l = read(auth->fd, buf, l); | ||
483 | if (l <= 0) | ||
484 | { | ||
485 | error("Error reading response from authentication socket."); | ||
486 | goto error_cleanup; | ||
487 | } | 478 | } |
488 | buffer_append(&buffer, (char *)buf, l); | 479 | /* NOTREACHED */ |
489 | len -= l; | 480 | return 0; |
490 | } | 481 | } |
491 | 482 | ||
492 | /* Get the type of the packet. */ | 483 | /* Removes all identities from the agent. This call is not meant |
493 | type = buffer_get_char(&buffer); | ||
494 | switch (type) | ||
495 | { | ||
496 | case SSH_AGENT_FAILURE: | ||
497 | buffer_free(&buffer); | ||
498 | return 0; | ||
499 | case SSH_AGENT_SUCCESS: | ||
500 | buffer_free(&buffer); | ||
501 | return 1; | ||
502 | default: | ||
503 | fatal("Bad response to remove identity from authentication agent: %d", | ||
504 | type); | ||
505 | } | ||
506 | /*NOTREACHED*/ | ||
507 | return 0; | ||
508 | } | ||
509 | |||
510 | /* Removes all identities from the agent. This call is not meant | ||
511 | to be used by normal applications. */ | 484 | to be used by normal applications. */ |
512 | 485 | ||
513 | int ssh_remove_all_identities(AuthenticationConnection *auth) | 486 | int |
487 | ssh_remove_all_identities(AuthenticationConnection *auth) | ||
514 | { | 488 | { |
515 | Buffer buffer; | 489 | Buffer buffer; |
516 | unsigned char buf[8192]; | 490 | unsigned char buf[8192]; |
517 | int len, l, type; | 491 | int len, l, type; |
518 | 492 | ||
519 | /* Get the length of the message, and format it in the buffer. */ | 493 | /* Get the length of the message, and format it in the buffer. */ |
520 | PUT_32BIT(buf, 1); | 494 | PUT_32BIT(buf, 1); |
521 | buf[4] = SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES; | 495 | buf[4] = SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES; |
522 | 496 | ||
523 | /* Send the length and then the packet to the agent. */ | 497 | /* Send the length and then the packet to the agent. */ |
524 | if (write(auth->fd, buf, 5) != 5) | 498 | if (write(auth->fd, buf, 5) != 5) { |
525 | { | 499 | error("Error writing to authentication socket."); |
526 | error("Error writing to authentication socket."); | 500 | return 0; |
527 | return 0; | 501 | } |
528 | } | 502 | /* Wait for response from the agent. First read the length of the |
529 | 503 | response packet. */ | |
530 | /* Wait for response from the agent. First read the length of the | 504 | len = 4; |
531 | response packet. */ | 505 | while (len > 0) { |
532 | len = 4; | 506 | l = read(auth->fd, buf + 4 - len, len); |
533 | while (len > 0) | 507 | if (l <= 0) { |
534 | { | 508 | error("Error reading response length from authentication socket."); |
535 | l = read(auth->fd, buf + 4 - len, len); | 509 | return 0; |
536 | if (l <= 0) | 510 | } |
537 | { | 511 | len -= l; |
538 | error("Error reading response length from authentication socket."); | 512 | } |
539 | return 0; | 513 | |
514 | /* Extract the length, and check it for sanity. */ | ||
515 | len = GET_32BIT(buf); | ||
516 | if (len > 256 * 1024) | ||
517 | fatal("Remove identity response too long: %d", len); | ||
518 | |||
519 | /* Read the rest of the response into the buffer. */ | ||
520 | buffer_init(&buffer); | ||
521 | while (len > 0) { | ||
522 | l = len; | ||
523 | if (l > sizeof(buf)) | ||
524 | l = sizeof(buf); | ||
525 | l = read(auth->fd, buf, l); | ||
526 | if (l <= 0) { | ||
527 | error("Error reading response from authentication socket."); | ||
528 | buffer_free(&buffer); | ||
529 | return 0; | ||
530 | } | ||
531 | buffer_append(&buffer, (char *) buf, l); | ||
532 | len -= l; | ||
540 | } | 533 | } |
541 | len -= l; | 534 | |
542 | } | 535 | /* Get the type of the packet. */ |
543 | 536 | type = buffer_get_char(&buffer); | |
544 | /* Extract the length, and check it for sanity. */ | 537 | switch (type) { |
545 | len = GET_32BIT(buf); | 538 | case SSH_AGENT_FAILURE: |
546 | if (len > 256*1024) | 539 | buffer_free(&buffer); |
547 | fatal("Remove identity response too long: %d", len); | 540 | return 0; |
548 | 541 | case SSH_AGENT_SUCCESS: | |
549 | /* Read the rest of the response into the buffer. */ | 542 | buffer_free(&buffer); |
550 | buffer_init(&buffer); | 543 | return 1; |
551 | while (len > 0) | 544 | default: |
552 | { | 545 | fatal("Bad response to remove identity from authentication agent: %d", |
553 | l = len; | 546 | type); |
554 | if (l > sizeof(buf)) | ||
555 | l = sizeof(buf); | ||
556 | l = read(auth->fd, buf, l); | ||
557 | if (l <= 0) | ||
558 | { | ||
559 | error("Error reading response from authentication socket."); | ||
560 | buffer_free(&buffer); | ||
561 | return 0; | ||
562 | } | 547 | } |
563 | buffer_append(&buffer, (char *)buf, l); | 548 | /* NOTREACHED */ |
564 | len -= l; | 549 | return 0; |
565 | } | 550 | } |
566 | |||
567 | /* Get the type of the packet. */ | ||
568 | type = buffer_get_char(&buffer); | ||
569 | switch (type) | ||
570 | { | ||
571 | case SSH_AGENT_FAILURE: | ||
572 | buffer_free(&buffer); | ||
573 | return 0; | ||
574 | case SSH_AGENT_SUCCESS: | ||
575 | buffer_free(&buffer); | ||
576 | return 1; | ||
577 | default: | ||
578 | fatal("Bad response to remove identity from authentication agent: %d", | ||
579 | type); | ||
580 | } | ||
581 | /*NOTREACHED*/ | ||
582 | return 0; | ||
583 | } | ||
@@ -1,19 +1,19 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * | |
3 | authfd.h | 3 | * authfd.h |
4 | 4 | * | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
6 | 6 | * | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
8 | All rights reserved | 8 | * All rights reserved |
9 | 9 | * | |
10 | Created: Wed Mar 29 01:17:41 1995 ylo | 10 | * Created: Wed Mar 29 01:17:41 1995 ylo |
11 | 11 | * | |
12 | Functions to interface with the SSH_AUTHENTICATION_FD socket. | 12 | * Functions to interface with the SSH_AUTHENTICATION_FD socket. |
13 | 13 | * | |
14 | */ | 14 | */ |
15 | 15 | ||
16 | /* RCSID("$Id: authfd.h,v 1.2 1999/11/16 02:37:16 damien Exp $"); */ | 16 | /* RCSID("$Id: authfd.h,v 1.3 1999/11/24 13:26:22 damien Exp $"); */ |
17 | 17 | ||
18 | #ifndef AUTHFD_H | 18 | #ifndef AUTHFD_H |
19 | #define AUTHFD_H | 19 | #define AUTHFD_H |
@@ -31,72 +31,73 @@ Functions to interface with the SSH_AUTHENTICATION_FD socket. | |||
31 | #define SSH_AGENTC_REMOVE_RSA_IDENTITY 8 | 31 | #define SSH_AGENTC_REMOVE_RSA_IDENTITY 8 |
32 | #define SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES 9 | 32 | #define SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES 9 |
33 | 33 | ||
34 | typedef struct | 34 | typedef struct { |
35 | { | 35 | int fd; |
36 | int fd; | 36 | Buffer packet; |
37 | Buffer packet; | 37 | Buffer identities; |
38 | Buffer identities; | 38 | int howmany; |
39 | int howmany; | 39 | } AuthenticationConnection; |
40 | } AuthenticationConnection; | ||
41 | |||
42 | /* Returns the number of the authentication fd, or -1 if there is none. */ | 40 | /* Returns the number of the authentication fd, or -1 if there is none. */ |
43 | int ssh_get_authentication_socket(); | 41 | int ssh_get_authentication_socket(); |
44 | 42 | ||
45 | /* This should be called for any descriptor returned by | 43 | /* This should be called for any descriptor returned by |
46 | ssh_get_authentication_socket(). Depending on the way the descriptor was | 44 | ssh_get_authentication_socket(). Depending on the way the descriptor was |
47 | obtained, this may close the descriptor. */ | 45 | obtained, this may close the descriptor. */ |
48 | void ssh_close_authentication_socket(int authfd); | 46 | void ssh_close_authentication_socket(int authfd); |
49 | 47 | ||
50 | /* Opens and connects a private socket for communication with the | 48 | /* Opens and connects a private socket for communication with the |
51 | authentication agent. Returns NULL if an error occurred and the | 49 | authentication agent. Returns NULL if an error occurred and the |
52 | connection could not be opened. The connection should be closed by | 50 | connection could not be opened. The connection should be closed by |
53 | the caller by calling ssh_close_authentication_connection(). */ | 51 | the caller by calling ssh_close_authentication_connection(). */ |
54 | AuthenticationConnection *ssh_get_authentication_connection(); | 52 | AuthenticationConnection *ssh_get_authentication_connection(); |
55 | 53 | ||
56 | /* Closes the connection to the authentication agent and frees any associated | 54 | /* Closes the connection to the authentication agent and frees any associated |
57 | memory. */ | 55 | memory. */ |
58 | void ssh_close_authentication_connection(AuthenticationConnection *ac); | 56 | void ssh_close_authentication_connection(AuthenticationConnection * ac); |
59 | 57 | ||
60 | /* Returns the first authentication identity held by the agent. | 58 | /* Returns the first authentication identity held by the agent. |
61 | Returns true if an identity is available, 0 otherwise. | 59 | Returns true if an identity is available, 0 otherwise. |
62 | The caller must initialize the integers before the call, and free the | 60 | The caller must initialize the integers before the call, and free the |
63 | comment after a successful call (before calling ssh_get_next_identity). */ | 61 | comment after a successful call (before calling ssh_get_next_identity). */ |
64 | int ssh_get_first_identity(AuthenticationConnection *connection, | 62 | int |
65 | BIGNUM *e, BIGNUM *n, char **comment); | 63 | ssh_get_first_identity(AuthenticationConnection * connection, |
64 | BIGNUM * e, BIGNUM * n, char **comment); | ||
66 | 65 | ||
67 | /* Returns the next authentication identity for the agent. Other functions | 66 | /* Returns the next authentication identity for the agent. Other functions |
68 | can be called between this and ssh_get_first_identity or two calls of this | 67 | can be called between this and ssh_get_first_identity or two calls of this |
69 | function. This returns 0 if there are no more identities. The caller | 68 | function. This returns 0 if there are no more identities. The caller |
70 | must free comment after a successful return. */ | 69 | must free comment after a successful return. */ |
71 | int ssh_get_next_identity(AuthenticationConnection *connection, | 70 | int |
72 | BIGNUM *e, BIGNUM *n, char **comment); | 71 | ssh_get_next_identity(AuthenticationConnection * connection, |
72 | BIGNUM * e, BIGNUM * n, char **comment); | ||
73 | 73 | ||
74 | /* Requests the agent to decrypt the given challenge. Returns true if | 74 | /* Requests the agent to decrypt the given challenge. Returns true if |
75 | the agent claims it was able to decrypt it. */ | 75 | the agent claims it was able to decrypt it. */ |
76 | int ssh_decrypt_challenge(AuthenticationConnection *auth, | 76 | int |
77 | BIGNUM *e, BIGNUM *n, BIGNUM *challenge, | 77 | ssh_decrypt_challenge(AuthenticationConnection * auth, |
78 | unsigned char session_id[16], | 78 | BIGNUM * e, BIGNUM * n, BIGNUM * challenge, |
79 | unsigned int response_type, | 79 | unsigned char session_id[16], |
80 | unsigned char response[16]); | 80 | unsigned int response_type, |
81 | unsigned char response[16]); | ||
81 | 82 | ||
82 | /* Adds an identity to the authentication server. This call is not meant to | 83 | /* Adds an identity to the authentication server. This call is not meant to |
83 | be used by normal applications. This returns true if the identity | 84 | be used by normal applications. This returns true if the identity |
84 | was successfully added. */ | 85 | was successfully added. */ |
85 | int ssh_add_identity(AuthenticationConnection *connection, | 86 | int ssh_add_identity(AuthenticationConnection * connection, |
86 | RSA *key, const char *comment); | 87 | RSA * key, const char *comment); |
87 | 88 | ||
88 | /* Removes the identity from the authentication server. This call is | 89 | /* Removes the identity from the authentication server. This call is |
89 | not meant to be used by normal applications. This returns true if the | 90 | not meant to be used by normal applications. This returns true if the |
90 | identity was successfully added. */ | 91 | identity was successfully added. */ |
91 | int ssh_remove_identity(AuthenticationConnection *connection, | 92 | int ssh_remove_identity(AuthenticationConnection * connection, |
92 | RSA *key); | 93 | RSA * key); |
93 | 94 | ||
94 | /* Removes all identities from the authentication agent. This call is not | 95 | /* Removes all identities from the authentication agent. This call is not |
95 | meant to be used by normal applications. This returns true if the | 96 | meant to be used by normal applications. This returns true if the |
96 | operation was successful. */ | 97 | operation was successful. */ |
97 | int ssh_remove_all_identities(AuthenticationConnection *connection); | 98 | int ssh_remove_all_identities(AuthenticationConnection * connection); |
98 | 99 | ||
99 | /* Closes the connection to the authentication agent. */ | 100 | /* Closes the connection to the authentication agent. */ |
100 | void ssh_close_authentication(AuthenticationConnection *connection); | 101 | void ssh_close_authentication(AuthenticationConnection * connection); |
101 | 102 | ||
102 | #endif /* AUTHFD_H */ | 103 | #endif /* AUTHFD_H */ |
diff --git a/authfile.c b/authfile.c index 0e77edf99..35a05d389 100644 --- a/authfile.c +++ b/authfile.c | |||
@@ -1,21 +1,21 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * | |
3 | authfile.c | 3 | * authfile.c |
4 | 4 | * | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
6 | 6 | * | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
8 | All rights reserved | 8 | * All rights reserved |
9 | 9 | * | |
10 | Created: Mon Mar 27 03:52:05 1995 ylo | 10 | * Created: Mon Mar 27 03:52:05 1995 ylo |
11 | 11 | * | |
12 | This file contains functions for reading and writing identity files, and | 12 | * This file contains functions for reading and writing identity files, and |
13 | for reading the passphrase from the user. | 13 | * for reading the passphrase from the user. |
14 | 14 | * | |
15 | */ | 15 | */ |
16 | 16 | ||
17 | #include "includes.h" | 17 | #include "includes.h" |
18 | RCSID("$Id: authfile.c,v 1.3 1999/11/13 02:07:45 damien Exp $"); | 18 | RCSID("$Id: authfile.c,v 1.4 1999/11/24 13:26:22 damien Exp $"); |
19 | 19 | ||
20 | #ifdef HAVE_OPENSSL | 20 | #ifdef HAVE_OPENSSL |
21 | #include <openssl/bn.h> | 21 | #include <openssl/bn.h> |
@@ -42,93 +42,93 @@ int | |||
42 | save_private_key(const char *filename, const char *passphrase, | 42 | save_private_key(const char *filename, const char *passphrase, |
43 | RSA *key, const char *comment) | 43 | RSA *key, const char *comment) |
44 | { | 44 | { |
45 | Buffer buffer, encrypted; | 45 | Buffer buffer, encrypted; |
46 | char buf[100], *cp; | 46 | char buf[100], *cp; |
47 | int f, i; | 47 | int f, i; |
48 | CipherContext cipher; | 48 | CipherContext cipher; |
49 | int cipher_type; | 49 | int cipher_type; |
50 | u_int32_t rand; | 50 | u_int32_t rand; |
51 | 51 | ||
52 | /* If the passphrase is empty, use SSH_CIPHER_NONE to ease converting to | 52 | /* If the passphrase is empty, use SSH_CIPHER_NONE to ease |
53 | another cipher; otherwise use SSH_AUTHFILE_CIPHER. */ | 53 | converting to another cipher; otherwise use |
54 | if (strcmp(passphrase, "") == 0) | 54 | SSH_AUTHFILE_CIPHER. */ |
55 | cipher_type = SSH_CIPHER_NONE; | 55 | if (strcmp(passphrase, "") == 0) |
56 | else | 56 | cipher_type = SSH_CIPHER_NONE; |
57 | cipher_type = SSH_AUTHFILE_CIPHER; | 57 | else |
58 | 58 | cipher_type = SSH_AUTHFILE_CIPHER; | |
59 | /* This buffer is used to built the secret part of the private key. */ | 59 | |
60 | buffer_init(&buffer); | 60 | /* This buffer is used to built the secret part of the private key. */ |
61 | 61 | buffer_init(&buffer); | |
62 | /* Put checkbytes for checking passphrase validity. */ | 62 | |
63 | rand = arc4random(); | 63 | /* Put checkbytes for checking passphrase validity. */ |
64 | buf[0] = rand & 0xff; | 64 | rand = arc4random(); |
65 | buf[1] = (rand >> 8) & 0xff; | 65 | buf[0] = rand & 0xff; |
66 | buf[2] = buf[0]; | 66 | buf[1] = (rand >> 8) & 0xff; |
67 | buf[3] = buf[1]; | 67 | buf[2] = buf[0]; |
68 | buffer_append(&buffer, buf, 4); | 68 | buf[3] = buf[1]; |
69 | 69 | buffer_append(&buffer, buf, 4); | |
70 | /* Store the private key (n and e will not be stored because they will | 70 | |
71 | be stored in plain text, and storing them also in encrypted format | 71 | /* Store the private key (n and e will not be stored because they |
72 | would just give known plaintext). */ | 72 | will be stored in plain text, and storing them also in |
73 | buffer_put_bignum(&buffer, key->d); | 73 | encrypted format would just give known plaintext). */ |
74 | buffer_put_bignum(&buffer, key->iqmp); | 74 | buffer_put_bignum(&buffer, key->d); |
75 | buffer_put_bignum(&buffer, key->q); /* reverse from SSL p */ | 75 | buffer_put_bignum(&buffer, key->iqmp); |
76 | buffer_put_bignum(&buffer, key->p); /* reverse from SSL q */ | 76 | buffer_put_bignum(&buffer, key->q); /* reverse from SSL p */ |
77 | 77 | buffer_put_bignum(&buffer, key->p); /* reverse from SSL q */ | |
78 | /* Pad the part to be encrypted until its size is a multiple of 8. */ | 78 | |
79 | while (buffer_len(&buffer) % 8 != 0) | 79 | /* Pad the part to be encrypted until its size is a multiple of 8. */ |
80 | buffer_put_char(&buffer, 0); | 80 | while (buffer_len(&buffer) % 8 != 0) |
81 | 81 | buffer_put_char(&buffer, 0); | |
82 | /* This buffer will be used to contain the data in the file. */ | 82 | |
83 | buffer_init(&encrypted); | 83 | /* This buffer will be used to contain the data in the file. */ |
84 | 84 | buffer_init(&encrypted); | |
85 | /* First store keyfile id string. */ | 85 | |
86 | cp = AUTHFILE_ID_STRING; | 86 | /* First store keyfile id string. */ |
87 | for (i = 0; cp[i]; i++) | 87 | cp = AUTHFILE_ID_STRING; |
88 | buffer_put_char(&encrypted, cp[i]); | 88 | for (i = 0; cp[i]; i++) |
89 | buffer_put_char(&encrypted, 0); | 89 | buffer_put_char(&encrypted, cp[i]); |
90 | 90 | buffer_put_char(&encrypted, 0); | |
91 | /* Store cipher type. */ | 91 | |
92 | buffer_put_char(&encrypted, cipher_type); | 92 | /* Store cipher type. */ |
93 | buffer_put_int(&encrypted, 0); /* For future extension */ | 93 | buffer_put_char(&encrypted, cipher_type); |
94 | 94 | buffer_put_int(&encrypted, 0); /* For future extension */ | |
95 | /* Store public key. This will be in plain text. */ | 95 | |
96 | buffer_put_int(&encrypted, BN_num_bits(key->n)); | 96 | /* Store public key. This will be in plain text. */ |
97 | buffer_put_bignum(&encrypted, key->n); | 97 | buffer_put_int(&encrypted, BN_num_bits(key->n)); |
98 | buffer_put_bignum(&encrypted, key->e); | 98 | buffer_put_bignum(&encrypted, key->n); |
99 | buffer_put_string(&encrypted, comment, strlen(comment)); | 99 | buffer_put_bignum(&encrypted, key->e); |
100 | 100 | buffer_put_string(&encrypted, comment, strlen(comment)); | |
101 | /* Allocate space for the private part of the key in the buffer. */ | 101 | |
102 | buffer_append_space(&encrypted, &cp, buffer_len(&buffer)); | 102 | /* Allocate space for the private part of the key in the buffer. */ |
103 | 103 | buffer_append_space(&encrypted, &cp, buffer_len(&buffer)); | |
104 | cipher_set_key_string(&cipher, cipher_type, passphrase, 1); | 104 | |
105 | cipher_encrypt(&cipher, (unsigned char *)cp, | 105 | cipher_set_key_string(&cipher, cipher_type, passphrase, 1); |
106 | (unsigned char *)buffer_ptr(&buffer), | 106 | cipher_encrypt(&cipher, (unsigned char *) cp, |
107 | buffer_len(&buffer)); | 107 | (unsigned char *) buffer_ptr(&buffer), |
108 | memset(&cipher, 0, sizeof(cipher)); | 108 | buffer_len(&buffer)); |
109 | 109 | memset(&cipher, 0, sizeof(cipher)); | |
110 | /* Destroy temporary data. */ | 110 | |
111 | memset(buf, 0, sizeof(buf)); | 111 | /* Destroy temporary data. */ |
112 | buffer_free(&buffer); | 112 | memset(buf, 0, sizeof(buf)); |
113 | 113 | buffer_free(&buffer); | |
114 | /* Write to a file. */ | 114 | |
115 | f = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0600); | 115 | /* Write to a file. */ |
116 | if (f < 0) | 116 | f = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600); |
117 | return 0; | 117 | if (f < 0) |
118 | 118 | return 0; | |
119 | if (write(f, buffer_ptr(&encrypted), buffer_len(&encrypted)) != | 119 | |
120 | buffer_len(&encrypted)) | 120 | if (write(f, buffer_ptr(&encrypted), buffer_len(&encrypted)) != |
121 | { | 121 | buffer_len(&encrypted)) { |
122 | debug("Write to key file %.200s failed: %.100s", filename, | 122 | debug("Write to key file %.200s failed: %.100s", filename, |
123 | strerror(errno)); | 123 | strerror(errno)); |
124 | buffer_free(&encrypted); | 124 | buffer_free(&encrypted); |
125 | close(f); | 125 | close(f); |
126 | remove(filename); | 126 | remove(filename); |
127 | return 0; | 127 | return 0; |
128 | } | 128 | } |
129 | close(f); | 129 | close(f); |
130 | buffer_free(&encrypted); | 130 | buffer_free(&encrypted); |
131 | return 1; | 131 | return 1; |
132 | } | 132 | } |
133 | 133 | ||
134 | /* Loads the public part of the key file. Returns 0 if an error | 134 | /* Loads the public part of the key file. Returns 0 if an error |
@@ -136,70 +136,65 @@ save_private_key(const char *filename, const char *passphrase, | |||
136 | non-zero otherwise. */ | 136 | non-zero otherwise. */ |
137 | 137 | ||
138 | int | 138 | int |
139 | load_public_key(const char *filename, RSA *pub, | 139 | load_public_key(const char *filename, RSA * pub, |
140 | char **comment_return) | 140 | char **comment_return) |
141 | { | 141 | { |
142 | int f, i; | 142 | int f, i; |
143 | off_t len; | 143 | off_t len; |
144 | Buffer buffer; | 144 | Buffer buffer; |
145 | char *cp; | 145 | char *cp; |
146 | 146 | ||
147 | /* Read data from the file into the buffer. */ | 147 | /* Read data from the file into the buffer. */ |
148 | f = open(filename, O_RDONLY); | 148 | f = open(filename, O_RDONLY); |
149 | if (f < 0) | 149 | if (f < 0) |
150 | return 0; | 150 | return 0; |
151 | 151 | ||
152 | len = lseek(f, (off_t)0, SEEK_END); | 152 | len = lseek(f, (off_t) 0, SEEK_END); |
153 | lseek(f, (off_t)0, SEEK_SET); | 153 | lseek(f, (off_t) 0, SEEK_SET); |
154 | 154 | ||
155 | buffer_init(&buffer); | 155 | buffer_init(&buffer); |
156 | buffer_append_space(&buffer, &cp, len); | 156 | buffer_append_space(&buffer, &cp, len); |
157 | 157 | ||
158 | if (read(f, cp, (size_t)len) != (size_t)len) | 158 | if (read(f, cp, (size_t) len) != (size_t) len) { |
159 | { | 159 | debug("Read from key file %.200s failed: %.100s", filename, |
160 | debug("Read from key file %.200s failed: %.100s", filename, | 160 | strerror(errno)); |
161 | strerror(errno)); | 161 | buffer_free(&buffer); |
162 | buffer_free(&buffer); | 162 | close(f); |
163 | close(f); | 163 | return 0; |
164 | return 0; | 164 | } |
165 | } | 165 | close(f); |
166 | close(f); | 166 | |
167 | 167 | /* Check that it is at least big enought to contain the ID string. */ | |
168 | /* Check that it is at least big enought to contain the ID string. */ | 168 | if (len < strlen(AUTHFILE_ID_STRING) + 1) { |
169 | if (len < strlen(AUTHFILE_ID_STRING) + 1) | 169 | debug("Bad key file %.200s.", filename); |
170 | { | 170 | buffer_free(&buffer); |
171 | debug("Bad key file %.200s.", filename); | 171 | return 0; |
172 | buffer_free(&buffer); | 172 | } |
173 | return 0; | 173 | /* Make sure it begins with the id string. Consume the id string |
174 | } | 174 | from the buffer. */ |
175 | 175 | for (i = 0; i < (unsigned int) strlen(AUTHFILE_ID_STRING) + 1; i++) | |
176 | /* Make sure it begins with the id string. Consume the id string from | 176 | if (buffer_get_char(&buffer) != (unsigned char) AUTHFILE_ID_STRING[i]) { |
177 | the buffer. */ | 177 | debug("Bad key file %.200s.", filename); |
178 | for (i = 0; i < (unsigned int)strlen(AUTHFILE_ID_STRING) + 1; i++) | 178 | buffer_free(&buffer); |
179 | if (buffer_get_char(&buffer) != (unsigned char)AUTHFILE_ID_STRING[i]) | 179 | return 0; |
180 | { | 180 | } |
181 | debug("Bad key file %.200s.", filename); | 181 | /* Skip cipher type and reserved data. */ |
182 | (void) buffer_get_char(&buffer); /* cipher type */ | ||
183 | (void) buffer_get_int(&buffer); /* reserved */ | ||
184 | |||
185 | /* Read the public key from the buffer. */ | ||
186 | buffer_get_int(&buffer); | ||
187 | pub->n = BN_new(); | ||
188 | buffer_get_bignum(&buffer, pub->n); | ||
189 | pub->e = BN_new(); | ||
190 | buffer_get_bignum(&buffer, pub->e); | ||
191 | if (comment_return) | ||
192 | *comment_return = buffer_get_string(&buffer, NULL); | ||
193 | /* The encrypted private part is not parsed by this function. */ | ||
194 | |||
182 | buffer_free(&buffer); | 195 | buffer_free(&buffer); |
183 | return 0; | 196 | |
184 | } | 197 | return 1; |
185 | |||
186 | /* Skip cipher type and reserved data. */ | ||
187 | (void)buffer_get_char(&buffer); /* cipher type */ | ||
188 | (void)buffer_get_int(&buffer); /* reserved */ | ||
189 | |||
190 | /* Read the public key from the buffer. */ | ||
191 | buffer_get_int(&buffer); | ||
192 | pub->n = BN_new(); | ||
193 | buffer_get_bignum(&buffer, pub->n); | ||
194 | pub->e = BN_new(); | ||
195 | buffer_get_bignum(&buffer, pub->e); | ||
196 | if (comment_return) | ||
197 | *comment_return = buffer_get_string(&buffer, NULL); | ||
198 | /* The encrypted private part is not parsed by this function. */ | ||
199 | |||
200 | buffer_free(&buffer); | ||
201 | |||
202 | return 1; | ||
203 | } | 198 | } |
204 | 199 | ||
205 | /* Loads the private key from the file. Returns 0 if an error is encountered | 200 | /* Loads the private key from the file. Returns 0 if an error is encountered |
@@ -208,149 +203,139 @@ load_public_key(const char *filename, RSA *pub, | |||
208 | 203 | ||
209 | int | 204 | int |
210 | load_private_key(const char *filename, const char *passphrase, | 205 | load_private_key(const char *filename, const char *passphrase, |
211 | RSA *prv, char **comment_return) | 206 | RSA * prv, char **comment_return) |
212 | { | 207 | { |
213 | int f, i, check1, check2, cipher_type; | 208 | int f, i, check1, check2, cipher_type; |
214 | off_t len; | 209 | off_t len; |
215 | Buffer buffer, decrypted; | 210 | Buffer buffer, decrypted; |
216 | char *cp; | 211 | char *cp; |
217 | CipherContext cipher; | 212 | CipherContext cipher; |
218 | BN_CTX *ctx; | 213 | BN_CTX *ctx; |
219 | BIGNUM *aux; | 214 | BIGNUM *aux; |
220 | struct stat st; | 215 | struct stat st; |
221 | 216 | ||
222 | /* Read the file into the buffer. */ | 217 | /* Read the file into the buffer. */ |
223 | f = open(filename, O_RDONLY); | 218 | f = open(filename, O_RDONLY); |
224 | if (f < 0) | 219 | if (f < 0) |
225 | return 0; | 220 | return 0; |
226 | 221 | ||
227 | /* We assume we are called under uid of the owner of the file */ | 222 | /* We assume we are called under uid of the owner of the file */ |
228 | if (fstat(f, &st) < 0 || | 223 | if (fstat(f, &st) < 0 || |
229 | (st.st_uid != 0 && st.st_uid != getuid()) || | 224 | (st.st_uid != 0 && st.st_uid != getuid()) || |
230 | (st.st_mode & 077) != 0) { | 225 | (st.st_mode & 077) != 0) { |
231 | error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); | 226 | error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); |
232 | error("@ WARNING: UNPROTECTED PRIVATE KEY FILE! @"); | 227 | error("@ WARNING: UNPROTECTED PRIVATE KEY FILE! @"); |
233 | error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); | 228 | error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); |
234 | error("Bad ownership or mode(0%3.3o) for '%s'.", | 229 | error("Bad ownership or mode(0%3.3o) for '%s'.", |
235 | st.st_mode & 0777, filename); | 230 | st.st_mode & 0777, filename); |
236 | error("It is recommended that your private key files are NOT accessible by others."); | 231 | error("It is recommended that your private key files are NOT accessible by others."); |
237 | return 0; | 232 | return 0; |
238 | } | 233 | } |
239 | 234 | len = lseek(f, (off_t) 0, SEEK_END); | |
240 | len = lseek(f, (off_t)0, SEEK_END); | 235 | lseek(f, (off_t) 0, SEEK_SET); |
241 | lseek(f, (off_t)0, SEEK_SET); | 236 | |
242 | 237 | buffer_init(&buffer); | |
243 | buffer_init(&buffer); | 238 | buffer_append_space(&buffer, &cp, len); |
244 | buffer_append_space(&buffer, &cp, len); | 239 | |
245 | 240 | if (read(f, cp, (size_t) len) != (size_t) len) { | |
246 | if (read(f, cp, (size_t)len) != (size_t)len) | 241 | debug("Read from key file %.200s failed: %.100s", filename, |
247 | { | 242 | strerror(errno)); |
248 | debug("Read from key file %.200s failed: %.100s", filename, | 243 | buffer_free(&buffer); |
249 | strerror(errno)); | 244 | close(f); |
250 | buffer_free(&buffer); | 245 | return 0; |
251 | close(f); | 246 | } |
252 | return 0; | 247 | close(f); |
253 | } | 248 | |
254 | close(f); | 249 | /* Check that it is at least big enought to contain the ID string. */ |
255 | 250 | if (len < strlen(AUTHFILE_ID_STRING) + 1) { | |
256 | /* Check that it is at least big enought to contain the ID string. */ | 251 | debug("Bad key file %.200s.", filename); |
257 | if (len < strlen(AUTHFILE_ID_STRING) + 1) | 252 | buffer_free(&buffer); |
258 | { | 253 | return 0; |
259 | debug("Bad key file %.200s.", filename); | 254 | } |
260 | buffer_free(&buffer); | 255 | /* Make sure it begins with the id string. Consume the id string |
261 | return 0; | 256 | from the buffer. */ |
262 | } | 257 | for (i = 0; i < (unsigned int) strlen(AUTHFILE_ID_STRING) + 1; i++) |
263 | 258 | if (buffer_get_char(&buffer) != (unsigned char) AUTHFILE_ID_STRING[i]) { | |
264 | /* Make sure it begins with the id string. Consume the id string from | 259 | debug("Bad key file %.200s.", filename); |
265 | the buffer. */ | 260 | buffer_free(&buffer); |
266 | for (i = 0; i < (unsigned int)strlen(AUTHFILE_ID_STRING) + 1; i++) | 261 | return 0; |
267 | if (buffer_get_char(&buffer) != (unsigned char)AUTHFILE_ID_STRING[i]) | 262 | } |
268 | { | 263 | /* Read cipher type. */ |
269 | debug("Bad key file %.200s.", filename); | 264 | cipher_type = buffer_get_char(&buffer); |
265 | (void) buffer_get_int(&buffer); /* Reserved data. */ | ||
266 | |||
267 | /* Read the public key from the buffer. */ | ||
268 | buffer_get_int(&buffer); | ||
269 | prv->n = BN_new(); | ||
270 | buffer_get_bignum(&buffer, prv->n); | ||
271 | prv->e = BN_new(); | ||
272 | buffer_get_bignum(&buffer, prv->e); | ||
273 | if (comment_return) | ||
274 | *comment_return = buffer_get_string(&buffer, NULL); | ||
275 | else | ||
276 | xfree(buffer_get_string(&buffer, NULL)); | ||
277 | |||
278 | /* Check that it is a supported cipher. */ | ||
279 | if (((cipher_mask() | SSH_CIPHER_NONE | SSH_AUTHFILE_CIPHER) & | ||
280 | (1 << cipher_type)) == 0) { | ||
281 | debug("Unsupported cipher %.100s used in key file %.200s.", | ||
282 | cipher_name(cipher_type), filename); | ||
283 | buffer_free(&buffer); | ||
284 | goto fail; | ||
285 | } | ||
286 | /* Initialize space for decrypted data. */ | ||
287 | buffer_init(&decrypted); | ||
288 | buffer_append_space(&decrypted, &cp, buffer_len(&buffer)); | ||
289 | |||
290 | /* Rest of the buffer is encrypted. Decrypt it using the passphrase. */ | ||
291 | cipher_set_key_string(&cipher, cipher_type, passphrase, 0); | ||
292 | cipher_decrypt(&cipher, (unsigned char *) cp, | ||
293 | (unsigned char *) buffer_ptr(&buffer), | ||
294 | buffer_len(&buffer)); | ||
295 | |||
270 | buffer_free(&buffer); | 296 | buffer_free(&buffer); |
271 | return 0; | 297 | |
272 | } | 298 | check1 = buffer_get_char(&decrypted); |
273 | 299 | check2 = buffer_get_char(&decrypted); | |
274 | /* Read cipher type. */ | 300 | if (check1 != buffer_get_char(&decrypted) || |
275 | cipher_type = buffer_get_char(&buffer); | 301 | check2 != buffer_get_char(&decrypted)) { |
276 | (void)buffer_get_int(&buffer); /* Reserved data. */ | 302 | if (strcmp(passphrase, "") != 0) |
277 | 303 | debug("Bad passphrase supplied for key file %.200s.", filename); | |
278 | /* Read the public key from the buffer. */ | 304 | /* Bad passphrase. */ |
279 | buffer_get_int(&buffer); | 305 | buffer_free(&decrypted); |
280 | prv->n = BN_new(); | 306 | fail: |
281 | buffer_get_bignum(&buffer, prv->n); | 307 | BN_clear_free(prv->n); |
282 | prv->e = BN_new(); | 308 | BN_clear_free(prv->e); |
283 | buffer_get_bignum(&buffer, prv->e); | 309 | if (comment_return) |
284 | if (comment_return) | 310 | xfree(*comment_return); |
285 | *comment_return = buffer_get_string(&buffer, NULL); | 311 | return 0; |
286 | else | 312 | } |
287 | xfree(buffer_get_string(&buffer, NULL)); | 313 | /* Read the rest of the private key. */ |
288 | 314 | prv->d = BN_new(); | |
289 | /* Check that it is a supported cipher. */ | 315 | buffer_get_bignum(&decrypted, prv->d); |
290 | if (((cipher_mask() | SSH_CIPHER_NONE | SSH_AUTHFILE_CIPHER) & | 316 | prv->iqmp = BN_new(); |
291 | (1 << cipher_type)) == 0) | 317 | buffer_get_bignum(&decrypted, prv->iqmp); /* u */ |
292 | { | 318 | /* in SSL and SSH p and q are exchanged */ |
293 | debug("Unsupported cipher %.100s used in key file %.200s.", | 319 | prv->q = BN_new(); |
294 | cipher_name(cipher_type), filename); | 320 | buffer_get_bignum(&decrypted, prv->q); /* p */ |
295 | buffer_free(&buffer); | 321 | prv->p = BN_new(); |
296 | goto fail; | 322 | buffer_get_bignum(&decrypted, prv->p); /* q */ |
297 | } | 323 | |
298 | 324 | ctx = BN_CTX_new(); | |
299 | /* Initialize space for decrypted data. */ | 325 | aux = BN_new(); |
300 | buffer_init(&decrypted); | 326 | |
301 | buffer_append_space(&decrypted, &cp, buffer_len(&buffer)); | 327 | BN_sub(aux, prv->q, BN_value_one()); |
302 | 328 | prv->dmq1 = BN_new(); | |
303 | /* Rest of the buffer is encrypted. Decrypt it using the passphrase. */ | 329 | BN_mod(prv->dmq1, prv->d, aux, ctx); |
304 | cipher_set_key_string(&cipher, cipher_type, passphrase, 0); | 330 | |
305 | cipher_decrypt(&cipher, (unsigned char *)cp, | 331 | BN_sub(aux, prv->p, BN_value_one()); |
306 | (unsigned char *)buffer_ptr(&buffer), | 332 | prv->dmp1 = BN_new(); |
307 | buffer_len(&buffer)); | 333 | BN_mod(prv->dmp1, prv->d, aux, ctx); |
308 | 334 | ||
309 | buffer_free(&buffer); | 335 | BN_clear_free(aux); |
310 | 336 | BN_CTX_free(ctx); | |
311 | check1 = buffer_get_char(&decrypted); | 337 | |
312 | check2 = buffer_get_char(&decrypted); | 338 | buffer_free(&decrypted); |
313 | if (check1 != buffer_get_char(&decrypted) || | 339 | |
314 | check2 != buffer_get_char(&decrypted)) | 340 | return 1; |
315 | { | ||
316 | if (strcmp(passphrase, "") != 0) | ||
317 | debug("Bad passphrase supplied for key file %.200s.", filename); | ||
318 | /* Bad passphrase. */ | ||
319 | buffer_free(&decrypted); | ||
320 | fail: | ||
321 | BN_clear_free(prv->n); | ||
322 | BN_clear_free(prv->e); | ||
323 | if (comment_return) | ||
324 | xfree(*comment_return); | ||
325 | return 0; | ||
326 | } | ||
327 | |||
328 | /* Read the rest of the private key. */ | ||
329 | prv->d = BN_new(); | ||
330 | buffer_get_bignum(&decrypted, prv->d); | ||
331 | prv->iqmp = BN_new(); | ||
332 | buffer_get_bignum(&decrypted, prv->iqmp); /* u */ | ||
333 | /* in SSL and SSH p and q are exchanged */ | ||
334 | prv->q = BN_new(); | ||
335 | buffer_get_bignum(&decrypted, prv->q); /* p */ | ||
336 | prv->p = BN_new(); | ||
337 | buffer_get_bignum(&decrypted, prv->p); /* q */ | ||
338 | |||
339 | ctx = BN_CTX_new(); | ||
340 | aux = BN_new(); | ||
341 | |||
342 | BN_sub(aux, prv->q, BN_value_one()); | ||
343 | prv->dmq1 = BN_new(); | ||
344 | BN_mod(prv->dmq1, prv->d, aux, ctx); | ||
345 | |||
346 | BN_sub(aux, prv->p, BN_value_one()); | ||
347 | prv->dmp1 = BN_new(); | ||
348 | BN_mod(prv->dmp1, prv->d, aux, ctx); | ||
349 | |||
350 | BN_clear_free(aux); | ||
351 | BN_CTX_free(ctx); | ||
352 | |||
353 | buffer_free(&decrypted); | ||
354 | |||
355 | return 1; | ||
356 | } | 341 | } |
@@ -1,21 +1,21 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * | |
3 | bufaux.c | 3 | * bufaux.c |
4 | 4 | * | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
6 | 6 | * | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
8 | All rights reserved | 8 | * All rights reserved |
9 | 9 | * | |
10 | Created: Wed Mar 29 02:24:47 1995 ylo | 10 | * Created: Wed Mar 29 02:24:47 1995 ylo |
11 | 11 | * | |
12 | Auxiliary functions for storing and retrieving various data types to/from | 12 | * Auxiliary functions for storing and retrieving various data types to/from |
13 | Buffers. | 13 | * Buffers. |
14 | 14 | * | |
15 | */ | 15 | */ |
16 | 16 | ||
17 | #include "includes.h" | 17 | #include "includes.h" |
18 | RCSID("$Id: bufaux.c,v 1.5 1999/11/13 02:22:46 damien Exp $"); | 18 | RCSID("$Id: bufaux.c,v 1.6 1999/11/24 13:26:22 damien Exp $"); |
19 | 19 | ||
20 | #include "ssh.h" | 20 | #include "ssh.h" |
21 | 21 | ||
@@ -30,122 +30,136 @@ RCSID("$Id: bufaux.c,v 1.5 1999/11/13 02:22:46 damien Exp $"); | |||
30 | #include "xmalloc.h" | 30 | #include "xmalloc.h" |
31 | #include "getput.h" | 31 | #include "getput.h" |
32 | 32 | ||
33 | /* Stores an BIGNUM in the buffer with a 2-byte msb first bit count, followed | 33 | /* |
34 | by (bits+7)/8 bytes of binary data, msb first. */ | 34 | * Stores an BIGNUM in the buffer with a 2-byte msb first bit count, followed |
35 | 35 | * by (bits+7)/8 bytes of binary data, msb first. | |
36 | */ | ||
36 | void | 37 | void |
37 | buffer_put_bignum(Buffer *buffer, BIGNUM *value) | 38 | buffer_put_bignum(Buffer *buffer, BIGNUM *value) |
38 | { | 39 | { |
39 | int bits = BN_num_bits(value); | 40 | int bits = BN_num_bits(value); |
40 | int bin_size = (bits + 7) / 8; | 41 | int bin_size = (bits + 7) / 8; |
41 | char *buf = xmalloc(bin_size); | 42 | char *buf = xmalloc(bin_size); |
42 | int oi; | 43 | int oi; |
43 | char msg[2]; | 44 | char msg[2]; |
44 | 45 | ||
45 | /* Get the value of in binary */ | 46 | /* Get the value of in binary */ |
46 | oi = BN_bn2bin(value, buf); | 47 | oi = BN_bn2bin(value, buf); |
47 | if (oi != bin_size) | 48 | if (oi != bin_size) |
48 | fatal("buffer_put_bignum: BN_bn2bin() failed: oi %d != bin_size %d", | 49 | fatal("buffer_put_bignum: BN_bn2bin() failed: oi %d != bin_size %d", |
49 | oi, bin_size); | 50 | oi, bin_size); |
50 | 51 | ||
51 | /* Store the number of bits in the buffer in two bytes, msb first. */ | 52 | /* Store the number of bits in the buffer in two bytes, msb first. */ |
52 | PUT_16BIT(msg, bits); | 53 | PUT_16BIT(msg, bits); |
53 | buffer_append(buffer, msg, 2); | 54 | buffer_append(buffer, msg, 2); |
54 | /* Store the binary data. */ | 55 | /* Store the binary data. */ |
55 | buffer_append(buffer, buf, oi); | 56 | buffer_append(buffer, buf, oi); |
56 | /* Clear the temporary data. */ | 57 | /* Clear the temporary data. */ |
57 | memset(buf, 0, bin_size); | 58 | memset(buf, 0, bin_size); |
58 | xfree(buf); | 59 | xfree(buf); |
59 | } | 60 | } |
60 | 61 | ||
61 | /* Retrieves an BIGNUM from the buffer. */ | 62 | /* |
62 | 63 | * Retrieves an BIGNUM from the buffer. | |
64 | */ | ||
63 | int | 65 | int |
64 | buffer_get_bignum(Buffer *buffer, BIGNUM *value) | 66 | buffer_get_bignum(Buffer *buffer, BIGNUM *value) |
65 | { | 67 | { |
66 | int bits, bytes; | 68 | int bits, bytes; |
67 | unsigned char buf[2], *bin; | 69 | unsigned char buf[2], *bin; |
68 | 70 | ||
69 | /* Get the number for bits. */ | 71 | /* Get the number for bits. */ |
70 | buffer_get(buffer, (char *)buf, 2); | 72 | buffer_get(buffer, (char *) buf, 2); |
71 | bits = GET_16BIT(buf); | 73 | bits = GET_16BIT(buf); |
72 | /* Compute the number of binary bytes that follow. */ | 74 | /* Compute the number of binary bytes that follow. */ |
73 | bytes = (bits + 7) / 8; | 75 | bytes = (bits + 7) / 8; |
74 | if (buffer_len(buffer) < bytes) | 76 | if (buffer_len(buffer) < bytes) |
75 | fatal("buffer_get_bignum: input buffer too small"); | 77 | fatal("buffer_get_bignum: input buffer too small"); |
76 | bin = buffer_ptr(buffer); | 78 | bin = buffer_ptr(buffer); |
77 | BN_bin2bn(bin, bytes, value); | 79 | BN_bin2bn(bin, bytes, value); |
78 | buffer_consume(buffer, bytes); | 80 | buffer_consume(buffer, bytes); |
79 | 81 | ||
80 | return 2 + bytes; | 82 | return 2 + bytes; |
81 | } | 83 | } |
82 | 84 | ||
83 | /* Returns an integer from the buffer (4 bytes, msb first). */ | 85 | /* |
84 | 86 | * Returns an integer from the buffer (4 bytes, msb first). | |
85 | unsigned int buffer_get_int(Buffer *buffer) | 87 | */ |
88 | unsigned int | ||
89 | buffer_get_int(Buffer *buffer) | ||
86 | { | 90 | { |
87 | unsigned char buf[4]; | 91 | unsigned char buf[4]; |
88 | buffer_get(buffer, (char *)buf, 4); | 92 | buffer_get(buffer, (char *) buf, 4); |
89 | return GET_32BIT(buf); | 93 | return GET_32BIT(buf); |
90 | } | 94 | } |
91 | 95 | ||
92 | /* Stores an integer in the buffer in 4 bytes, msb first. */ | 96 | /* |
93 | 97 | * Stores an integer in the buffer in 4 bytes, msb first. | |
94 | void buffer_put_int(Buffer *buffer, unsigned int value) | 98 | */ |
99 | void | ||
100 | buffer_put_int(Buffer *buffer, unsigned int value) | ||
95 | { | 101 | { |
96 | char buf[4]; | 102 | char buf[4]; |
97 | PUT_32BIT(buf, value); | 103 | PUT_32BIT(buf, value); |
98 | buffer_append(buffer, buf, 4); | 104 | buffer_append(buffer, buf, 4); |
99 | } | 105 | } |
100 | 106 | ||
101 | /* Returns an arbitrary binary string from the buffer. The string cannot | 107 | /* |
102 | be longer than 256k. The returned value points to memory allocated | 108 | * Returns an arbitrary binary string from the buffer. The string cannot |
103 | with xmalloc; it is the responsibility of the calling function to free | 109 | * be longer than 256k. The returned value points to memory allocated |
104 | the data. If length_ptr is non-NULL, the length of the returned data | 110 | * with xmalloc; it is the responsibility of the calling function to free |
105 | will be stored there. A null character will be automatically appended | 111 | * the data. If length_ptr is non-NULL, the length of the returned data |
106 | to the returned string, and is not counted in length. */ | 112 | * will be stored there. A null character will be automatically appended |
107 | 113 | * to the returned string, and is not counted in length. | |
108 | char *buffer_get_string(Buffer *buffer, unsigned int *length_ptr) | 114 | */ |
115 | char * | ||
116 | buffer_get_string(Buffer *buffer, unsigned int *length_ptr) | ||
109 | { | 117 | { |
110 | unsigned int len; | 118 | unsigned int len; |
111 | char *value; | 119 | char *value; |
112 | /* Get the length. */ | 120 | /* Get the length. */ |
113 | len = buffer_get_int(buffer); | 121 | len = buffer_get_int(buffer); |
114 | if (len > 256*1024) | 122 | if (len > 256 * 1024) |
115 | fatal("Received packet with bad string length %d", len); | 123 | fatal("Received packet with bad string length %d", len); |
116 | /* Allocate space for the string. Add one byte for a null character. */ | 124 | /* Allocate space for the string. Add one byte for a null character. */ |
117 | value = xmalloc(len + 1); | 125 | value = xmalloc(len + 1); |
118 | /* Get the string. */ | 126 | /* Get the string. */ |
119 | buffer_get(buffer, value, len); | 127 | buffer_get(buffer, value, len); |
120 | /* Append a null character to make processing easier. */ | 128 | /* Append a null character to make processing easier. */ |
121 | value[len] = 0; | 129 | value[len] = 0; |
122 | /* Optionally return the length of the string. */ | 130 | /* Optionally return the length of the string. */ |
123 | if (length_ptr) | 131 | if (length_ptr) |
124 | *length_ptr = len; | 132 | *length_ptr = len; |
125 | return value; | 133 | return value; |
126 | } | 134 | } |
127 | 135 | ||
128 | /* Stores and arbitrary binary string in the buffer. */ | 136 | /* |
129 | 137 | * Stores and arbitrary binary string in the buffer. | |
130 | void buffer_put_string(Buffer *buffer, const void *buf, unsigned int len) | 138 | */ |
139 | void | ||
140 | buffer_put_string(Buffer *buffer, const void *buf, unsigned int len) | ||
131 | { | 141 | { |
132 | buffer_put_int(buffer, len); | 142 | buffer_put_int(buffer, len); |
133 | buffer_append(buffer, buf, len); | 143 | buffer_append(buffer, buf, len); |
134 | } | 144 | } |
135 | 145 | ||
136 | /* Returns a character from the buffer (0 - 255). */ | 146 | /* |
137 | 147 | * Returns a character from the buffer (0 - 255). | |
138 | int buffer_get_char(Buffer *buffer) | 148 | */ |
149 | int | ||
150 | buffer_get_char(Buffer *buffer) | ||
139 | { | 151 | { |
140 | char ch; | 152 | char ch; |
141 | buffer_get(buffer, &ch, 1); | 153 | buffer_get(buffer, &ch, 1); |
142 | return (unsigned char)ch; | 154 | return (unsigned char) ch; |
143 | } | 155 | } |
144 | 156 | ||
145 | /* Stores a character in the buffer. */ | 157 | /* |
146 | 158 | * Stores a character in the buffer. | |
147 | void buffer_put_char(Buffer *buffer, int value) | 159 | */ |
160 | void | ||
161 | buffer_put_char(Buffer *buffer, int value) | ||
148 | { | 162 | { |
149 | char ch = value; | 163 | char ch = value; |
150 | buffer_append(buffer, &ch, 1); | 164 | buffer_append(buffer, &ch, 1); |
151 | } | 165 | } |
@@ -1,17 +1,17 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * | |
3 | bufaux.h | 3 | * bufaux.h |
4 | 4 | * | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
6 | 6 | * | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
8 | All rights reserved | 8 | * All rights reserved |
9 | 9 | * | |
10 | Created: Wed Mar 29 02:18:23 1995 ylo | 10 | * Created: Wed Mar 29 02:18:23 1995 ylo |
11 | 11 | * | |
12 | */ | 12 | */ |
13 | 13 | ||
14 | /* RCSID("$Id: bufaux.h,v 1.1 1999/10/27 03:42:43 damien Exp $"); */ | 14 | /* RCSID("$Id: bufaux.h,v 1.2 1999/11/24 13:26:22 damien Exp $"); */ |
15 | 15 | ||
16 | #ifndef BUFAUX_H | 16 | #ifndef BUFAUX_H |
17 | #define BUFAUX_H | 17 | #define BUFAUX_H |
@@ -20,22 +20,22 @@ Created: Wed Mar 29 02:18:23 1995 ylo | |||
20 | 20 | ||
21 | /* Stores an BIGNUM in the buffer with a 2-byte msb first bit count, followed | 21 | /* Stores an BIGNUM in the buffer with a 2-byte msb first bit count, followed |
22 | by (bits+7)/8 bytes of binary data, msb first. */ | 22 | by (bits+7)/8 bytes of binary data, msb first. */ |
23 | void buffer_put_bignum(Buffer *buffer, BIGNUM *value); | 23 | void buffer_put_bignum(Buffer * buffer, BIGNUM * value); |
24 | 24 | ||
25 | /* Retrieves an BIGNUM from the buffer. */ | 25 | /* Retrieves an BIGNUM from the buffer. */ |
26 | int buffer_get_bignum(Buffer *buffer, BIGNUM *value); | 26 | int buffer_get_bignum(Buffer * buffer, BIGNUM * value); |
27 | 27 | ||
28 | /* Returns an integer from the buffer (4 bytes, msb first). */ | 28 | /* Returns an integer from the buffer (4 bytes, msb first). */ |
29 | unsigned int buffer_get_int(Buffer *buffer); | 29 | unsigned int buffer_get_int(Buffer * buffer); |
30 | 30 | ||
31 | /* Stores an integer in the buffer in 4 bytes, msb first. */ | 31 | /* Stores an integer in the buffer in 4 bytes, msb first. */ |
32 | void buffer_put_int(Buffer *buffer, unsigned int value); | 32 | void buffer_put_int(Buffer * buffer, unsigned int value); |
33 | 33 | ||
34 | /* Returns a character from the buffer (0 - 255). */ | 34 | /* Returns a character from the buffer (0 - 255). */ |
35 | int buffer_get_char(Buffer *buffer); | 35 | int buffer_get_char(Buffer * buffer); |
36 | 36 | ||
37 | /* Stores a character in the buffer. */ | 37 | /* Stores a character in the buffer. */ |
38 | void buffer_put_char(Buffer *buffer, int value); | 38 | void buffer_put_char(Buffer * buffer, int value); |
39 | 39 | ||
40 | /* Returns an arbitrary binary string from the buffer. The string cannot | 40 | /* Returns an arbitrary binary string from the buffer. The string cannot |
41 | be longer than 256k. The returned value points to memory allocated | 41 | be longer than 256k. The returned value points to memory allocated |
@@ -43,9 +43,9 @@ void buffer_put_char(Buffer *buffer, int value); | |||
43 | the data. If length_ptr is non-NULL, the length of the returned data | 43 | the data. If length_ptr is non-NULL, the length of the returned data |
44 | will be stored there. A null character will be automatically appended | 44 | will be stored there. A null character will be automatically appended |
45 | to the returned string, and is not counted in length. */ | 45 | to the returned string, and is not counted in length. */ |
46 | char *buffer_get_string(Buffer *buffer, unsigned int *length_ptr); | 46 | char *buffer_get_string(Buffer * buffer, unsigned int *length_ptr); |
47 | 47 | ||
48 | /* Stores and arbitrary binary string in the buffer. */ | 48 | /* Stores and arbitrary binary string in the buffer. */ |
49 | void buffer_put_string(Buffer *buffer, const void *buf, unsigned int len); | 49 | void buffer_put_string(Buffer * buffer, const void *buf, unsigned int len); |
50 | 50 | ||
51 | #endif /* BUFAUX_H */ | 51 | #endif /* BUFAUX_H */ |
@@ -1,20 +1,20 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * | |
3 | buffer.c | 3 | * buffer.c |
4 | 4 | * | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
6 | 6 | * | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
8 | All rights reserved | 8 | * All rights reserved |
9 | 9 | * | |
10 | Created: Sat Mar 18 04:15:33 1995 ylo | 10 | * Created: Sat Mar 18 04:15:33 1995 ylo |
11 | 11 | * | |
12 | Functions for manipulating fifo buffers (that can grow if needed). | 12 | * Functions for manipulating fifo buffers (that can grow if needed). |
13 | 13 | * | |
14 | */ | 14 | */ |
15 | 15 | ||
16 | #include "includes.h" | 16 | #include "includes.h" |
17 | RCSID("$Id: buffer.c,v 1.1 1999/10/27 03:42:43 damien Exp $"); | 17 | RCSID("$Id: buffer.c,v 1.2 1999/11/24 13:26:22 damien Exp $"); |
18 | 18 | ||
19 | #include "xmalloc.h" | 19 | #include "xmalloc.h" |
20 | #include "buffer.h" | 20 | #include "buffer.h" |
@@ -22,129 +22,134 @@ RCSID("$Id: buffer.c,v 1.1 1999/10/27 03:42:43 damien Exp $"); | |||
22 | 22 | ||
23 | /* Initializes the buffer structure. */ | 23 | /* Initializes the buffer structure. */ |
24 | 24 | ||
25 | void buffer_init(Buffer *buffer) | 25 | void |
26 | buffer_init(Buffer *buffer) | ||
26 | { | 27 | { |
27 | buffer->alloc = 4096; | 28 | buffer->alloc = 4096; |
28 | buffer->buf = xmalloc(buffer->alloc); | 29 | buffer->buf = xmalloc(buffer->alloc); |
29 | buffer->offset = 0; | 30 | buffer->offset = 0; |
30 | buffer->end = 0; | 31 | buffer->end = 0; |
31 | } | 32 | } |
32 | 33 | ||
33 | /* Frees any memory used for the buffer. */ | 34 | /* Frees any memory used for the buffer. */ |
34 | 35 | ||
35 | void buffer_free(Buffer *buffer) | 36 | void |
37 | buffer_free(Buffer *buffer) | ||
36 | { | 38 | { |
37 | memset(buffer->buf, 0, buffer->alloc); | 39 | memset(buffer->buf, 0, buffer->alloc); |
38 | xfree(buffer->buf); | 40 | xfree(buffer->buf); |
39 | } | 41 | } |
40 | 42 | ||
41 | /* Clears any data from the buffer, making it empty. This does not actually | 43 | /* Clears any data from the buffer, making it empty. This does not actually |
42 | zero the memory. */ | 44 | zero the memory. */ |
43 | 45 | ||
44 | void buffer_clear(Buffer *buffer) | 46 | void |
47 | buffer_clear(Buffer *buffer) | ||
45 | { | 48 | { |
46 | buffer->offset = 0; | 49 | buffer->offset = 0; |
47 | buffer->end = 0; | 50 | buffer->end = 0; |
48 | } | 51 | } |
49 | 52 | ||
50 | /* Appends data to the buffer, expanding it if necessary. */ | 53 | /* Appends data to the buffer, expanding it if necessary. */ |
51 | 54 | ||
52 | void buffer_append(Buffer *buffer, const char *data, unsigned int len) | 55 | void |
56 | buffer_append(Buffer *buffer, const char *data, unsigned int len) | ||
53 | { | 57 | { |
54 | char *cp; | 58 | char *cp; |
55 | buffer_append_space(buffer, &cp, len); | 59 | buffer_append_space(buffer, &cp, len); |
56 | memcpy(cp, data, len); | 60 | memcpy(cp, data, len); |
57 | } | 61 | } |
58 | 62 | ||
59 | /* Appends space to the buffer, expanding the buffer if necessary. | 63 | /* Appends space to the buffer, expanding the buffer if necessary. |
60 | This does not actually copy the data into the buffer, but instead | 64 | This does not actually copy the data into the buffer, but instead |
61 | returns a pointer to the allocated region. */ | 65 | returns a pointer to the allocated region. */ |
62 | 66 | ||
63 | void buffer_append_space(Buffer *buffer, char **datap, unsigned int len) | 67 | void |
68 | buffer_append_space(Buffer *buffer, char **datap, unsigned int len) | ||
64 | { | 69 | { |
65 | /* If the buffer is empty, start using it from the beginning. */ | 70 | /* If the buffer is empty, start using it from the beginning. */ |
66 | if (buffer->offset == buffer->end) | 71 | if (buffer->offset == buffer->end) { |
67 | { | 72 | buffer->offset = 0; |
68 | buffer->offset = 0; | 73 | buffer->end = 0; |
69 | buffer->end = 0; | 74 | } |
70 | } | 75 | restart: |
71 | 76 | /* If there is enough space to store all data, store it now. */ | |
72 | restart: | 77 | if (buffer->end + len < buffer->alloc) { |
73 | /* If there is enough space to store all data, store it now. */ | 78 | *datap = buffer->buf + buffer->end; |
74 | if (buffer->end + len < buffer->alloc) | 79 | buffer->end += len; |
75 | { | 80 | return; |
76 | *datap = buffer->buf + buffer->end; | 81 | } |
77 | buffer->end += len; | 82 | /* If the buffer is quite empty, but all data is at the end, move |
78 | return; | 83 | the data to the beginning and retry. */ |
79 | } | 84 | if (buffer->offset > buffer->alloc / 2) { |
80 | 85 | memmove(buffer->buf, buffer->buf + buffer->offset, | |
81 | /* If the buffer is quite empty, but all data is at the end, move the | 86 | buffer->end - buffer->offset); |
82 | data to the beginning and retry. */ | 87 | buffer->end -= buffer->offset; |
83 | if (buffer->offset > buffer->alloc / 2) | 88 | buffer->offset = 0; |
84 | { | 89 | goto restart; |
85 | memmove(buffer->buf, buffer->buf + buffer->offset, | 90 | } |
86 | buffer->end - buffer->offset); | 91 | /* Increase the size of the buffer and retry. */ |
87 | buffer->end -= buffer->offset; | 92 | buffer->alloc += len + 32768; |
88 | buffer->offset = 0; | 93 | buffer->buf = xrealloc(buffer->buf, buffer->alloc); |
89 | goto restart; | 94 | goto restart; |
90 | } | ||
91 | |||
92 | /* Increase the size of the buffer and retry. */ | ||
93 | buffer->alloc += len + 32768; | ||
94 | buffer->buf = xrealloc(buffer->buf, buffer->alloc); | ||
95 | goto restart; | ||
96 | } | 95 | } |
97 | 96 | ||
98 | /* Returns the number of bytes of data in the buffer. */ | 97 | /* Returns the number of bytes of data in the buffer. */ |
99 | 98 | ||
100 | unsigned int buffer_len(Buffer *buffer) | 99 | unsigned int |
100 | buffer_len(Buffer *buffer) | ||
101 | { | 101 | { |
102 | return buffer->end - buffer->offset; | 102 | return buffer->end - buffer->offset; |
103 | } | 103 | } |
104 | 104 | ||
105 | /* Gets data from the beginning of the buffer. */ | 105 | /* Gets data from the beginning of the buffer. */ |
106 | 106 | ||
107 | void buffer_get(Buffer *buffer, char *buf, unsigned int len) | 107 | void |
108 | buffer_get(Buffer *buffer, char *buf, unsigned int len) | ||
108 | { | 109 | { |
109 | if (len > buffer->end - buffer->offset) | 110 | if (len > buffer->end - buffer->offset) |
110 | fatal("buffer_get trying to get more bytes than in buffer"); | 111 | fatal("buffer_get trying to get more bytes than in buffer"); |
111 | memcpy(buf, buffer->buf + buffer->offset, len); | 112 | memcpy(buf, buffer->buf + buffer->offset, len); |
112 | buffer->offset += len; | 113 | buffer->offset += len; |
113 | } | 114 | } |
114 | 115 | ||
115 | /* Consumes the given number of bytes from the beginning of the buffer. */ | 116 | /* Consumes the given number of bytes from the beginning of the buffer. */ |
116 | 117 | ||
117 | void buffer_consume(Buffer *buffer, unsigned int bytes) | 118 | void |
119 | buffer_consume(Buffer *buffer, unsigned int bytes) | ||
118 | { | 120 | { |
119 | if (bytes > buffer->end - buffer->offset) | 121 | if (bytes > buffer->end - buffer->offset) |
120 | fatal("buffer_get trying to get more bytes than in buffer"); | 122 | fatal("buffer_get trying to get more bytes than in buffer"); |
121 | buffer->offset += bytes; | 123 | buffer->offset += bytes; |
122 | } | 124 | } |
123 | 125 | ||
124 | /* Consumes the given number of bytes from the end of the buffer. */ | 126 | /* Consumes the given number of bytes from the end of the buffer. */ |
125 | 127 | ||
126 | void buffer_consume_end(Buffer *buffer, unsigned int bytes) | 128 | void |
129 | buffer_consume_end(Buffer *buffer, unsigned int bytes) | ||
127 | { | 130 | { |
128 | if (bytes > buffer->end - buffer->offset) | 131 | if (bytes > buffer->end - buffer->offset) |
129 | fatal("buffer_get trying to get more bytes than in buffer"); | 132 | fatal("buffer_get trying to get more bytes than in buffer"); |
130 | buffer->end -= bytes; | 133 | buffer->end -= bytes; |
131 | } | 134 | } |
132 | 135 | ||
133 | /* Returns a pointer to the first used byte in the buffer. */ | 136 | /* Returns a pointer to the first used byte in the buffer. */ |
134 | 137 | ||
135 | char *buffer_ptr(Buffer *buffer) | 138 | char * |
139 | buffer_ptr(Buffer *buffer) | ||
136 | { | 140 | { |
137 | return buffer->buf + buffer->offset; | 141 | return buffer->buf + buffer->offset; |
138 | } | 142 | } |
139 | 143 | ||
140 | /* Dumps the contents of the buffer to stderr. */ | 144 | /* Dumps the contents of the buffer to stderr. */ |
141 | 145 | ||
142 | void buffer_dump(Buffer *buffer) | 146 | void |
147 | buffer_dump(Buffer *buffer) | ||
143 | { | 148 | { |
144 | int i; | 149 | int i; |
145 | unsigned char *ucp = (unsigned char *)buffer->buf; | 150 | unsigned char *ucp = (unsigned char *) buffer->buf; |
146 | 151 | ||
147 | for (i = buffer->offset; i < buffer->end; i++) | 152 | for (i = buffer->offset; i < buffer->end; i++) |
148 | fprintf(stderr, " %02x", ucp[i]); | 153 | fprintf(stderr, " %02x", ucp[i]); |
149 | fprintf(stderr, "\n"); | 154 | fprintf(stderr, "\n"); |
150 | } | 155 | } |
@@ -1,66 +1,64 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * | |
3 | buffer.h | 3 | * buffer.h |
4 | 4 | * | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
6 | 6 | * | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
8 | All rights reserved | 8 | * All rights reserved |
9 | 9 | * | |
10 | Created: Sat Mar 18 04:12:25 1995 ylo | 10 | * Created: Sat Mar 18 04:12:25 1995 ylo |
11 | 11 | * | |
12 | Code for manipulating FIFO buffers. | 12 | * Code for manipulating FIFO buffers. |
13 | 13 | * | |
14 | */ | 14 | */ |
15 | 15 | ||
16 | /* RCSID("$Id: buffer.h,v 1.1 1999/10/27 03:42:43 damien Exp $"); */ | 16 | /* RCSID("$Id: buffer.h,v 1.2 1999/11/24 13:26:22 damien Exp $"); */ |
17 | 17 | ||
18 | #ifndef BUFFER_H | 18 | #ifndef BUFFER_H |
19 | #define BUFFER_H | 19 | #define BUFFER_H |
20 | 20 | ||
21 | typedef struct | 21 | typedef struct { |
22 | { | 22 | char *buf; /* Buffer for data. */ |
23 | char *buf; /* Buffer for data. */ | 23 | unsigned int alloc; /* Number of bytes allocated for data. */ |
24 | unsigned int alloc; /* Number of bytes allocated for data. */ | 24 | unsigned int offset; /* Offset of first byte containing data. */ |
25 | unsigned int offset; /* Offset of first byte containing data. */ | 25 | unsigned int end; /* Offset of last byte containing data. */ |
26 | unsigned int end; /* Offset of last byte containing data. */ | 26 | } Buffer; |
27 | } Buffer; | ||
28 | |||
29 | /* Initializes the buffer structure. */ | 27 | /* Initializes the buffer structure. */ |
30 | void buffer_init(Buffer *buffer); | 28 | void buffer_init(Buffer * buffer); |
31 | 29 | ||
32 | /* Frees any memory used for the buffer. */ | 30 | /* Frees any memory used for the buffer. */ |
33 | void buffer_free(Buffer *buffer); | 31 | void buffer_free(Buffer * buffer); |
34 | 32 | ||
35 | /* Clears any data from the buffer, making it empty. This does not actually | 33 | /* Clears any data from the buffer, making it empty. This does not actually |
36 | zero the memory. */ | 34 | zero the memory. */ |
37 | void buffer_clear(Buffer *buffer); | 35 | void buffer_clear(Buffer * buffer); |
38 | 36 | ||
39 | /* Appends data to the buffer, expanding it if necessary. */ | 37 | /* Appends data to the buffer, expanding it if necessary. */ |
40 | void buffer_append(Buffer *buffer, const char *data, unsigned int len); | 38 | void buffer_append(Buffer * buffer, const char *data, unsigned int len); |
41 | 39 | ||
42 | /* Appends space to the buffer, expanding the buffer if necessary. | 40 | /* Appends space to the buffer, expanding the buffer if necessary. |
43 | This does not actually copy the data into the buffer, but instead | 41 | This does not actually copy the data into the buffer, but instead |
44 | returns a pointer to the allocated region. */ | 42 | returns a pointer to the allocated region. */ |
45 | void buffer_append_space(Buffer *buffer, char **datap, unsigned int len); | 43 | void buffer_append_space(Buffer * buffer, char **datap, unsigned int len); |
46 | 44 | ||
47 | /* Returns the number of bytes of data in the buffer. */ | 45 | /* Returns the number of bytes of data in the buffer. */ |
48 | unsigned int buffer_len(Buffer *buffer); | 46 | unsigned int buffer_len(Buffer * buffer); |
49 | 47 | ||
50 | /* Gets data from the beginning of the buffer. */ | 48 | /* Gets data from the beginning of the buffer. */ |
51 | void buffer_get(Buffer *buffer, char *buf, unsigned int len); | 49 | void buffer_get(Buffer * buffer, char *buf, unsigned int len); |
52 | 50 | ||
53 | /* Consumes the given number of bytes from the beginning of the buffer. */ | 51 | /* Consumes the given number of bytes from the beginning of the buffer. */ |
54 | void buffer_consume(Buffer *buffer, unsigned int bytes); | 52 | void buffer_consume(Buffer * buffer, unsigned int bytes); |
55 | 53 | ||
56 | /* Consumes the given number of bytes from the end of the buffer. */ | 54 | /* Consumes the given number of bytes from the end of the buffer. */ |
57 | void buffer_consume_end(Buffer *buffer, unsigned int bytes); | 55 | void buffer_consume_end(Buffer * buffer, unsigned int bytes); |
58 | 56 | ||
59 | /* Returns a pointer to the first used byte in the buffer. */ | 57 | /* Returns a pointer to the first used byte in the buffer. */ |
60 | char *buffer_ptr(Buffer *buffer); | 58 | char *buffer_ptr(Buffer * buffer); |
61 | 59 | ||
62 | /* Dumps the contents of the buffer to stderr in hex. This intended for | 60 | /* Dumps the contents of the buffer to stderr in hex. This intended for |
63 | debugging purposes only. */ | 61 | debugging purposes only. */ |
64 | void buffer_dump(Buffer *buffer); | 62 | void buffer_dump(Buffer * buffer); |
65 | 63 | ||
66 | #endif /* BUFFER_H */ | 64 | #endif /* BUFFER_H */ |
diff --git a/canohost.c b/canohost.c index 08f798758..3179ddc25 100644 --- a/canohost.c +++ b/canohost.c | |||
@@ -1,233 +1,228 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * | |
3 | canohost.c | 3 | * canohost.c |
4 | 4 | * | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
6 | 6 | * | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
8 | All rights reserved | 8 | * All rights reserved |
9 | 9 | * | |
10 | Created: Sun Jul 2 17:52:22 1995 ylo | 10 | * Created: Sun Jul 2 17:52:22 1995 ylo |
11 | 11 | * | |
12 | Functions for returning the canonical host name of the remote site. | 12 | * Functions for returning the canonical host name of the remote site. |
13 | 13 | * | |
14 | */ | 14 | */ |
15 | 15 | ||
16 | #include "includes.h" | 16 | #include "includes.h" |
17 | RCSID("$Id: canohost.c,v 1.2 1999/11/15 04:25:10 damien Exp $"); | 17 | RCSID("$Id: canohost.c,v 1.3 1999/11/24 13:26:22 damien Exp $"); |
18 | 18 | ||
19 | #include "packet.h" | 19 | #include "packet.h" |
20 | #include "xmalloc.h" | 20 | #include "xmalloc.h" |
21 | #include "ssh.h" | 21 | #include "ssh.h" |
22 | 22 | ||
23 | /* Return the canonical name of the host at the other end of the socket. | 23 | /* Return the canonical name of the host at the other end of the socket. |
24 | The caller should free the returned string with xfree. */ | 24 | The caller should free the returned string with xfree. */ |
25 | 25 | ||
26 | char *get_remote_hostname(int socket) | 26 | char * |
27 | get_remote_hostname(int socket) | ||
27 | { | 28 | { |
28 | struct sockaddr_in from; | 29 | struct sockaddr_in from; |
29 | int fromlen, i; | 30 | int fromlen, i; |
30 | struct hostent *hp; | 31 | struct hostent *hp; |
31 | char name[MAXHOSTNAMELEN]; | 32 | char name[MAXHOSTNAMELEN]; |
32 | 33 | ||
33 | /* Get IP address of client. */ | 34 | /* Get IP address of client. */ |
34 | fromlen = sizeof(from); | 35 | fromlen = sizeof(from); |
35 | memset(&from, 0, sizeof(from)); | 36 | memset(&from, 0, sizeof(from)); |
36 | if (getpeername(socket, (struct sockaddr *)&from, &fromlen) < 0) | 37 | if (getpeername(socket, (struct sockaddr *) & from, &fromlen) < 0) { |
37 | { | 38 | debug("getpeername failed: %.100s", strerror(errno)); |
38 | debug("getpeername failed: %.100s", strerror(errno)); | 39 | fatal_cleanup(); |
39 | fatal_cleanup(); | 40 | } |
40 | } | 41 | /* Map the IP address to a host name. */ |
41 | 42 | hp = gethostbyaddr((char *) &from.sin_addr, sizeof(struct in_addr), | |
42 | /* Map the IP address to a host name. */ | 43 | from.sin_family); |
43 | hp = gethostbyaddr((char *)&from.sin_addr, sizeof(struct in_addr), | 44 | if (hp) { |
44 | from.sin_family); | 45 | /* Got host name, find canonic host name. */ |
45 | if (hp) | 46 | if (strchr(hp->h_name, '.') != 0) |
46 | { | 47 | strlcpy(name, hp->h_name, sizeof(name)); |
47 | /* Got host name, find canonic host name. */ | 48 | else if (hp->h_aliases != 0 |
48 | if (strchr(hp->h_name, '.') != 0) | 49 | && hp->h_aliases[0] != 0 |
49 | strlcpy(name, hp->h_name, sizeof(name)); | 50 | && strchr(hp->h_aliases[0], '.') != 0) |
50 | else if (hp->h_aliases != 0 | 51 | strlcpy(name, hp->h_aliases[0], sizeof(name)); |
51 | && hp->h_aliases[0] != 0 | 52 | else |
52 | && strchr(hp->h_aliases[0], '.') != 0) | 53 | strlcpy(name, hp->h_name, sizeof(name)); |
53 | strlcpy(name, hp->h_aliases[0], sizeof(name)); | 54 | |
54 | else | 55 | /* Convert it to all lowercase (which is expected by the |
55 | strlcpy(name, hp->h_name, sizeof(name)); | 56 | rest of this software). */ |
56 | 57 | for (i = 0; name[i]; i++) | |
57 | /* Convert it to all lowercase (which is expected by the rest of this | 58 | if (isupper(name[i])) |
58 | software). */ | 59 | name[i] = tolower(name[i]); |
59 | for (i = 0; name[i]; i++) | 60 | |
60 | if (isupper(name[i])) | 61 | /* Map it back to an IP address and check that the given |
61 | name[i] = tolower(name[i]); | 62 | address actually is an address of this host. This is |
62 | 63 | necessary because anyone with access to a name server | |
63 | /* Map it back to an IP address and check that the given address actually | 64 | can define arbitrary names for an IP address. Mapping |
64 | is an address of this host. This is necessary because anyone with | 65 | from name to IP address can be trusted better (but can |
65 | access to a name server can define arbitrary names for an IP address. | 66 | still be fooled if the intruder has access to the name |
66 | Mapping from name to IP address can be trusted better (but can still | 67 | server of the domain). */ |
67 | be fooled if the intruder has access to the name server of the | 68 | hp = gethostbyname(name); |
68 | domain). */ | 69 | if (!hp) { |
69 | hp = gethostbyname(name); | 70 | log("reverse mapping checking gethostbyname for %.700s failed - POSSIBLE BREAKIN ATTEMPT!", name); |
70 | if (!hp) | 71 | strlcpy(name, inet_ntoa(from.sin_addr), sizeof name); |
71 | { | 72 | goto check_ip_options; |
72 | log("reverse mapping checking gethostbyname for %.700s failed - POSSIBLE BREAKIN ATTEMPT!", name); | 73 | } |
73 | strlcpy(name, inet_ntoa(from.sin_addr), sizeof name); | 74 | /* Look for the address from the list of addresses. */ |
74 | goto check_ip_options; | 75 | for (i = 0; hp->h_addr_list[i]; i++) |
76 | if (memcmp(hp->h_addr_list[i], &from.sin_addr, sizeof(from.sin_addr)) | ||
77 | == 0) | ||
78 | break; | ||
79 | /* If we reached the end of the list, the address was not | ||
80 | there. */ | ||
81 | if (!hp->h_addr_list[i]) { | ||
82 | /* Address not found for the host name. */ | ||
83 | log("Address %.100s maps to %.600s, but this does not map back to the address - POSSIBLE BREAKIN ATTEMPT!", | ||
84 | inet_ntoa(from.sin_addr), name); | ||
85 | strlcpy(name, inet_ntoa(from.sin_addr), sizeof name); | ||
86 | goto check_ip_options; | ||
87 | } | ||
88 | /* Address was found for the host name. We accept the host name. */ | ||
89 | } else { | ||
90 | /* Host name not found. Use ascii representation of the address. */ | ||
91 | strlcpy(name, inet_ntoa(from.sin_addr), sizeof name); | ||
92 | log("Could not reverse map address %.100s.", name); | ||
75 | } | 93 | } |
76 | /* Look for the address from the list of addresses. */ | 94 | |
77 | for (i = 0; hp->h_addr_list[i]; i++) | 95 | check_ip_options: |
78 | if (memcmp(hp->h_addr_list[i], &from.sin_addr, sizeof(from.sin_addr)) | 96 | |
79 | == 0) | 97 | /* If IP options are supported, make sure there are none (log and |
80 | break; | 98 | disconnect them if any are found). Basically we are worried |
81 | /* If we reached the end of the list, the address was not there. */ | 99 | about source routing; it can be used to pretend you are |
82 | if (!hp->h_addr_list[i]) | 100 | somebody (ip-address) you are not. That itself may be "almost |
101 | acceptable" under certain circumstances, but rhosts | ||
102 | autentication is useless if source routing is accepted. Notice | ||
103 | also that if we just dropped source routing here, the other | ||
104 | side could use IP spoofing to do rest of the interaction and | ||
105 | could still bypass security. So we exit here if we detect any | ||
106 | IP options. */ | ||
83 | { | 107 | { |
84 | /* Address not found for the host name. */ | 108 | unsigned char options[200], *ucp; |
85 | log("Address %.100s maps to %.600s, but this does not map back to the address - POSSIBLE BREAKIN ATTEMPT!", | 109 | char text[1024], *cp; |
86 | inet_ntoa(from.sin_addr), name); | 110 | int option_size, ipproto; |
87 | strlcpy(name, inet_ntoa(from.sin_addr), sizeof name); | 111 | struct protoent *ip; |
88 | goto check_ip_options; | 112 | |
113 | if ((ip = getprotobyname("ip")) != NULL) | ||
114 | ipproto = ip->p_proto; | ||
115 | else | ||
116 | ipproto = IPPROTO_IP; | ||
117 | option_size = sizeof(options); | ||
118 | if (getsockopt(0, ipproto, IP_OPTIONS, (char *) options, | ||
119 | &option_size) >= 0 && option_size != 0) { | ||
120 | cp = text; | ||
121 | /* Note: "text" buffer must be at least 3x as big as options. */ | ||
122 | for (ucp = options; option_size > 0; ucp++, option_size--, cp += 3) | ||
123 | sprintf(cp, " %2.2x", *ucp); | ||
124 | log("Connection from %.100s with IP options:%.800s", | ||
125 | inet_ntoa(from.sin_addr), text); | ||
126 | packet_disconnect("Connection from %.100s with IP options:%.800s", | ||
127 | inet_ntoa(from.sin_addr), text); | ||
128 | } | ||
89 | } | 129 | } |
90 | /* Address was found for the host name. We accept the host name. */ | 130 | |
91 | } | 131 | return xstrdup(name); |
92 | else | ||
93 | { | ||
94 | /* Host name not found. Use ascii representation of the address. */ | ||
95 | strlcpy(name, inet_ntoa(from.sin_addr), sizeof name); | ||
96 | log("Could not reverse map address %.100s.", name); | ||
97 | } | ||
98 | |||
99 | check_ip_options: | ||
100 | |||
101 | /* If IP options are supported, make sure there are none (log and disconnect | ||
102 | them if any are found). Basically we are worried about source routing; | ||
103 | it can be used to pretend you are somebody (ip-address) you are not. | ||
104 | That itself may be "almost acceptable" under certain circumstances, | ||
105 | but rhosts autentication is useless if source routing is accepted. | ||
106 | Notice also that if we just dropped source routing here, the other | ||
107 | side could use IP spoofing to do rest of the interaction and could still | ||
108 | bypass security. So we exit here if we detect any IP options. */ | ||
109 | { | ||
110 | unsigned char options[200], *ucp; | ||
111 | char text[1024], *cp; | ||
112 | int option_size, ipproto; | ||
113 | struct protoent *ip; | ||
114 | |||
115 | if ((ip = getprotobyname("ip")) != NULL) | ||
116 | ipproto = ip->p_proto; | ||
117 | else | ||
118 | ipproto = IPPROTO_IP; | ||
119 | option_size = sizeof(options); | ||
120 | if (getsockopt(0, ipproto, IP_OPTIONS, (char *)options, | ||
121 | &option_size) >= 0 && option_size != 0) | ||
122 | { | ||
123 | cp = text; | ||
124 | /* Note: "text" buffer must be at least 3x as big as options. */ | ||
125 | for (ucp = options; option_size > 0; ucp++, option_size--, cp += 3) | ||
126 | sprintf(cp, " %2.2x", *ucp); | ||
127 | log("Connection from %.100s with IP options:%.800s", | ||
128 | inet_ntoa(from.sin_addr), text); | ||
129 | packet_disconnect("Connection from %.100s with IP options:%.800s", | ||
130 | inet_ntoa(from.sin_addr), text); | ||
131 | } | ||
132 | } | ||
133 | |||
134 | return xstrdup(name); | ||
135 | } | 132 | } |
136 | 133 | ||
137 | static char *canonical_host_name = NULL; | 134 | static char *canonical_host_name = NULL; |
138 | static char *canonical_host_ip = NULL; | 135 | static char *canonical_host_ip = NULL; |
139 | 136 | ||
140 | /* Return the canonical name of the host in the other side of the current | 137 | /* Return the canonical name of the host in the other side of the current |
141 | connection. The host name is cached, so it is efficient to call this | 138 | connection. The host name is cached, so it is efficient to call this |
142 | several times. */ | 139 | several times. */ |
143 | 140 | ||
144 | const char *get_canonical_hostname() | 141 | const char * |
142 | get_canonical_hostname() | ||
145 | { | 143 | { |
146 | /* Check if we have previously retrieved this same name. */ | 144 | /* Check if we have previously retrieved this same name. */ |
147 | if (canonical_host_name != NULL) | 145 | if (canonical_host_name != NULL) |
148 | return canonical_host_name; | 146 | return canonical_host_name; |
149 | 147 | ||
150 | /* Get the real hostname if socket; otherwise return UNKNOWN. */ | 148 | /* Get the real hostname if socket; otherwise return UNKNOWN. */ |
151 | if (packet_get_connection_in() == packet_get_connection_out()) | 149 | if (packet_get_connection_in() == packet_get_connection_out()) |
152 | canonical_host_name = get_remote_hostname(packet_get_connection_in()); | 150 | canonical_host_name = get_remote_hostname(packet_get_connection_in()); |
153 | else | 151 | else |
154 | canonical_host_name = xstrdup("UNKNOWN"); | 152 | canonical_host_name = xstrdup("UNKNOWN"); |
155 | 153 | ||
156 | return canonical_host_name; | 154 | return canonical_host_name; |
157 | } | 155 | } |
158 | 156 | ||
159 | /* Returns the IP-address of the remote host as a string. The returned | 157 | /* Returns the IP-address of the remote host as a string. The returned |
160 | string need not be freed. */ | 158 | string need not be freed. */ |
161 | 159 | ||
162 | const char *get_remote_ipaddr() | 160 | const char * |
161 | get_remote_ipaddr() | ||
163 | { | 162 | { |
164 | struct sockaddr_in from; | 163 | struct sockaddr_in from; |
165 | int fromlen, socket; | 164 | int fromlen, socket; |
166 | 165 | ||
167 | /* Check if we have previously retrieved this same name. */ | 166 | /* Check if we have previously retrieved this same name. */ |
168 | if (canonical_host_ip != NULL) | 167 | if (canonical_host_ip != NULL) |
169 | return canonical_host_ip; | 168 | return canonical_host_ip; |
170 | 169 | ||
171 | /* If not a socket, return UNKNOWN. */ | 170 | /* If not a socket, return UNKNOWN. */ |
172 | if (packet_get_connection_in() != packet_get_connection_out()) | 171 | if (packet_get_connection_in() != packet_get_connection_out()) { |
173 | { | 172 | canonical_host_ip = xstrdup("UNKNOWN"); |
174 | canonical_host_ip = xstrdup("UNKNOWN"); | 173 | return canonical_host_ip; |
175 | return canonical_host_ip; | 174 | } |
176 | } | 175 | /* Get client socket. */ |
177 | 176 | socket = packet_get_connection_in(); | |
178 | /* Get client socket. */ | 177 | |
179 | socket = packet_get_connection_in(); | 178 | /* Get IP address of client. */ |
180 | 179 | fromlen = sizeof(from); | |
181 | /* Get IP address of client. */ | 180 | memset(&from, 0, sizeof(from)); |
182 | fromlen = sizeof(from); | 181 | if (getpeername(socket, (struct sockaddr *) & from, &fromlen) < 0) { |
183 | memset(&from, 0, sizeof(from)); | 182 | debug("getpeername failed: %.100s", strerror(errno)); |
184 | if (getpeername(socket, (struct sockaddr *)&from, &fromlen) < 0) | 183 | fatal_cleanup(); |
185 | { | 184 | } |
186 | debug("getpeername failed: %.100s", strerror(errno)); | 185 | /* Get the IP address in ascii. */ |
187 | fatal_cleanup(); | 186 | canonical_host_ip = xstrdup(inet_ntoa(from.sin_addr)); |
188 | } | 187 | |
189 | 188 | /* Return ip address string. */ | |
190 | /* Get the IP address in ascii. */ | 189 | return canonical_host_ip; |
191 | canonical_host_ip = xstrdup(inet_ntoa(from.sin_addr)); | ||
192 | |||
193 | /* Return ip address string. */ | ||
194 | return canonical_host_ip; | ||
195 | } | 190 | } |
196 | 191 | ||
197 | /* Returns the port of the peer of the socket. */ | 192 | /* Returns the port of the peer of the socket. */ |
198 | 193 | ||
199 | int get_peer_port(int sock) | 194 | int |
195 | get_peer_port(int sock) | ||
200 | { | 196 | { |
201 | struct sockaddr_in from; | 197 | struct sockaddr_in from; |
202 | int fromlen; | 198 | int fromlen; |
203 | 199 | ||
204 | /* Get IP address of client. */ | 200 | /* Get IP address of client. */ |
205 | fromlen = sizeof(from); | 201 | fromlen = sizeof(from); |
206 | memset(&from, 0, sizeof(from)); | 202 | memset(&from, 0, sizeof(from)); |
207 | if (getpeername(sock, (struct sockaddr *)&from, &fromlen) < 0) | 203 | if (getpeername(sock, (struct sockaddr *) & from, &fromlen) < 0) { |
208 | { | 204 | debug("getpeername failed: %.100s", strerror(errno)); |
209 | debug("getpeername failed: %.100s", strerror(errno)); | 205 | fatal_cleanup(); |
210 | fatal_cleanup(); | 206 | } |
211 | } | 207 | /* Return port number. */ |
212 | 208 | return ntohs(from.sin_port); | |
213 | /* Return port number. */ | ||
214 | return ntohs(from.sin_port); | ||
215 | } | 209 | } |
216 | 210 | ||
217 | /* Returns the port number of the remote host. */ | 211 | /* Returns the port number of the remote host. */ |
218 | 212 | ||
219 | int get_remote_port() | 213 | int |
214 | get_remote_port() | ||
220 | { | 215 | { |
221 | int socket; | 216 | int socket; |
222 | 217 | ||
223 | /* If the connection is not a socket, return 65535. This is intentionally | 218 | /* If the connection is not a socket, return 65535. This is |
224 | chosen to be an unprivileged port number. */ | 219 | intentionally chosen to be an unprivileged port number. */ |
225 | if (packet_get_connection_in() != packet_get_connection_out()) | 220 | if (packet_get_connection_in() != packet_get_connection_out()) |
226 | return 65535; | 221 | return 65535; |
227 | 222 | ||
228 | /* Get client socket. */ | 223 | /* Get client socket. */ |
229 | socket = packet_get_connection_in(); | 224 | socket = packet_get_connection_in(); |
230 | 225 | ||
231 | /* Get and return the peer port number. */ | 226 | /* Get and return the peer port number. */ |
232 | return get_peer_port(socket); | 227 | return get_peer_port(socket); |
233 | } | 228 | } |
diff --git a/channels.c b/channels.c index 3e3b5f369..0a37d1abd 100644 --- a/channels.c +++ b/channels.c | |||
@@ -1,22 +1,22 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * | |
3 | channels.c | 3 | * channels.c |
4 | 4 | * | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
6 | 6 | * | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
8 | All rights reserved | 8 | * All rights reserved |
9 | 9 | * | |
10 | Created: Fri Mar 24 16:35:24 1995 ylo | 10 | * Created: Fri Mar 24 16:35:24 1995 ylo |
11 | 11 | * | |
12 | This file contains functions for generic socket connection forwarding. | 12 | * This file contains functions for generic socket connection forwarding. |
13 | There is also code for initiating connection forwarding for X11 connections, | 13 | * There is also code for initiating connection forwarding for X11 connections, |
14 | arbitrary tcp/ip connections, and the authentication agent connection. | 14 | * arbitrary tcp/ip connections, and the authentication agent connection. |
15 | 15 | * | |
16 | */ | 16 | */ |
17 | 17 | ||
18 | #include "includes.h" | 18 | #include "includes.h" |
19 | RCSID("$Id: channels.c,v 1.6 1999/11/21 02:23:53 damien Exp $"); | 19 | RCSID("$Id: channels.c,v 1.7 1999/11/24 13:26:22 damien Exp $"); |
20 | 20 | ||
21 | #include "ssh.h" | 21 | #include "ssh.h" |
22 | #include "packet.h" | 22 | #include "packet.h" |
@@ -52,7 +52,7 @@ static int channel_max_fd_value = 0; | |||
52 | 52 | ||
53 | /* Name and directory of socket for authentication agent forwarding. */ | 53 | /* Name and directory of socket for authentication agent forwarding. */ |
54 | static char *channel_forwarded_auth_socket_name = NULL; | 54 | static char *channel_forwarded_auth_socket_name = NULL; |
55 | static char *channel_forwarded_auth_socket_dir = NULL; | 55 | static char *channel_forwarded_auth_socket_dir = NULL; |
56 | 56 | ||
57 | /* Saved X11 authentication protocol name. */ | 57 | /* Saved X11 authentication protocol name. */ |
58 | char *x11_saved_proto = NULL; | 58 | char *x11_saved_proto = NULL; |
@@ -70,10 +70,9 @@ unsigned int x11_fake_data_len; | |||
70 | The local sides of any remote forwards are stored in this array to prevent | 70 | The local sides of any remote forwards are stored in this array to prevent |
71 | a corrupt remote server from accessing arbitrary TCP/IP ports on our | 71 | a corrupt remote server from accessing arbitrary TCP/IP ports on our |
72 | local network (which might be behind a firewall). */ | 72 | local network (which might be behind a firewall). */ |
73 | typedef struct | 73 | typedef struct { |
74 | { | 74 | char *host; /* Host name. */ |
75 | char *host; /* Host name. */ | 75 | int port; /* Port number. */ |
76 | int port; /* Port number. */ | ||
77 | } ForwardPermission; | 76 | } ForwardPermission; |
78 | 77 | ||
79 | /* List of all permitted host/port pairs to connect. */ | 78 | /* List of all permitted host/port pairs to connect. */ |
@@ -90,1056 +89,1015 @@ static int have_hostname_in_open = 0; | |||
90 | 89 | ||
91 | /* Sets specific protocol options. */ | 90 | /* Sets specific protocol options. */ |
92 | 91 | ||
93 | void channel_set_options(int hostname_in_open) | 92 | void |
93 | channel_set_options(int hostname_in_open) | ||
94 | { | 94 | { |
95 | have_hostname_in_open = hostname_in_open; | 95 | have_hostname_in_open = hostname_in_open; |
96 | } | 96 | } |
97 | 97 | ||
98 | /* Permits opening to any host/port in SSH_MSG_PORT_OPEN. This is usually | 98 | /* Permits opening to any host/port in SSH_MSG_PORT_OPEN. This is usually |
99 | called by the server, because the user could connect to any port anyway, | 99 | called by the server, because the user could connect to any port anyway, |
100 | and the server has no way to know but to trust the client anyway. */ | 100 | and the server has no way to know but to trust the client anyway. */ |
101 | 101 | ||
102 | void channel_permit_all_opens() | 102 | void |
103 | channel_permit_all_opens() | ||
103 | { | 104 | { |
104 | all_opens_permitted = 1; | 105 | all_opens_permitted = 1; |
105 | } | 106 | } |
106 | 107 | ||
107 | /* Allocate a new channel object and set its type and socket. | 108 | /* Allocate a new channel object and set its type and socket. |
108 | This will cause remote_name to be freed. */ | 109 | This will cause remote_name to be freed. */ |
109 | 110 | ||
110 | int channel_allocate(int type, int sock, char *remote_name) | 111 | int |
112 | channel_allocate(int type, int sock, char *remote_name) | ||
111 | { | 113 | { |
112 | int i, found; | 114 | int i, found; |
113 | Channel *c; | 115 | Channel *c; |
114 | 116 | ||
115 | /* Update the maximum file descriptor value. */ | 117 | /* Update the maximum file descriptor value. */ |
116 | if (sock > channel_max_fd_value) | 118 | if (sock > channel_max_fd_value) |
117 | channel_max_fd_value = sock; | 119 | channel_max_fd_value = sock; |
118 | 120 | ||
119 | /* Do initial allocation if this is the first call. */ | 121 | /* Do initial allocation if this is the first call. */ |
120 | if (channels_alloc == 0) | 122 | if (channels_alloc == 0) { |
121 | { | 123 | channels_alloc = 10; |
122 | channels_alloc = 10; | 124 | channels = xmalloc(channels_alloc * sizeof(Channel)); |
123 | channels = xmalloc(channels_alloc * sizeof(Channel)); | 125 | for (i = 0; i < channels_alloc; i++) |
124 | for (i = 0; i < channels_alloc; i++) | 126 | channels[i].type = SSH_CHANNEL_FREE; |
125 | channels[i].type = SSH_CHANNEL_FREE; | 127 | |
126 | 128 | /* Kludge: arrange a call to channel_stop_listening if we | |
127 | /* Kludge: arrange a call to channel_stop_listening if we terminate | 129 | terminate with fatal(). */ |
128 | with fatal(). */ | 130 | fatal_add_cleanup((void (*) (void *)) channel_stop_listening, NULL); |
129 | fatal_add_cleanup((void (*)(void *))channel_stop_listening, NULL); | 131 | } |
130 | } | 132 | /* Try to find a free slot where to put the new channel. */ |
131 | 133 | for (found = -1, i = 0; i < channels_alloc; i++) | |
132 | /* Try to find a free slot where to put the new channel. */ | 134 | if (channels[i].type == SSH_CHANNEL_FREE) { |
133 | for (found = -1, i = 0; i < channels_alloc; i++) | 135 | /* Found a free slot. */ |
134 | if (channels[i].type == SSH_CHANNEL_FREE) | 136 | found = i; |
135 | { | 137 | break; |
136 | /* Found a free slot. */ | 138 | } |
137 | found = i; | 139 | if (found == -1) { |
138 | break; | 140 | /* There are no free slots. Take last+1 slot and expand |
139 | } | 141 | the array. */ |
140 | 142 | found = channels_alloc; | |
141 | if (found == -1) | 143 | channels_alloc += 10; |
142 | { | 144 | debug("channel: expanding %d", channels_alloc); |
143 | /* There are no free slots. Take last+1 slot and expand the array. */ | 145 | channels = xrealloc(channels, channels_alloc * sizeof(Channel)); |
144 | found = channels_alloc; | 146 | for (i = found; i < channels_alloc; i++) |
145 | channels_alloc += 10; | 147 | channels[i].type = SSH_CHANNEL_FREE; |
146 | debug("channel: expanding %d", channels_alloc); | 148 | } |
147 | channels = xrealloc(channels, channels_alloc * sizeof(Channel)); | 149 | /* Initialize and return new channel number. */ |
148 | for (i = found; i < channels_alloc; i++) | 150 | c = &channels[found]; |
149 | channels[i].type = SSH_CHANNEL_FREE; | 151 | buffer_init(&c->input); |
150 | } | 152 | buffer_init(&c->output); |
151 | 153 | chan_init_iostates(c); | |
152 | /* Initialize and return new channel number. */ | 154 | c->self = found; |
153 | c=&channels[found]; | 155 | c->type = type; |
154 | buffer_init(&c->input); | 156 | c->sock = sock; |
155 | buffer_init(&c->output); | 157 | c->remote_id = -1; |
156 | chan_init_iostates(c); | 158 | c->remote_name = remote_name; |
157 | c->self = found; | 159 | debug("channel %d: new [%s]", found, remote_name); |
158 | c->type = type; | 160 | return found; |
159 | c->sock = sock; | ||
160 | c->remote_id = -1; | ||
161 | c->remote_name = remote_name; | ||
162 | debug("channel %d: new [%s]", found, remote_name); | ||
163 | return found; | ||
164 | } | 161 | } |
165 | 162 | ||
166 | /* Free the channel and close its socket. */ | 163 | /* Free the channel and close its socket. */ |
167 | 164 | ||
168 | void channel_free(int channel) | 165 | void |
166 | channel_free(int channel) | ||
169 | { | 167 | { |
170 | if (channel < 0 || channel >= channels_alloc || | 168 | if (channel < 0 || channel >= channels_alloc || |
171 | channels[channel].type == SSH_CHANNEL_FREE) | 169 | channels[channel].type == SSH_CHANNEL_FREE) |
172 | packet_disconnect("channel free: bad local channel %d", channel); | 170 | packet_disconnect("channel free: bad local channel %d", channel); |
173 | 171 | ||
174 | if(compat13) | 172 | if (compat13) |
175 | shutdown(channels[channel].sock, SHUT_RDWR); | 173 | shutdown(channels[channel].sock, SHUT_RDWR); |
176 | close(channels[channel].sock); | 174 | close(channels[channel].sock); |
177 | buffer_free(&channels[channel].input); | 175 | buffer_free(&channels[channel].input); |
178 | buffer_free(&channels[channel].output); | 176 | buffer_free(&channels[channel].output); |
179 | channels[channel].type = SSH_CHANNEL_FREE; | 177 | channels[channel].type = SSH_CHANNEL_FREE; |
180 | if (channels[channel].remote_name) | 178 | if (channels[channel].remote_name) { |
181 | { | 179 | xfree(channels[channel].remote_name); |
182 | xfree(channels[channel].remote_name); | 180 | channels[channel].remote_name = NULL; |
183 | channels[channel].remote_name = NULL; | 181 | } |
184 | } | ||
185 | } | 182 | } |
186 | 183 | ||
187 | /* This is called just before select() to add any bits relevant to | 184 | /* This is called just before select() to add any bits relevant to |
188 | channels in the select bitmasks. */ | 185 | channels in the select bitmasks. */ |
189 | 186 | ||
190 | void channel_prepare_select(fd_set *readset, fd_set *writeset) | 187 | void |
188 | channel_prepare_select(fd_set * readset, fd_set * writeset) | ||
191 | { | 189 | { |
192 | int i; | 190 | int i; |
193 | Channel *ch; | 191 | Channel *ch; |
194 | unsigned char *ucp; | 192 | unsigned char *ucp; |
195 | unsigned int proto_len, data_len; | 193 | unsigned int proto_len, data_len; |
196 | 194 | ||
197 | for (i = 0; i < channels_alloc; i++) | 195 | for (i = 0; i < channels_alloc; i++) { |
198 | { | 196 | ch = &channels[i]; |
199 | ch = &channels[i]; | 197 | redo: |
200 | redo: | 198 | switch (ch->type) { |
201 | switch (ch->type) | 199 | case SSH_CHANNEL_X11_LISTENER: |
202 | { | 200 | case SSH_CHANNEL_PORT_LISTENER: |
203 | case SSH_CHANNEL_X11_LISTENER: | 201 | case SSH_CHANNEL_AUTH_SOCKET: |
204 | case SSH_CHANNEL_PORT_LISTENER: | 202 | FD_SET(ch->sock, readset); |
205 | case SSH_CHANNEL_AUTH_SOCKET: | 203 | break; |
206 | FD_SET(ch->sock, readset); | 204 | |
207 | break; | 205 | case SSH_CHANNEL_OPEN: |
208 | 206 | if (compat13) { | |
209 | case SSH_CHANNEL_OPEN: | 207 | if (buffer_len(&ch->input) < packet_get_maxsize()) |
210 | if(compat13){ | 208 | FD_SET(ch->sock, readset); |
211 | if (buffer_len(&ch->input) < packet_get_maxsize()) | 209 | if (buffer_len(&ch->output) > 0) |
212 | FD_SET(ch->sock, readset); | 210 | FD_SET(ch->sock, writeset); |
213 | if (buffer_len(&ch->output) > 0) | 211 | break; |
214 | FD_SET(ch->sock, writeset); | 212 | } |
215 | break; | 213 | /* test whether sockets are 'alive' for read/write */ |
216 | } | 214 | if (ch->istate == CHAN_INPUT_OPEN) |
217 | /* test whether sockets are 'alive' for read/write */ | 215 | if (buffer_len(&ch->input) < packet_get_maxsize()) |
218 | if (ch->istate == CHAN_INPUT_OPEN) | 216 | FD_SET(ch->sock, readset); |
219 | if (buffer_len(&ch->input) < packet_get_maxsize()) | 217 | if (ch->ostate == CHAN_OUTPUT_OPEN || |
220 | FD_SET(ch->sock, readset); | 218 | ch->ostate == CHAN_OUTPUT_WAIT_DRAIN) { |
221 | if (ch->ostate == CHAN_OUTPUT_OPEN || ch->ostate == CHAN_OUTPUT_WAIT_DRAIN){ | 219 | if (buffer_len(&ch->output) > 0) { |
222 | if (buffer_len(&ch->output) > 0){ | 220 | FD_SET(ch->sock, writeset); |
223 | FD_SET(ch->sock, writeset); | 221 | } else if (ch->ostate == CHAN_OUTPUT_WAIT_DRAIN) { |
224 | }else if(ch->ostate == CHAN_OUTPUT_WAIT_DRAIN) { | 222 | chan_obuf_empty(ch); |
225 | chan_obuf_empty(ch); | 223 | } |
226 | } | 224 | } |
227 | } | 225 | break; |
228 | break; | 226 | |
229 | 227 | case SSH_CHANNEL_INPUT_DRAINING: | |
230 | case SSH_CHANNEL_INPUT_DRAINING: | 228 | if (!compat13) |
231 | if (!compat13) | 229 | fatal("cannot happen: IN_DRAIN"); |
232 | fatal("cannot happen: IN_DRAIN"); | 230 | if (buffer_len(&ch->input) == 0) { |
233 | if (buffer_len(&ch->input) == 0) | 231 | packet_start(SSH_MSG_CHANNEL_CLOSE); |
234 | { | 232 | packet_put_int(ch->remote_id); |
235 | packet_start(SSH_MSG_CHANNEL_CLOSE); | 233 | packet_send(); |
236 | packet_put_int(ch->remote_id); | 234 | ch->type = SSH_CHANNEL_CLOSED; |
237 | packet_send(); | 235 | debug("Closing channel %d after input drain.", i); |
238 | ch->type = SSH_CHANNEL_CLOSED; | 236 | break; |
239 | debug("Closing channel %d after input drain.", i); | 237 | } |
240 | break; | 238 | break; |
241 | } | 239 | |
242 | break; | 240 | case SSH_CHANNEL_OUTPUT_DRAINING: |
243 | 241 | if (!compat13) | |
244 | case SSH_CHANNEL_OUTPUT_DRAINING: | 242 | fatal("cannot happen: OUT_DRAIN"); |
245 | if (!compat13) | 243 | if (buffer_len(&ch->output) == 0) { |
246 | fatal("cannot happen: OUT_DRAIN"); | 244 | channel_free(i); |
247 | if (buffer_len(&ch->output) == 0) | 245 | break; |
248 | { | 246 | } |
249 | /* debug("Freeing channel %d after output drain.", i); */ | 247 | FD_SET(ch->sock, writeset); |
250 | channel_free(i); | 248 | break; |
251 | break; | 249 | |
252 | } | 250 | case SSH_CHANNEL_X11_OPEN: |
253 | FD_SET(ch->sock, writeset); | 251 | /* This is a special state for X11 authentication |
254 | break; | 252 | spoofing. An opened X11 connection (when |
255 | 253 | authentication spoofing is being done) remains | |
256 | case SSH_CHANNEL_X11_OPEN: | 254 | in this state until the first packet has been |
257 | /* This is a special state for X11 authentication spoofing. An | 255 | completely read. The authentication data in |
258 | opened X11 connection (when authentication spoofing is being | 256 | that packet is then substituted by the real |
259 | done) remains in this state until the first packet has been | 257 | data if it matches the fake data, and the |
260 | completely read. The authentication data in that packet is | 258 | channel is put into normal mode. */ |
261 | then substituted by the real data if it matches the fake data, | 259 | |
262 | and the channel is put into normal mode. */ | 260 | /* Check if the fixed size part of the packet is in buffer. */ |
263 | 261 | if (buffer_len(&ch->output) < 12) | |
264 | /* Check if the fixed size part of the packet is in buffer. */ | 262 | break; |
265 | if (buffer_len(&ch->output) < 12) | 263 | |
266 | break; | 264 | /* Parse the lengths of variable-length fields. */ |
267 | 265 | ucp = (unsigned char *) buffer_ptr(&ch->output); | |
268 | /* Parse the lengths of variable-length fields. */ | 266 | if (ucp[0] == 0x42) { /* Byte order MSB first. */ |
269 | ucp = (unsigned char *)buffer_ptr(&ch->output); | 267 | proto_len = 256 * ucp[6] + ucp[7]; |
270 | if (ucp[0] == 0x42) | 268 | data_len = 256 * ucp[8] + ucp[9]; |
271 | { /* Byte order MSB first. */ | 269 | } else if (ucp[0] == 0x6c) { /* Byte order LSB first. */ |
272 | proto_len = 256 * ucp[6] + ucp[7]; | 270 | proto_len = ucp[6] + 256 * ucp[7]; |
273 | data_len = 256 * ucp[8] + ucp[9]; | 271 | data_len = ucp[8] + 256 * ucp[9]; |
274 | } | 272 | } else { |
275 | else | 273 | debug("Initial X11 packet contains bad byte order byte: 0x%x", |
276 | if (ucp[0] == 0x6c) | 274 | ucp[0]); |
277 | { /* Byte order LSB first. */ | 275 | ch->type = SSH_CHANNEL_OPEN; |
278 | proto_len = ucp[6] + 256 * ucp[7]; | 276 | goto reject; |
279 | data_len = ucp[8] + 256 * ucp[9]; | 277 | } |
280 | } | 278 | |
281 | else | 279 | /* Check if the whole packet is in buffer. */ |
282 | { | 280 | if (buffer_len(&ch->output) < |
283 | debug("Initial X11 packet contains bad byte order byte: 0x%x", | 281 | 12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3)) |
284 | ucp[0]); | 282 | break; |
285 | ch->type = SSH_CHANNEL_OPEN; | 283 | |
286 | goto reject; | 284 | /* Check if authentication protocol matches. */ |
287 | } | 285 | if (proto_len != strlen(x11_saved_proto) || |
288 | 286 | memcmp(ucp + 12, x11_saved_proto, proto_len) != 0) { | |
289 | /* Check if the whole packet is in buffer. */ | 287 | debug("X11 connection uses different authentication protocol."); |
290 | if (buffer_len(&ch->output) < | 288 | ch->type = SSH_CHANNEL_OPEN; |
291 | 12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3)) | 289 | goto reject; |
292 | break; | 290 | } |
293 | 291 | /* Check if authentication data matches our fake data. */ | |
294 | /* Check if authentication protocol matches. */ | 292 | if (data_len != x11_fake_data_len || |
295 | if (proto_len != strlen(x11_saved_proto) || | 293 | memcmp(ucp + 12 + ((proto_len + 3) & ~3), |
296 | memcmp(ucp + 12, x11_saved_proto, proto_len) != 0) | 294 | x11_fake_data, x11_fake_data_len) != 0) { |
297 | { | 295 | debug("X11 auth data does not match fake data."); |
298 | debug("X11 connection uses different authentication protocol."); | 296 | ch->type = SSH_CHANNEL_OPEN; |
299 | ch->type = SSH_CHANNEL_OPEN; | 297 | goto reject; |
300 | goto reject; | 298 | } |
301 | } | 299 | /* Check fake data length */ |
302 | 300 | if (x11_fake_data_len != x11_saved_data_len) { | |
303 | /* Check if authentication data matches our fake data. */ | 301 | error("X11 fake_data_len %d != saved_data_len %d", |
304 | if (data_len != x11_fake_data_len || | 302 | x11_fake_data_len, x11_saved_data_len); |
305 | memcmp(ucp + 12 + ((proto_len + 3) & ~3), | 303 | ch->type = SSH_CHANNEL_OPEN; |
306 | x11_fake_data, x11_fake_data_len) != 0) | 304 | goto reject; |
307 | { | 305 | } |
308 | debug("X11 auth data does not match fake data."); | 306 | /* Received authentication protocol and data match |
309 | ch->type = SSH_CHANNEL_OPEN; | 307 | our fake data. Substitute the fake data with |
310 | goto reject; | 308 | real data. */ |
311 | } | 309 | memcpy(ucp + 12 + ((proto_len + 3) & ~3), |
312 | 310 | x11_saved_data, x11_saved_data_len); | |
313 | /* Check fake data length */ | 311 | |
314 | if (x11_fake_data_len != x11_saved_data_len) | 312 | /* Start normal processing for the channel. */ |
315 | { | 313 | ch->type = SSH_CHANNEL_OPEN; |
316 | error("X11 fake_data_len %d != saved_data_len %d", | 314 | goto redo; |
317 | x11_fake_data_len, x11_saved_data_len); | 315 | |
318 | ch->type = SSH_CHANNEL_OPEN; | ||
319 | goto reject; | ||
320 | } | ||
321 | |||
322 | /* Received authentication protocol and data match our fake data. | ||
323 | Substitute the fake data with real data. */ | ||
324 | memcpy(ucp + 12 + ((proto_len + 3) & ~3), | ||
325 | x11_saved_data, x11_saved_data_len); | ||
326 | |||
327 | /* Start normal processing for the channel. */ | ||
328 | ch->type = SSH_CHANNEL_OPEN; | ||
329 | goto redo; | ||
330 | |||
331 | reject: | 316 | reject: |
332 | /* We have received an X11 connection that has bad authentication | 317 | /* We have received an X11 connection that has bad |
333 | information. */ | 318 | authentication information. */ |
334 | log("X11 connection rejected because of wrong authentication.\r\n"); | 319 | log("X11 connection rejected because of wrong authentication.\r\n"); |
335 | buffer_clear(&ch->input); | 320 | buffer_clear(&ch->input); |
336 | buffer_clear(&ch->output); | 321 | buffer_clear(&ch->output); |
337 | if (compat13) { | 322 | if (compat13) { |
338 | close(ch->sock); | 323 | close(ch->sock); |
339 | ch->sock = -1; | 324 | ch->sock = -1; |
340 | ch->type = SSH_CHANNEL_CLOSED; | 325 | ch->type = SSH_CHANNEL_CLOSED; |
341 | packet_start(SSH_MSG_CHANNEL_CLOSE); | 326 | packet_start(SSH_MSG_CHANNEL_CLOSE); |
342 | packet_put_int(ch->remote_id); | 327 | packet_put_int(ch->remote_id); |
343 | packet_send(); | 328 | packet_send(); |
344 | }else{ | 329 | } else { |
345 | debug("X11 rejected %d i%d/o%d", ch->self, ch->istate, ch->ostate); | 330 | debug("X11 rejected %d i%d/o%d", ch->self, ch->istate, ch->ostate); |
346 | chan_read_failed(ch); | 331 | chan_read_failed(ch); |
347 | chan_write_failed(ch); | 332 | chan_write_failed(ch); |
348 | debug("X11 rejected %d i%d/o%d", ch->self, ch->istate, ch->ostate); | 333 | debug("X11 rejected %d i%d/o%d", ch->self, ch->istate, ch->ostate); |
349 | } | 334 | } |
350 | break; | 335 | break; |
351 | 336 | ||
352 | case SSH_CHANNEL_FREE: | 337 | case SSH_CHANNEL_FREE: |
353 | default: | 338 | default: |
354 | continue; | 339 | continue; |
340 | } | ||
355 | } | 341 | } |
356 | } | ||
357 | } | 342 | } |
358 | 343 | ||
359 | /* After select, perform any appropriate operations for channels which | 344 | /* After select, perform any appropriate operations for channels which |
360 | have events pending. */ | 345 | have events pending. */ |
361 | 346 | ||
362 | void channel_after_select(fd_set *readset, fd_set *writeset) | 347 | void |
348 | channel_after_select(fd_set * readset, fd_set * writeset) | ||
363 | { | 349 | { |
364 | struct sockaddr addr; | 350 | struct sockaddr addr; |
365 | int addrlen, newsock, i, newch, len; | 351 | int addrlen, newsock, i, newch, len; |
366 | Channel *ch; | 352 | Channel *ch; |
367 | char buf[16384], *remote_hostname; | 353 | char buf[16384], *remote_hostname; |
368 | 354 | ||
369 | /* Loop over all channels... */ | 355 | /* Loop over all channels... */ |
370 | for (i = 0; i < channels_alloc; i++) | 356 | for (i = 0; i < channels_alloc; i++) { |
371 | { | 357 | ch = &channels[i]; |
372 | ch = &channels[i]; | 358 | switch (ch->type) { |
373 | switch (ch->type) | 359 | case SSH_CHANNEL_X11_LISTENER: |
374 | { | 360 | /* This is our fake X11 server socket. */ |
375 | case SSH_CHANNEL_X11_LISTENER: | 361 | if (FD_ISSET(ch->sock, readset)) { |
376 | /* This is our fake X11 server socket. */ | 362 | debug("X11 connection requested."); |
377 | if (FD_ISSET(ch->sock, readset)) | 363 | addrlen = sizeof(addr); |
378 | { | 364 | newsock = accept(ch->sock, &addr, &addrlen); |
379 | debug("X11 connection requested."); | 365 | if (newsock < 0) { |
380 | addrlen = sizeof(addr); | 366 | error("accept: %.100s", strerror(errno)); |
381 | newsock = accept(ch->sock, &addr, &addrlen); | 367 | break; |
382 | if (newsock < 0) | 368 | } |
383 | { | 369 | remote_hostname = get_remote_hostname(newsock); |
384 | error("accept: %.100s", strerror(errno)); | 370 | snprintf(buf, sizeof buf, "X11 connection from %.200s port %d", |
385 | break; | 371 | remote_hostname, get_peer_port(newsock)); |
386 | } | 372 | xfree(remote_hostname); |
387 | remote_hostname = get_remote_hostname(newsock); | 373 | newch = channel_allocate(SSH_CHANNEL_OPENING, newsock, |
388 | snprintf(buf, sizeof buf, "X11 connection from %.200s port %d", | 374 | xstrdup(buf)); |
389 | remote_hostname, get_peer_port(newsock)); | 375 | packet_start(SSH_SMSG_X11_OPEN); |
390 | xfree(remote_hostname); | 376 | packet_put_int(newch); |
391 | newch = channel_allocate(SSH_CHANNEL_OPENING, newsock, | 377 | if (have_hostname_in_open) |
392 | xstrdup(buf)); | 378 | packet_put_string(buf, strlen(buf)); |
393 | packet_start(SSH_SMSG_X11_OPEN); | 379 | packet_send(); |
394 | packet_put_int(newch); | 380 | } |
395 | if (have_hostname_in_open) | 381 | break; |
396 | packet_put_string(buf, strlen(buf)); | 382 | |
397 | packet_send(); | 383 | case SSH_CHANNEL_PORT_LISTENER: |
398 | } | 384 | /* This socket is listening for connections to a |
399 | break; | 385 | forwarded TCP/IP port. */ |
400 | 386 | if (FD_ISSET(ch->sock, readset)) { | |
401 | case SSH_CHANNEL_PORT_LISTENER: | 387 | debug("Connection to port %d forwarding to %.100s:%d requested.", |
402 | /* This socket is listening for connections to a forwarded TCP/IP | 388 | ch->listening_port, ch->path, ch->host_port); |
403 | port. */ | 389 | addrlen = sizeof(addr); |
404 | if (FD_ISSET(ch->sock, readset)) | 390 | newsock = accept(ch->sock, &addr, &addrlen); |
405 | { | 391 | if (newsock < 0) { |
406 | debug("Connection to port %d forwarding to %.100s:%d requested.", | 392 | error("accept: %.100s", strerror(errno)); |
407 | ch->listening_port, ch->path, ch->host_port); | 393 | break; |
408 | addrlen = sizeof(addr); | 394 | } |
409 | newsock = accept(ch->sock, &addr, &addrlen); | 395 | remote_hostname = get_remote_hostname(newsock); |
410 | if (newsock < 0) | 396 | snprintf(buf, sizeof buf, "listen port %d:%.100s:%d, connect from %.200s:%d", |
411 | { | 397 | ch->listening_port, ch->path, ch->host_port, |
412 | error("accept: %.100s", strerror(errno)); | 398 | remote_hostname, get_peer_port(newsock)); |
413 | break; | 399 | xfree(remote_hostname); |
414 | } | 400 | newch = channel_allocate(SSH_CHANNEL_OPENING, newsock, |
415 | remote_hostname = get_remote_hostname(newsock); | 401 | xstrdup(buf)); |
416 | snprintf(buf, sizeof buf, "listen port %d:%.100s:%d, connect from %.200s:%d", | 402 | packet_start(SSH_MSG_PORT_OPEN); |
417 | ch->listening_port, ch->path, ch->host_port, | 403 | packet_put_int(newch); |
418 | remote_hostname, get_peer_port(newsock)); | 404 | packet_put_string(ch->path, strlen(ch->path)); |
419 | xfree(remote_hostname); | 405 | packet_put_int(ch->host_port); |
420 | newch = channel_allocate(SSH_CHANNEL_OPENING, newsock, | 406 | if (have_hostname_in_open) |
421 | xstrdup(buf)); | 407 | packet_put_string(buf, strlen(buf)); |
422 | packet_start(SSH_MSG_PORT_OPEN); | 408 | packet_send(); |
423 | packet_put_int(newch); | 409 | } |
424 | packet_put_string(ch->path, strlen(ch->path)); | 410 | break; |
425 | packet_put_int(ch->host_port); | 411 | |
426 | if (have_hostname_in_open) | 412 | case SSH_CHANNEL_AUTH_SOCKET: |
427 | packet_put_string(buf, strlen(buf)); | 413 | /* This is the authentication agent socket |
428 | packet_send(); | 414 | listening for connections from clients. */ |
429 | } | 415 | if (FD_ISSET(ch->sock, readset)) { |
430 | break; | 416 | int nchan; |
431 | 417 | len = sizeof(addr); | |
432 | case SSH_CHANNEL_AUTH_SOCKET: | 418 | newsock = accept(ch->sock, &addr, &len); |
433 | /* This is the authentication agent socket listening for connections | 419 | if (newsock < 0) { |
434 | from clients. */ | 420 | error("accept from auth socket: %.100s", strerror(errno)); |
435 | if (FD_ISSET(ch->sock, readset)) | 421 | break; |
436 | { | 422 | } |
437 | int nchan; | 423 | nchan = channel_allocate(SSH_CHANNEL_OPENING, newsock, |
438 | len = sizeof(addr); | 424 | xstrdup("accepted auth socket")); |
439 | newsock = accept(ch->sock, &addr, &len); | 425 | packet_start(SSH_SMSG_AGENT_OPEN); |
440 | if (newsock < 0) | 426 | packet_put_int(nchan); |
441 | { | 427 | packet_send(); |
442 | error("accept from auth socket: %.100s", strerror(errno)); | 428 | } |
443 | break; | 429 | break; |
430 | |||
431 | case SSH_CHANNEL_OPEN: | ||
432 | /* This is an open two-way communication channel. | ||
433 | It is not of interest to us at this point what | ||
434 | kind of data is being transmitted. */ | ||
435 | |||
436 | /* Read available incoming data and append it to | ||
437 | buffer; shutdown socket, if read or write | ||
438 | failes */ | ||
439 | if (FD_ISSET(ch->sock, readset)) { | ||
440 | len = read(ch->sock, buf, sizeof(buf)); | ||
441 | if (len <= 0) { | ||
442 | if (compat13) { | ||
443 | buffer_consume(&ch->output, buffer_len(&ch->output)); | ||
444 | ch->type = SSH_CHANNEL_INPUT_DRAINING; | ||
445 | debug("Channel %d status set to input draining.", i); | ||
446 | } else { | ||
447 | chan_read_failed(ch); | ||
448 | } | ||
449 | break; | ||
450 | } | ||
451 | buffer_append(&ch->input, buf, len); | ||
452 | } | ||
453 | /* Send buffered output data to the socket. */ | ||
454 | if (FD_ISSET(ch->sock, writeset) && buffer_len(&ch->output) > 0) { | ||
455 | len = write(ch->sock, buffer_ptr(&ch->output), | ||
456 | buffer_len(&ch->output)); | ||
457 | if (len <= 0) { | ||
458 | if (compat13) { | ||
459 | buffer_consume(&ch->output, buffer_len(&ch->output)); | ||
460 | debug("Channel %d status set to input draining.", i); | ||
461 | ch->type = SSH_CHANNEL_INPUT_DRAINING; | ||
462 | } else { | ||
463 | chan_write_failed(ch); | ||
464 | } | ||
465 | break; | ||
466 | } | ||
467 | buffer_consume(&ch->output, len); | ||
468 | } | ||
469 | break; | ||
470 | |||
471 | case SSH_CHANNEL_OUTPUT_DRAINING: | ||
472 | if (!compat13) | ||
473 | fatal("cannot happen: OUT_DRAIN"); | ||
474 | /* Send buffered output data to the socket. */ | ||
475 | if (FD_ISSET(ch->sock, writeset) && buffer_len(&ch->output) > 0) { | ||
476 | len = write(ch->sock, buffer_ptr(&ch->output), | ||
477 | buffer_len(&ch->output)); | ||
478 | if (len <= 0) | ||
479 | buffer_consume(&ch->output, buffer_len(&ch->output)); | ||
480 | else | ||
481 | buffer_consume(&ch->output, len); | ||
482 | } | ||
483 | break; | ||
484 | |||
485 | case SSH_CHANNEL_X11_OPEN: | ||
486 | case SSH_CHANNEL_FREE: | ||
487 | default: | ||
488 | continue; | ||
444 | } | 489 | } |
445 | |||
446 | nchan = channel_allocate(SSH_CHANNEL_OPENING, newsock, | ||
447 | xstrdup("accepted auth socket")); | ||
448 | packet_start(SSH_SMSG_AGENT_OPEN); | ||
449 | packet_put_int(nchan); | ||
450 | packet_send(); | ||
451 | } | ||
452 | break; | ||
453 | |||
454 | case SSH_CHANNEL_OPEN: | ||
455 | /* This is an open two-way communication channel. It is not of | ||
456 | interest to us at this point what kind of data is being | ||
457 | transmitted. */ | ||
458 | |||
459 | /* Read available incoming data and append it to buffer; | ||
460 | shutdown socket, if read or write failes */ | ||
461 | if (FD_ISSET(ch->sock, readset)) | ||
462 | { | ||
463 | len = read(ch->sock, buf, sizeof(buf)); | ||
464 | if (len <= 0) | ||
465 | { | ||
466 | if (compat13) { | ||
467 | buffer_consume(&ch->output, buffer_len(&ch->output)); | ||
468 | ch->type = SSH_CHANNEL_INPUT_DRAINING; | ||
469 | debug("Channel %d status set to input draining.", i); | ||
470 | }else{ | ||
471 | chan_read_failed(ch); | ||
472 | } | ||
473 | break; | ||
474 | } | ||
475 | buffer_append(&ch->input, buf, len); | ||
476 | } | ||
477 | /* Send buffered output data to the socket. */ | ||
478 | if (FD_ISSET(ch->sock, writeset) && buffer_len(&ch->output) > 0) | ||
479 | { | ||
480 | len = write(ch->sock, buffer_ptr(&ch->output), | ||
481 | buffer_len(&ch->output)); | ||
482 | if (len <= 0) | ||
483 | { | ||
484 | if (compat13) { | ||
485 | buffer_consume(&ch->output, buffer_len(&ch->output)); | ||
486 | debug("Channel %d status set to input draining.", i); | ||
487 | ch->type = SSH_CHANNEL_INPUT_DRAINING; | ||
488 | }else{ | ||
489 | chan_write_failed(ch); | ||
490 | } | ||
491 | break; | ||
492 | } | ||
493 | buffer_consume(&ch->output, len); | ||
494 | } | ||
495 | break; | ||
496 | |||
497 | case SSH_CHANNEL_OUTPUT_DRAINING: | ||
498 | if (!compat13) | ||
499 | fatal("cannot happen: OUT_DRAIN"); | ||
500 | /* Send buffered output data to the socket. */ | ||
501 | if (FD_ISSET(ch->sock, writeset) && buffer_len(&ch->output) > 0) | ||
502 | { | ||
503 | len = write(ch->sock, buffer_ptr(&ch->output), | ||
504 | buffer_len(&ch->output)); | ||
505 | if (len <= 0) | ||
506 | buffer_consume(&ch->output, buffer_len(&ch->output)); | ||
507 | else | ||
508 | buffer_consume(&ch->output, len); | ||
509 | } | ||
510 | break; | ||
511 | |||
512 | case SSH_CHANNEL_X11_OPEN: | ||
513 | case SSH_CHANNEL_FREE: | ||
514 | default: | ||
515 | continue; | ||
516 | } | 490 | } |
517 | } | ||
518 | } | 491 | } |
519 | 492 | ||
520 | /* If there is data to send to the connection, send some of it now. */ | 493 | /* If there is data to send to the connection, send some of it now. */ |
521 | 494 | ||
522 | void channel_output_poll() | 495 | void |
496 | channel_output_poll() | ||
523 | { | 497 | { |
524 | int len, i; | 498 | int len, i; |
525 | Channel *ch; | 499 | Channel *ch; |
526 | 500 | ||
527 | for (i = 0; i < channels_alloc; i++) | 501 | for (i = 0; i < channels_alloc; i++) { |
528 | { | 502 | ch = &channels[i]; |
529 | ch = &channels[i]; | 503 | /* We are only interested in channels that can have |
530 | /* We are only interested in channels that can have buffered incoming | 504 | buffered incoming data. */ |
531 | data. */ | 505 | if (ch->type != SSH_CHANNEL_OPEN && |
532 | if (ch->type != SSH_CHANNEL_OPEN && | 506 | ch->type != SSH_CHANNEL_INPUT_DRAINING) |
533 | ch->type != SSH_CHANNEL_INPUT_DRAINING) | 507 | continue; |
534 | continue; | 508 | |
535 | 509 | /* Get the amount of buffered data for this channel. */ | |
536 | /* Get the amount of buffered data for this channel. */ | 510 | len = buffer_len(&ch->input); |
537 | len = buffer_len(&ch->input); | 511 | if (len > 0) { |
538 | if (len > 0) | 512 | /* Send some data for the other side over the |
539 | { | 513 | secure connection. */ |
540 | /* Send some data for the other side over the secure connection. */ | 514 | if (packet_is_interactive()) { |
541 | if (packet_is_interactive()) | 515 | if (len > 1024) |
542 | { | 516 | len = 512; |
543 | if (len > 1024) | 517 | } else { |
544 | len = 512; | 518 | /* Keep the packets at reasonable size. */ |
545 | } | 519 | if (len > 16384) |
546 | else | 520 | len = 16384; |
547 | { | 521 | } |
548 | if (len > 16384) | 522 | packet_start(SSH_MSG_CHANNEL_DATA); |
549 | len = 16384; /* Keep the packets at reasonable size. */ | 523 | packet_put_int(ch->remote_id); |
550 | } | 524 | packet_put_string(buffer_ptr(&ch->input), len); |
551 | packet_start(SSH_MSG_CHANNEL_DATA); | 525 | packet_send(); |
552 | packet_put_int(ch->remote_id); | 526 | buffer_consume(&ch->input, len); |
553 | packet_put_string(buffer_ptr(&ch->input), len); | 527 | } else if (ch->istate == CHAN_INPUT_WAIT_DRAIN) { |
554 | packet_send(); | 528 | if (compat13) |
555 | buffer_consume(&ch->input, len); | 529 | fatal("cannot happen: istate == INPUT_WAIT_DRAIN for proto 1.3"); |
530 | /* input-buffer is empty and read-socket shutdown: | ||
531 | tell peer, that we will not send more data: | ||
532 | send IEOF */ | ||
533 | chan_ibuf_empty(ch); | ||
534 | } | ||
556 | } | 535 | } |
557 | else if(ch->istate == CHAN_INPUT_WAIT_DRAIN) | ||
558 | { | ||
559 | if (compat13) | ||
560 | fatal("cannot happen: istate == INPUT_WAIT_DRAIN for proto 1.3"); | ||
561 | /* input-buffer is empty and read-socket shutdown: | ||
562 | tell peer, that we will not send more data: send IEOF */ | ||
563 | chan_ibuf_empty(ch); | ||
564 | } | ||
565 | } | ||
566 | } | 536 | } |
567 | 537 | ||
568 | /* This is called when a packet of type CHANNEL_DATA has just been received. | 538 | /* This is called when a packet of type CHANNEL_DATA has just been received. |
569 | The message type has already been consumed, but channel number and data | 539 | The message type has already been consumed, but channel number and data |
570 | is still there. */ | 540 | is still there. */ |
571 | 541 | ||
572 | void channel_input_data(int payload_len) | 542 | void |
543 | channel_input_data(int payload_len) | ||
573 | { | 544 | { |
574 | int channel; | 545 | int channel; |
575 | char *data; | 546 | char *data; |
576 | unsigned int data_len; | 547 | unsigned int data_len; |
577 | 548 | ||
578 | /* Get the channel number and verify it. */ | 549 | /* Get the channel number and verify it. */ |
579 | channel = packet_get_int(); | 550 | channel = packet_get_int(); |
580 | if (channel < 0 || channel >= channels_alloc || | 551 | if (channel < 0 || channel >= channels_alloc || |
581 | channels[channel].type == SSH_CHANNEL_FREE) | 552 | channels[channel].type == SSH_CHANNEL_FREE) |
582 | packet_disconnect("Received data for nonexistent channel %d.", channel); | 553 | packet_disconnect("Received data for nonexistent channel %d.", channel); |
583 | 554 | ||
584 | /* Ignore any data for non-open channels (might happen on close) */ | 555 | /* Ignore any data for non-open channels (might happen on close) */ |
585 | if (channels[channel].type != SSH_CHANNEL_OPEN && | 556 | if (channels[channel].type != SSH_CHANNEL_OPEN && |
586 | channels[channel].type != SSH_CHANNEL_X11_OPEN) | 557 | channels[channel].type != SSH_CHANNEL_X11_OPEN) |
587 | return; | 558 | return; |
588 | 559 | ||
589 | /* Get the data. */ | 560 | /* Get the data. */ |
590 | data = packet_get_string(&data_len); | 561 | data = packet_get_string(&data_len); |
591 | packet_integrity_check(payload_len, 4 + 4+data_len, SSH_MSG_CHANNEL_DATA); | 562 | packet_integrity_check(payload_len, 4 + 4 + data_len, SSH_MSG_CHANNEL_DATA); |
592 | buffer_append(&channels[channel].output, data, data_len); | 563 | buffer_append(&channels[channel].output, data, data_len); |
593 | xfree(data); | 564 | xfree(data); |
594 | } | 565 | } |
595 | 566 | ||
596 | /* Returns true if no channel has too much buffered data, and false if | 567 | /* Returns true if no channel has too much buffered data, and false if |
597 | one or more channel is overfull. */ | 568 | one or more channel is overfull. */ |
598 | 569 | ||
599 | int channel_not_very_much_buffered_data() | 570 | int |
571 | channel_not_very_much_buffered_data() | ||
600 | { | 572 | { |
601 | unsigned int i; | 573 | unsigned int i; |
602 | Channel *ch; | 574 | Channel *ch; |
603 | 575 | ||
604 | for (i = 0; i < channels_alloc; i++) | 576 | for (i = 0; i < channels_alloc; i++) { |
605 | { | 577 | ch = &channels[i]; |
606 | ch = &channels[i]; | 578 | switch (ch->type) { |
607 | switch (ch->type) | 579 | case SSH_CHANNEL_X11_LISTENER: |
608 | { | 580 | case SSH_CHANNEL_PORT_LISTENER: |
609 | case SSH_CHANNEL_X11_LISTENER: | 581 | case SSH_CHANNEL_AUTH_SOCKET: |
610 | case SSH_CHANNEL_PORT_LISTENER: | 582 | continue; |
611 | case SSH_CHANNEL_AUTH_SOCKET: | 583 | case SSH_CHANNEL_OPEN: |
612 | continue; | 584 | if (buffer_len(&ch->input) > packet_get_maxsize()) |
613 | case SSH_CHANNEL_OPEN: | 585 | return 0; |
614 | if (buffer_len(&ch->input) > packet_get_maxsize()) | 586 | if (buffer_len(&ch->output) > packet_get_maxsize()) |
615 | return 0; | 587 | return 0; |
616 | if (buffer_len(&ch->output) > packet_get_maxsize()) | 588 | continue; |
617 | return 0; | 589 | case SSH_CHANNEL_INPUT_DRAINING: |
618 | continue; | 590 | case SSH_CHANNEL_OUTPUT_DRAINING: |
619 | case SSH_CHANNEL_INPUT_DRAINING: | 591 | case SSH_CHANNEL_X11_OPEN: |
620 | case SSH_CHANNEL_OUTPUT_DRAINING: | 592 | case SSH_CHANNEL_FREE: |
621 | case SSH_CHANNEL_X11_OPEN: | 593 | default: |
622 | case SSH_CHANNEL_FREE: | 594 | continue; |
623 | default: | 595 | } |
624 | continue; | ||
625 | } | 596 | } |
626 | } | 597 | return 1; |
627 | return 1; | ||
628 | } | 598 | } |
629 | 599 | ||
630 | /* This is called after receiving CHANNEL_CLOSE/IEOF. */ | 600 | /* This is called after receiving CHANNEL_CLOSE/IEOF. */ |
631 | 601 | ||
632 | void channel_input_close() | 602 | void |
603 | channel_input_close() | ||
633 | { | 604 | { |
634 | int channel; | 605 | int channel; |
635 | 606 | ||
636 | /* Get the channel number and verify it. */ | 607 | /* Get the channel number and verify it. */ |
637 | channel = packet_get_int(); | 608 | channel = packet_get_int(); |
638 | if (channel < 0 || channel >= channels_alloc || | 609 | if (channel < 0 || channel >= channels_alloc || |
639 | channels[channel].type == SSH_CHANNEL_FREE) | 610 | channels[channel].type == SSH_CHANNEL_FREE) |
640 | packet_disconnect("Received data for nonexistent channel %d.", channel); | 611 | packet_disconnect("Received data for nonexistent channel %d.", channel); |
641 | 612 | ||
642 | if(!compat13){ | 613 | if (!compat13) { |
643 | /* proto version 1.5 overloads CLOSE with IEOF */ | 614 | /* proto version 1.5 overloads CLOSE with IEOF */ |
644 | chan_rcvd_ieof(&channels[channel]); | 615 | chan_rcvd_ieof(&channels[channel]); |
645 | return; | 616 | return; |
646 | } | 617 | } |
647 | 618 | /* Send a confirmation that we have closed the channel and no more | |
648 | /* Send a confirmation that we have closed the channel and no more data is | 619 | data is coming for it. */ |
649 | coming for it. */ | 620 | packet_start(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION); |
650 | packet_start(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION); | 621 | packet_put_int(channels[channel].remote_id); |
651 | packet_put_int(channels[channel].remote_id); | 622 | packet_send(); |
652 | packet_send(); | 623 | |
653 | 624 | /* If the channel is in closed state, we have sent a close | |
654 | /* If the channel is in closed state, we have sent a close request, and | 625 | request, and the other side will eventually respond with a |
655 | the other side will eventually respond with a confirmation. Thus, | 626 | confirmation. Thus, we cannot free the channel here, because |
656 | we cannot free the channel here, because then there would be no-one to | 627 | then there would be no-one to receive the confirmation. The |
657 | receive the confirmation. The channel gets freed when the confirmation | 628 | channel gets freed when the confirmation arrives. */ |
658 | arrives. */ | 629 | if (channels[channel].type != SSH_CHANNEL_CLOSED) { |
659 | if (channels[channel].type != SSH_CHANNEL_CLOSED) | 630 | /* Not a closed channel - mark it as draining, which will |
660 | { | 631 | cause it to be freed later. */ |
661 | /* Not a closed channel - mark it as draining, which will cause it to | 632 | buffer_consume(&channels[channel].input, |
662 | be freed later. */ | 633 | buffer_len(&channels[channel].input)); |
663 | buffer_consume(&channels[channel].input, | 634 | channels[channel].type = SSH_CHANNEL_OUTPUT_DRAINING; |
664 | buffer_len(&channels[channel].input)); | 635 | } |
665 | channels[channel].type = SSH_CHANNEL_OUTPUT_DRAINING; | ||
666 | /* debug("Setting status to output draining; output len = %d", | ||
667 | buffer_len(&channels[channel].output)); */ | ||
668 | } | ||
669 | } | 636 | } |
670 | 637 | ||
671 | /* This is called after receiving CHANNEL_CLOSE_CONFIRMATION/OCLOSE. */ | 638 | /* This is called after receiving CHANNEL_CLOSE_CONFIRMATION/OCLOSE. */ |
672 | 639 | ||
673 | void channel_input_close_confirmation() | 640 | void |
641 | channel_input_close_confirmation() | ||
674 | { | 642 | { |
675 | int channel; | 643 | int channel; |
676 | 644 | ||
677 | /* Get the channel number and verify it. */ | 645 | /* Get the channel number and verify it. */ |
678 | channel = packet_get_int(); | 646 | channel = packet_get_int(); |
679 | if (channel < 0 || channel >= channels_alloc) | 647 | if (channel < 0 || channel >= channels_alloc) |
680 | packet_disconnect("Received close confirmation for out-of-range channel %d.", | 648 | packet_disconnect("Received close confirmation for out-of-range channel %d.", |
681 | channel); | 649 | channel); |
682 | 650 | ||
683 | if(!compat13){ | 651 | if (!compat13) { |
684 | /* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */ | 652 | /* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */ |
685 | chan_rcvd_oclose(&channels[channel]); | 653 | chan_rcvd_oclose(&channels[channel]); |
686 | return; | 654 | return; |
687 | } | 655 | } |
688 | 656 | if (channels[channel].type != SSH_CHANNEL_CLOSED) | |
689 | if (channels[channel].type != SSH_CHANNEL_CLOSED) | 657 | packet_disconnect("Received close confirmation for non-closed channel %d (type %d).", |
690 | packet_disconnect("Received close confirmation for non-closed channel %d (type %d).", | 658 | channel, channels[channel].type); |
691 | channel, channels[channel].type); | 659 | |
692 | 660 | /* Free the channel. */ | |
693 | /* Free the channel. */ | 661 | channel_free(channel); |
694 | channel_free(channel); | ||
695 | } | 662 | } |
696 | 663 | ||
697 | /* This is called after receiving CHANNEL_OPEN_CONFIRMATION. */ | 664 | /* This is called after receiving CHANNEL_OPEN_CONFIRMATION. */ |
698 | 665 | ||
699 | void channel_input_open_confirmation() | 666 | void |
667 | channel_input_open_confirmation() | ||
700 | { | 668 | { |
701 | int channel, remote_channel; | 669 | int channel, remote_channel; |
702 | 670 | ||
703 | /* Get the channel number and verify it. */ | 671 | /* Get the channel number and verify it. */ |
704 | channel = packet_get_int(); | 672 | channel = packet_get_int(); |
705 | if (channel < 0 || channel >= channels_alloc || | 673 | if (channel < 0 || channel >= channels_alloc || |
706 | channels[channel].type != SSH_CHANNEL_OPENING) | 674 | channels[channel].type != SSH_CHANNEL_OPENING) |
707 | packet_disconnect("Received open confirmation for non-opening channel %d.", | 675 | packet_disconnect("Received open confirmation for non-opening channel %d.", |
708 | channel); | 676 | channel); |
709 | 677 | ||
710 | /* Get remote side's id for this channel. */ | 678 | /* Get remote side's id for this channel. */ |
711 | remote_channel = packet_get_int(); | 679 | remote_channel = packet_get_int(); |
712 | 680 | ||
713 | /* Record the remote channel number and mark that the channel is now open. */ | 681 | /* Record the remote channel number and mark that the channel is |
714 | channels[channel].remote_id = remote_channel; | 682 | now open. */ |
715 | channels[channel].type = SSH_CHANNEL_OPEN; | 683 | channels[channel].remote_id = remote_channel; |
684 | channels[channel].type = SSH_CHANNEL_OPEN; | ||
716 | } | 685 | } |
717 | 686 | ||
718 | /* This is called after receiving CHANNEL_OPEN_FAILURE from the other side. */ | 687 | /* This is called after receiving CHANNEL_OPEN_FAILURE from the other side. */ |
719 | 688 | ||
720 | void channel_input_open_failure() | 689 | void |
690 | channel_input_open_failure() | ||
721 | { | 691 | { |
722 | int channel; | 692 | int channel; |
723 | 693 | ||
724 | /* Get the channel number and verify it. */ | 694 | /* Get the channel number and verify it. */ |
725 | channel = packet_get_int(); | 695 | channel = packet_get_int(); |
726 | if (channel < 0 || channel >= channels_alloc || | 696 | if (channel < 0 || channel >= channels_alloc || |
727 | channels[channel].type != SSH_CHANNEL_OPENING) | 697 | channels[channel].type != SSH_CHANNEL_OPENING) |
728 | packet_disconnect("Received open failure for non-opening channel %d.", | 698 | packet_disconnect("Received open failure for non-opening channel %d.", |
729 | channel); | 699 | channel); |
730 | 700 | ||
731 | /* Free the channel. This will also close the socket. */ | 701 | /* Free the channel. This will also close the socket. */ |
732 | channel_free(channel); | 702 | channel_free(channel); |
733 | } | 703 | } |
734 | 704 | ||
735 | /* Stops listening for channels, and removes any unix domain sockets that | 705 | /* Stops listening for channels, and removes any unix domain sockets that |
736 | we might have. */ | 706 | we might have. */ |
737 | 707 | ||
738 | void channel_stop_listening() | 708 | void |
709 | channel_stop_listening() | ||
739 | { | 710 | { |
740 | int i; | 711 | int i; |
741 | for (i = 0; i < channels_alloc; i++) | 712 | for (i = 0; i < channels_alloc; i++) { |
742 | { | 713 | switch (channels[i].type) { |
743 | switch (channels[i].type) | 714 | case SSH_CHANNEL_AUTH_SOCKET: |
744 | { | 715 | close(channels[i].sock); |
745 | case SSH_CHANNEL_AUTH_SOCKET: | 716 | remove(channels[i].path); |
746 | close(channels[i].sock); | 717 | channel_free(i); |
747 | remove(channels[i].path); | 718 | break; |
748 | channel_free(i); | 719 | case SSH_CHANNEL_PORT_LISTENER: |
749 | break; | 720 | case SSH_CHANNEL_X11_LISTENER: |
750 | case SSH_CHANNEL_PORT_LISTENER: | 721 | close(channels[i].sock); |
751 | case SSH_CHANNEL_X11_LISTENER: | 722 | channel_free(i); |
752 | close(channels[i].sock); | 723 | break; |
753 | channel_free(i); | 724 | default: |
754 | break; | 725 | break; |
755 | default: | 726 | } |
756 | break; | ||
757 | } | 727 | } |
758 | } | ||
759 | } | 728 | } |
760 | 729 | ||
761 | /* Closes the sockets of all channels. This is used to close extra file | 730 | /* Closes the sockets of all channels. This is used to close extra file |
762 | descriptors after a fork. */ | 731 | descriptors after a fork. */ |
763 | 732 | ||
764 | void channel_close_all() | 733 | void |
734 | channel_close_all() | ||
765 | { | 735 | { |
766 | int i; | 736 | int i; |
767 | for (i = 0; i < channels_alloc; i++) | 737 | for (i = 0; i < channels_alloc; i++) { |
768 | { | 738 | if (channels[i].type != SSH_CHANNEL_FREE) |
769 | if (channels[i].type != SSH_CHANNEL_FREE) | 739 | close(channels[i].sock); |
770 | close(channels[i].sock); | 740 | } |
771 | } | ||
772 | } | 741 | } |
773 | 742 | ||
774 | /* Returns the maximum file descriptor number used by the channels. */ | 743 | /* Returns the maximum file descriptor number used by the channels. */ |
775 | 744 | ||
776 | int channel_max_fd() | 745 | int |
746 | channel_max_fd() | ||
777 | { | 747 | { |
778 | return channel_max_fd_value; | 748 | return channel_max_fd_value; |
779 | } | 749 | } |
780 | 750 | ||
781 | /* Returns true if any channel is still open. */ | 751 | /* Returns true if any channel is still open. */ |
782 | 752 | ||
783 | int channel_still_open() | 753 | int |
754 | channel_still_open() | ||
784 | { | 755 | { |
785 | unsigned int i; | 756 | unsigned int i; |
786 | for (i = 0; i < channels_alloc; i++) | 757 | for (i = 0; i < channels_alloc; i++) |
787 | switch (channels[i].type) | 758 | switch (channels[i].type) { |
788 | { | 759 | case SSH_CHANNEL_FREE: |
789 | case SSH_CHANNEL_FREE: | 760 | case SSH_CHANNEL_X11_LISTENER: |
790 | case SSH_CHANNEL_X11_LISTENER: | 761 | case SSH_CHANNEL_PORT_LISTENER: |
791 | case SSH_CHANNEL_PORT_LISTENER: | 762 | case SSH_CHANNEL_CLOSED: |
792 | case SSH_CHANNEL_CLOSED: | 763 | case SSH_CHANNEL_AUTH_SOCKET: |
793 | case SSH_CHANNEL_AUTH_SOCKET: | 764 | continue; |
794 | continue; | 765 | case SSH_CHANNEL_OPENING: |
795 | case SSH_CHANNEL_OPENING: | 766 | case SSH_CHANNEL_OPEN: |
796 | case SSH_CHANNEL_OPEN: | 767 | case SSH_CHANNEL_X11_OPEN: |
797 | case SSH_CHANNEL_X11_OPEN: | 768 | return 1; |
798 | return 1; | 769 | case SSH_CHANNEL_INPUT_DRAINING: |
799 | case SSH_CHANNEL_INPUT_DRAINING: | 770 | case SSH_CHANNEL_OUTPUT_DRAINING: |
800 | case SSH_CHANNEL_OUTPUT_DRAINING: | 771 | if (!compat13) |
801 | if (!compat13) | 772 | fatal("cannot happen: OUT_DRAIN"); |
802 | fatal("cannot happen: OUT_DRAIN"); | 773 | return 1; |
803 | return 1; | 774 | default: |
804 | default: | 775 | fatal("channel_still_open: bad channel type %d", channels[i].type); |
805 | fatal("channel_still_open: bad channel type %d", channels[i].type); | 776 | /* NOTREACHED */ |
806 | /*NOTREACHED*/ | 777 | } |
807 | } | 778 | return 0; |
808 | return 0; | ||
809 | } | 779 | } |
810 | 780 | ||
811 | /* Returns a message describing the currently open forwarded | 781 | /* Returns a message describing the currently open forwarded |
812 | connections, suitable for sending to the client. The message | 782 | connections, suitable for sending to the client. The message |
813 | contains crlf pairs for newlines. */ | 783 | contains crlf pairs for newlines. */ |
814 | 784 | ||
815 | char *channel_open_message() | 785 | char * |
786 | channel_open_message() | ||
816 | { | 787 | { |
817 | Buffer buffer; | 788 | Buffer buffer; |
818 | int i; | 789 | int i; |
819 | char buf[512], *cp; | 790 | char buf[512], *cp; |
820 | 791 | ||
821 | buffer_init(&buffer); | 792 | buffer_init(&buffer); |
822 | snprintf(buf, sizeof buf, "The following connections are open:\r\n"); | 793 | snprintf(buf, sizeof buf, "The following connections are open:\r\n"); |
823 | buffer_append(&buffer, buf, strlen(buf)); | ||
824 | for (i = 0; i < channels_alloc; i++){ | ||
825 | Channel *c=&channels[i]; | ||
826 | switch (c->type) | ||
827 | { | ||
828 | case SSH_CHANNEL_FREE: | ||
829 | case SSH_CHANNEL_X11_LISTENER: | ||
830 | case SSH_CHANNEL_PORT_LISTENER: | ||
831 | case SSH_CHANNEL_CLOSED: | ||
832 | case SSH_CHANNEL_AUTH_SOCKET: | ||
833 | continue; | ||
834 | case SSH_CHANNEL_OPENING: | ||
835 | case SSH_CHANNEL_OPEN: | ||
836 | case SSH_CHANNEL_X11_OPEN: | ||
837 | case SSH_CHANNEL_INPUT_DRAINING: | ||
838 | case SSH_CHANNEL_OUTPUT_DRAINING: | ||
839 | snprintf(buf, sizeof buf, " #%d %.300s (t%d r%d i%d o%d)\r\n", | ||
840 | c->self,c->remote_name, | ||
841 | c->type,c->remote_id, c->istate,c->ostate); | ||
842 | buffer_append(&buffer, buf, strlen(buf)); | 794 | buffer_append(&buffer, buf, strlen(buf)); |
843 | continue; | 795 | for (i = 0; i < channels_alloc; i++) { |
844 | default: | 796 | Channel *c = &channels[i]; |
845 | fatal("channel_still_open: bad channel type %d", c->type); | 797 | switch (c->type) { |
846 | /*NOTREACHED*/ | 798 | case SSH_CHANNEL_FREE: |
847 | } | 799 | case SSH_CHANNEL_X11_LISTENER: |
848 | } | 800 | case SSH_CHANNEL_PORT_LISTENER: |
849 | buffer_append(&buffer, "\0", 1); | 801 | case SSH_CHANNEL_CLOSED: |
850 | cp = xstrdup(buffer_ptr(&buffer)); | 802 | case SSH_CHANNEL_AUTH_SOCKET: |
851 | buffer_free(&buffer); | 803 | continue; |
852 | return cp; | 804 | case SSH_CHANNEL_OPENING: |
805 | case SSH_CHANNEL_OPEN: | ||
806 | case SSH_CHANNEL_X11_OPEN: | ||
807 | case SSH_CHANNEL_INPUT_DRAINING: | ||
808 | case SSH_CHANNEL_OUTPUT_DRAINING: | ||
809 | snprintf(buf, sizeof buf, " #%d %.300s (t%d r%d i%d o%d)\r\n", | ||
810 | c->self, c->remote_name, | ||
811 | c->type, c->remote_id, c->istate, c->ostate); | ||
812 | buffer_append(&buffer, buf, strlen(buf)); | ||
813 | continue; | ||
814 | default: | ||
815 | fatal("channel_still_open: bad channel type %d", c->type); | ||
816 | /* NOTREACHED */ | ||
817 | } | ||
818 | } | ||
819 | buffer_append(&buffer, "\0", 1); | ||
820 | cp = xstrdup(buffer_ptr(&buffer)); | ||
821 | buffer_free(&buffer); | ||
822 | return cp; | ||
853 | } | 823 | } |
854 | 824 | ||
855 | /* Initiate forwarding of connections to local port "port" through the secure | 825 | /* Initiate forwarding of connections to local port "port" through the secure |
856 | channel to host:port from remote side. */ | 826 | channel to host:port from remote side. */ |
857 | 827 | ||
858 | void channel_request_local_forwarding(int port, const char *host, | 828 | void |
859 | int host_port) | 829 | channel_request_local_forwarding(int port, const char *host, |
830 | int host_port) | ||
860 | { | 831 | { |
861 | int ch, sock; | 832 | int ch, sock; |
862 | struct sockaddr_in sin; | 833 | struct sockaddr_in sin; |
863 | extern Options options; | 834 | extern Options options; |
864 | 835 | ||
865 | if (strlen(host) > sizeof(channels[0].path) - 1) | 836 | if (strlen(host) > sizeof(channels[0].path) - 1) |
866 | packet_disconnect("Forward host name too long."); | 837 | packet_disconnect("Forward host name too long."); |
867 | 838 | ||
868 | /* Create a port to listen for the host. */ | 839 | /* Create a port to listen for the host. */ |
869 | sock = socket(AF_INET, SOCK_STREAM, 0); | 840 | sock = socket(AF_INET, SOCK_STREAM, 0); |
870 | if (sock < 0) | 841 | if (sock < 0) |
871 | packet_disconnect("socket: %.100s", strerror(errno)); | 842 | packet_disconnect("socket: %.100s", strerror(errno)); |
872 | 843 | ||
873 | /* Initialize socket address. */ | 844 | /* Initialize socket address. */ |
874 | memset(&sin, 0, sizeof(sin)); | 845 | memset(&sin, 0, sizeof(sin)); |
875 | sin.sin_family = AF_INET; | 846 | sin.sin_family = AF_INET; |
876 | if (options.gateway_ports == 1) | 847 | if (options.gateway_ports == 1) |
877 | sin.sin_addr.s_addr = htonl(INADDR_ANY); | 848 | sin.sin_addr.s_addr = htonl(INADDR_ANY); |
878 | else | 849 | else |
879 | sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); | 850 | sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); |
880 | sin.sin_port = htons(port); | 851 | sin.sin_port = htons(port); |
881 | 852 | ||
882 | /* Bind the socket to the address. */ | 853 | /* Bind the socket to the address. */ |
883 | if (bind(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) | 854 | if (bind(sock, (struct sockaddr *) & sin, sizeof(sin)) < 0) |
884 | packet_disconnect("bind: %.100s", strerror(errno)); | 855 | packet_disconnect("bind: %.100s", strerror(errno)); |
885 | 856 | ||
886 | /* Start listening for connections on the socket. */ | 857 | /* Start listening for connections on the socket. */ |
887 | if (listen(sock, 5) < 0) | 858 | if (listen(sock, 5) < 0) |
888 | packet_disconnect("listen: %.100s", strerror(errno)); | 859 | packet_disconnect("listen: %.100s", strerror(errno)); |
889 | 860 | ||
890 | /* Allocate a channel number for the socket. */ | 861 | /* Allocate a channel number for the socket. */ |
891 | ch = channel_allocate(SSH_CHANNEL_PORT_LISTENER, sock, | 862 | ch = channel_allocate(SSH_CHANNEL_PORT_LISTENER, sock, |
892 | xstrdup("port listener")); | 863 | xstrdup("port listener")); |
893 | strcpy(channels[ch].path, host); /* note: host name stored here */ | 864 | strcpy(channels[ch].path, host); |
894 | channels[ch].host_port = host_port; /* port on host to connect to */ | 865 | channels[ch].host_port = host_port; |
895 | channels[ch].listening_port = port; /* port being listened */ | 866 | channels[ch].listening_port = port; |
896 | } | 867 | } |
897 | 868 | ||
898 | /* Initiate forwarding of connections to port "port" on remote host through | 869 | /* Initiate forwarding of connections to port "port" on remote host through |
899 | the secure channel to host:port from local side. */ | 870 | the secure channel to host:port from local side. */ |
900 | 871 | ||
901 | void channel_request_remote_forwarding(int port, const char *host, | 872 | void |
902 | int remote_port) | 873 | channel_request_remote_forwarding(int port, const char *host, |
874 | int remote_port) | ||
903 | { | 875 | { |
904 | int payload_len; | 876 | int payload_len; |
905 | /* Record locally that connection to this host/port is permitted. */ | 877 | /* Record locally that connection to this host/port is permitted. */ |
906 | if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION) | 878 | if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION) |
907 | fatal("channel_request_remote_forwarding: too many forwards"); | 879 | fatal("channel_request_remote_forwarding: too many forwards"); |
908 | permitted_opens[num_permitted_opens].host = xstrdup(host); | 880 | |
909 | permitted_opens[num_permitted_opens].port = remote_port; | 881 | permitted_opens[num_permitted_opens].host = xstrdup(host); |
910 | num_permitted_opens++; | 882 | permitted_opens[num_permitted_opens].port = remote_port; |
911 | 883 | num_permitted_opens++; | |
912 | /* Send the forward request to the remote side. */ | 884 | |
913 | packet_start(SSH_CMSG_PORT_FORWARD_REQUEST); | 885 | /* Send the forward request to the remote side. */ |
914 | packet_put_int(port); | 886 | packet_start(SSH_CMSG_PORT_FORWARD_REQUEST); |
915 | packet_put_string(host, strlen(host)); | 887 | packet_put_int(port); |
916 | packet_put_int(remote_port); | 888 | packet_put_string(host, strlen(host)); |
917 | packet_send(); | 889 | packet_put_int(remote_port); |
918 | packet_write_wait(); | 890 | packet_send(); |
919 | 891 | packet_write_wait(); | |
920 | /* Wait for response from the remote side. It will send a disconnect | 892 | |
921 | message on failure, and we will never see it here. */ | 893 | /* Wait for response from the remote side. It will send a |
922 | packet_read_expect(&payload_len, SSH_SMSG_SUCCESS); | 894 | disconnect message on failure, and we will never see it here. */ |
895 | packet_read_expect(&payload_len, SSH_SMSG_SUCCESS); | ||
923 | } | 896 | } |
924 | 897 | ||
925 | /* This is called after receiving CHANNEL_FORWARDING_REQUEST. This initates | 898 | /* This is called after receiving CHANNEL_FORWARDING_REQUEST. This initates |
926 | listening for the port, and sends back a success reply (or disconnect | 899 | listening for the port, and sends back a success reply (or disconnect |
927 | message if there was an error). This never returns if there was an | 900 | message if there was an error). This never returns if there was an |
928 | error. */ | 901 | error. */ |
929 | 902 | ||
930 | void channel_input_port_forward_request(int is_root) | 903 | void |
904 | channel_input_port_forward_request(int is_root) | ||
931 | { | 905 | { |
932 | int port, host_port; | 906 | int port, host_port; |
933 | char *hostname; | 907 | char *hostname; |
934 | 908 | ||
935 | /* Get arguments from the packet. */ | 909 | /* Get arguments from the packet. */ |
936 | port = packet_get_int(); | 910 | port = packet_get_int(); |
937 | hostname = packet_get_string(NULL); | 911 | hostname = packet_get_string(NULL); |
938 | host_port = packet_get_int(); | 912 | host_port = packet_get_int(); |
939 | 913 | ||
940 | /* Port numbers are 16 bit quantities. */ | 914 | /* Port numbers are 16 bit quantities. */ |
941 | if ((port & 0xffff) != port) | 915 | if ((port & 0xffff) != port) |
942 | packet_disconnect("Requested forwarding of nonexistent port %d.", port); | 916 | packet_disconnect("Requested forwarding of nonexistent port %d.", port); |
943 | 917 | ||
944 | /* Check that an unprivileged user is not trying to forward a privileged | 918 | /* Check that an unprivileged user is not trying to forward a |
945 | port. */ | 919 | privileged port. */ |
946 | if (port < IPPORT_RESERVED && !is_root) | 920 | if (port < IPPORT_RESERVED && !is_root) |
947 | packet_disconnect("Requested forwarding of port %d but user is not root.", | 921 | packet_disconnect("Requested forwarding of port %d but user is not root.", |
948 | port); | 922 | port); |
949 | 923 | ||
950 | /* Initiate forwarding. */ | 924 | /* Initiate forwarding. */ |
951 | channel_request_local_forwarding(port, hostname, host_port); | 925 | channel_request_local_forwarding(port, hostname, host_port); |
952 | 926 | ||
953 | /* Free the argument string. */ | 927 | /* Free the argument string. */ |
954 | xfree(hostname); | 928 | xfree(hostname); |
955 | } | 929 | } |
956 | 930 | ||
957 | /* This is called after receiving PORT_OPEN message. This attempts to connect | 931 | /* This is called after receiving PORT_OPEN message. This attempts to connect |
958 | to the given host:port, and sends back CHANNEL_OPEN_CONFIRMATION or | 932 | to the given host:port, and sends back CHANNEL_OPEN_CONFIRMATION or |
959 | CHANNEL_OPEN_FAILURE. */ | 933 | CHANNEL_OPEN_FAILURE. */ |
960 | 934 | ||
961 | void channel_input_port_open(int payload_len) | 935 | void |
936 | channel_input_port_open(int payload_len) | ||
962 | { | 937 | { |
963 | int remote_channel, sock, newch, host_port, i; | 938 | int remote_channel, sock, newch, host_port, i; |
964 | struct sockaddr_in sin; | 939 | struct sockaddr_in sin; |
965 | char *host, *originator_string; | 940 | char *host, *originator_string; |
966 | struct hostent *hp; | 941 | struct hostent *hp; |
967 | int host_len, originator_len; | 942 | int host_len, originator_len; |
968 | 943 | ||
969 | /* Get remote channel number. */ | 944 | /* Get remote channel number. */ |
970 | remote_channel = packet_get_int(); | 945 | remote_channel = packet_get_int(); |
971 | 946 | ||
972 | /* Get host name to connect to. */ | 947 | /* Get host name to connect to. */ |
973 | host = packet_get_string(&host_len); | 948 | host = packet_get_string(&host_len); |
974 | 949 | ||
975 | /* Get port to connect to. */ | 950 | /* Get port to connect to. */ |
976 | host_port = packet_get_int(); | 951 | host_port = packet_get_int(); |
977 | 952 | ||
978 | /* Get remote originator name. */ | 953 | /* Get remote originator name. */ |
979 | if (have_hostname_in_open) | 954 | if (have_hostname_in_open) |
980 | originator_string = packet_get_string(&originator_len); | 955 | originator_string = packet_get_string(&originator_len); |
981 | else | 956 | else |
982 | originator_string = xstrdup("unknown (remote did not supply name)"); | 957 | originator_string = xstrdup("unknown (remote did not supply name)"); |
983 | 958 | ||
984 | packet_integrity_check(payload_len, | 959 | packet_integrity_check(payload_len, |
985 | 4 + 4 + host_len + 4 + 4 + originator_len, | 960 | 4 + 4 + host_len + 4 + 4 + originator_len, |
986 | SSH_MSG_PORT_OPEN); | 961 | SSH_MSG_PORT_OPEN); |
987 | 962 | ||
988 | /* Check if opening that port is permitted. */ | 963 | /* Check if opening that port is permitted. */ |
989 | if (!all_opens_permitted) | 964 | if (!all_opens_permitted) { |
990 | { | 965 | /* Go trough all permitted ports. */ |
991 | /* Go trough all permitted ports. */ | 966 | for (i = 0; i < num_permitted_opens; i++) |
992 | for (i = 0; i < num_permitted_opens; i++) | 967 | if (permitted_opens[i].port == host_port && |
993 | if (permitted_opens[i].port == host_port && | 968 | strcmp(permitted_opens[i].host, host) == 0) |
994 | strcmp(permitted_opens[i].host, host) == 0) | 969 | break; |
995 | break; | 970 | |
996 | 971 | /* Check if we found the requested port among those permitted. */ | |
997 | /* Check if we found the requested port among those permitted. */ | 972 | if (i >= num_permitted_opens) { |
998 | if (i >= num_permitted_opens) | 973 | /* The port is not permitted. */ |
999 | { | 974 | log("Received request to connect to %.100s:%d, but the request was denied.", |
1000 | /* The port is not permitted. */ | 975 | host, host_port); |
1001 | log("Received request to connect to %.100s:%d, but the request was denied.", | 976 | packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); |
1002 | host, host_port); | 977 | packet_put_int(remote_channel); |
1003 | packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); | 978 | packet_send(); |
1004 | packet_put_int(remote_channel); | 979 | } |
1005 | packet_send(); | ||
1006 | } | 980 | } |
1007 | } | 981 | memset(&sin, 0, sizeof(sin)); |
1008 | 982 | sin.sin_addr.s_addr = inet_addr(host); | |
1009 | memset(&sin, 0, sizeof(sin)); | 983 | if ((sin.sin_addr.s_addr & 0xffffffff) != 0xffffffff) { |
1010 | sin.sin_addr.s_addr = inet_addr(host); | 984 | /* It was a valid numeric host address. */ |
1011 | if ((sin.sin_addr.s_addr & 0xffffffff) != 0xffffffff) | 985 | sin.sin_family = AF_INET; |
1012 | { | 986 | } else { |
1013 | /* It was a valid numeric host address. */ | 987 | /* Look up the host address from the name servers. */ |
1014 | sin.sin_family = AF_INET; | 988 | hp = gethostbyname(host); |
1015 | } | 989 | if (!hp) { |
1016 | else | 990 | error("%.100s: unknown host.", host); |
1017 | { | 991 | goto fail; |
1018 | /* Look up the host address from the name servers. */ | 992 | } |
1019 | hp = gethostbyname(host); | 993 | if (!hp->h_addr_list[0]) { |
1020 | if (!hp) | 994 | error("%.100s: host has no IP address.", host); |
1021 | { | 995 | goto fail; |
1022 | error("%.100s: unknown host.", host); | 996 | } |
1023 | goto fail; | 997 | sin.sin_family = hp->h_addrtype; |
998 | memcpy(&sin.sin_addr, hp->h_addr_list[0], | ||
999 | sizeof(sin.sin_addr)); | ||
1024 | } | 1000 | } |
1025 | if (!hp->h_addr_list[0]) | 1001 | sin.sin_port = htons(host_port); |
1026 | { | 1002 | |
1027 | error("%.100s: host has no IP address.", host); | 1003 | /* Create the socket. */ |
1028 | goto fail; | 1004 | sock = socket(sin.sin_family, SOCK_STREAM, 0); |
1005 | if (sock < 0) { | ||
1006 | error("socket: %.100s", strerror(errno)); | ||
1007 | goto fail; | ||
1008 | } | ||
1009 | /* Connect to the host/port. */ | ||
1010 | if (connect(sock, (struct sockaddr *) & sin, sizeof(sin)) < 0) { | ||
1011 | error("connect %.100s:%d: %.100s", host, host_port, | ||
1012 | strerror(errno)); | ||
1013 | close(sock); | ||
1014 | goto fail; | ||
1029 | } | 1015 | } |
1030 | sin.sin_family = hp->h_addrtype; | 1016 | /* Successful connection. */ |
1031 | memcpy(&sin.sin_addr, hp->h_addr_list[0], | 1017 | |
1032 | sizeof(sin.sin_addr)); | 1018 | /* Allocate a channel for this connection. */ |
1033 | } | 1019 | newch = channel_allocate(SSH_CHANNEL_OPEN, sock, originator_string); |
1034 | sin.sin_port = htons(host_port); | 1020 | channels[newch].remote_id = remote_channel; |
1035 | 1021 | ||
1036 | /* Create the socket. */ | 1022 | /* Send a confirmation to the remote host. */ |
1037 | sock = socket(sin.sin_family, SOCK_STREAM, 0); | 1023 | packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); |
1038 | if (sock < 0) | 1024 | packet_put_int(remote_channel); |
1039 | { | 1025 | packet_put_int(newch); |
1040 | error("socket: %.100s", strerror(errno)); | 1026 | packet_send(); |
1041 | goto fail; | 1027 | |
1042 | } | 1028 | /* Free the argument string. */ |
1043 | 1029 | xfree(host); | |
1044 | /* Connect to the host/port. */ | 1030 | |
1045 | if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) | 1031 | return; |
1046 | { | 1032 | |
1047 | error("connect %.100s:%d: %.100s", host, host_port, | 1033 | fail: |
1048 | strerror(errno)); | 1034 | /* Free the argument string. */ |
1049 | close(sock); | 1035 | xfree(host); |
1050 | goto fail; | 1036 | |
1051 | } | 1037 | /* Send refusal to the remote host. */ |
1052 | 1038 | packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); | |
1053 | /* Successful connection. */ | 1039 | packet_put_int(remote_channel); |
1054 | 1040 | packet_send(); | |
1055 | /* Allocate a channel for this connection. */ | ||
1056 | newch = channel_allocate(SSH_CHANNEL_OPEN, sock, originator_string); | ||
1057 | channels[newch].remote_id = remote_channel; | ||
1058 | |||
1059 | /* Send a confirmation to the remote host. */ | ||
1060 | packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); | ||
1061 | packet_put_int(remote_channel); | ||
1062 | packet_put_int(newch); | ||
1063 | packet_send(); | ||
1064 | |||
1065 | /* Free the argument string. */ | ||
1066 | xfree(host); | ||
1067 | |||
1068 | return; | ||
1069 | |||
1070 | fail: | ||
1071 | /* Free the argument string. */ | ||
1072 | xfree(host); | ||
1073 | |||
1074 | /* Send refusal to the remote host. */ | ||
1075 | packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); | ||
1076 | packet_put_int(remote_channel); | ||
1077 | packet_send(); | ||
1078 | } | 1041 | } |
1079 | 1042 | ||
1080 | /* Creates an internet domain socket for listening for X11 connections. | 1043 | /* Creates an internet domain socket for listening for X11 connections. |
1081 | Returns a suitable value for the DISPLAY variable, or NULL if an error | 1044 | Returns a suitable value for the DISPLAY variable, or NULL if an error |
1082 | occurs. */ | 1045 | occurs. */ |
1083 | 1046 | ||
1084 | char *x11_create_display_inet(int screen_number) | 1047 | char * |
1048 | x11_create_display_inet(int screen_number) | ||
1085 | { | 1049 | { |
1086 | extern ServerOptions options; | 1050 | extern ServerOptions options; |
1087 | int display_number, port, sock; | 1051 | int display_number, port, sock; |
1088 | struct sockaddr_in sin; | 1052 | struct sockaddr_in sin; |
1089 | char buf[512]; | 1053 | char buf[512]; |
1090 | char hostname[MAXHOSTNAMELEN]; | 1054 | char hostname[MAXHOSTNAMELEN]; |
1091 | 1055 | ||
1092 | for (display_number = options.x11_display_offset; display_number < MAX_DISPLAYS; display_number++) | 1056 | for (display_number = options.x11_display_offset; |
1093 | { | 1057 | display_number < MAX_DISPLAYS; |
1094 | port = 6000 + display_number; | 1058 | display_number++) { |
1095 | memset(&sin, 0, sizeof(sin)); | 1059 | port = 6000 + display_number; |
1096 | sin.sin_family = AF_INET; | 1060 | memset(&sin, 0, sizeof(sin)); |
1097 | sin.sin_addr.s_addr = htonl(INADDR_ANY); | 1061 | sin.sin_family = AF_INET; |
1098 | sin.sin_port = htons(port); | 1062 | sin.sin_addr.s_addr = htonl(INADDR_ANY); |
1099 | 1063 | sin.sin_port = htons(port); | |
1100 | sock = socket(AF_INET, SOCK_STREAM, 0); | 1064 | |
1101 | if (sock < 0) | 1065 | sock = socket(AF_INET, SOCK_STREAM, 0); |
1102 | { | 1066 | if (sock < 0) { |
1103 | error("socket: %.100s", strerror(errno)); | 1067 | error("socket: %.100s", strerror(errno)); |
1104 | return NULL; | 1068 | return NULL; |
1069 | } | ||
1070 | if (bind(sock, (struct sockaddr *) & sin, sizeof(sin)) < 0) { | ||
1071 | debug("bind port %d: %.100s", port, strerror(errno)); | ||
1072 | shutdown(sock, SHUT_RDWR); | ||
1073 | close(sock); | ||
1074 | continue; | ||
1075 | } | ||
1076 | break; | ||
1077 | } | ||
1078 | if (display_number >= MAX_DISPLAYS) { | ||
1079 | error("Failed to allocate internet-domain X11 display socket."); | ||
1080 | return NULL; | ||
1105 | } | 1081 | } |
1106 | 1082 | /* Start listening for connections on the socket. */ | |
1107 | if (bind(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) | 1083 | if (listen(sock, 5) < 0) { |
1108 | { | 1084 | error("listen: %.100s", strerror(errno)); |
1109 | debug("bind port %d: %.100s", port, strerror(errno)); | 1085 | shutdown(sock, SHUT_RDWR); |
1110 | shutdown(sock, SHUT_RDWR); | 1086 | close(sock); |
1111 | close(sock); | 1087 | return NULL; |
1112 | continue; | ||
1113 | } | 1088 | } |
1114 | break; | 1089 | /* Set up a suitable value for the DISPLAY variable. */ |
1115 | } | 1090 | if (gethostname(hostname, sizeof(hostname)) < 0) |
1116 | if (display_number >= MAX_DISPLAYS) | 1091 | fatal("gethostname: %.100s", strerror(errno)); |
1117 | { | 1092 | snprintf(buf, sizeof buf, "%.400s:%d.%d", hostname, |
1118 | error("Failed to allocate internet-domain X11 display socket."); | 1093 | display_number, screen_number); |
1119 | return NULL; | 1094 | |
1120 | } | 1095 | /* Allocate a channel for the socket. */ |
1121 | 1096 | (void) channel_allocate(SSH_CHANNEL_X11_LISTENER, sock, | |
1122 | /* Start listening for connections on the socket. */ | 1097 | xstrdup("X11 inet listener")); |
1123 | if (listen(sock, 5) < 0) | 1098 | |
1124 | { | 1099 | /* Return a suitable value for the DISPLAY environment variable. */ |
1125 | error("listen: %.100s", strerror(errno)); | 1100 | return xstrdup(buf); |
1126 | shutdown(sock, SHUT_RDWR); | ||
1127 | close(sock); | ||
1128 | return NULL; | ||
1129 | } | ||
1130 | |||
1131 | /* Set up a suitable value for the DISPLAY variable. */ | ||
1132 | if (gethostname(hostname, sizeof(hostname)) < 0) | ||
1133 | fatal("gethostname: %.100s", strerror(errno)); | ||
1134 | snprintf(buf, sizeof buf, "%.400s:%d.%d", hostname, | ||
1135 | display_number, screen_number); | ||
1136 | |||
1137 | /* Allocate a channel for the socket. */ | ||
1138 | (void)channel_allocate(SSH_CHANNEL_X11_LISTENER, sock, | ||
1139 | xstrdup("X11 inet listener")); | ||
1140 | |||
1141 | /* Return a suitable value for the DISPLAY environment variable. */ | ||
1142 | return xstrdup(buf); | ||
1143 | } | 1101 | } |
1144 | 1102 | ||
1145 | #ifndef X_UNIX_PATH | 1103 | #ifndef X_UNIX_PATH |
@@ -1150,30 +1108,29 @@ static | |||
1150 | int | 1108 | int |
1151 | connect_local_xsocket(unsigned dnr) | 1109 | connect_local_xsocket(unsigned dnr) |
1152 | { | 1110 | { |
1153 | static const char *const x_sockets[] = { | 1111 | static const char *const x_sockets[] = { |
1154 | X_UNIX_PATH "%u", | 1112 | X_UNIX_PATH "%u", |
1155 | "/var/X/.X11-unix/X" "%u", | 1113 | "/var/X/.X11-unix/X" "%u", |
1156 | "/usr/spool/sockets/X11/" "%u", | 1114 | "/usr/spool/sockets/X11/" "%u", |
1157 | NULL | 1115 | NULL |
1158 | }; | 1116 | }; |
1159 | int sock; | 1117 | int sock; |
1160 | struct sockaddr_un addr; | 1118 | struct sockaddr_un addr; |
1161 | const char *const *path; | 1119 | const char *const * path; |
1162 | 1120 | ||
1163 | for (path = x_sockets; *path; ++path) | 1121 | for (path = x_sockets; *path; ++path) { |
1164 | { | 1122 | sock = socket(AF_UNIX, SOCK_STREAM, 0); |
1165 | sock = socket(AF_UNIX, SOCK_STREAM, 0); | 1123 | if (sock < 0) |
1166 | if (sock < 0) | 1124 | error("socket: %.100s", strerror(errno)); |
1167 | error("socket: %.100s", strerror(errno)); | 1125 | memset(&addr, 0, sizeof(addr)); |
1168 | memset(&addr, 0, sizeof(addr)); | 1126 | addr.sun_family = AF_UNIX; |
1169 | addr.sun_family = AF_UNIX; | 1127 | snprintf(addr.sun_path, sizeof addr.sun_path, *path, dnr); |
1170 | snprintf(addr.sun_path, sizeof addr.sun_path, *path, dnr); | 1128 | if (connect(sock, (struct sockaddr *) & addr, sizeof(addr)) == 0) |
1171 | if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0) | 1129 | return sock; |
1172 | return sock; | 1130 | close(sock); |
1173 | close(sock); | 1131 | } |
1174 | } | 1132 | error("connect %.100s: %.100s", addr.sun_path, strerror(errno)); |
1175 | error("connect %.100s: %.100s", addr.sun_path, strerror(errno)); | 1133 | return -1; |
1176 | return -1; | ||
1177 | } | 1134 | } |
1178 | 1135 | ||
1179 | 1136 | ||
@@ -1181,327 +1138,317 @@ connect_local_xsocket(unsigned dnr) | |||
1181 | the remote channel number. We should do whatever we want, and respond | 1138 | the remote channel number. We should do whatever we want, and respond |
1182 | with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE. */ | 1139 | with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE. */ |
1183 | 1140 | ||
1184 | void x11_input_open(int payload_len) | 1141 | void |
1142 | x11_input_open(int payload_len) | ||
1185 | { | 1143 | { |
1186 | int remote_channel, display_number, sock, newch; | 1144 | int remote_channel, display_number, sock, newch; |
1187 | const char *display; | 1145 | const char *display; |
1188 | struct sockaddr_in sin; | 1146 | struct sockaddr_in sin; |
1189 | char buf[1024], *cp, *remote_host; | 1147 | char buf[1024], *cp, *remote_host; |
1190 | struct hostent *hp; | 1148 | struct hostent *hp; |
1191 | int remote_len; | 1149 | int remote_len; |
1192 | 1150 | ||
1193 | /* Get remote channel number. */ | 1151 | /* Get remote channel number. */ |
1194 | remote_channel = packet_get_int(); | 1152 | remote_channel = packet_get_int(); |
1195 | 1153 | ||
1196 | /* Get remote originator name. */ | 1154 | /* Get remote originator name. */ |
1197 | if (have_hostname_in_open) | 1155 | if (have_hostname_in_open) |
1198 | remote_host = packet_get_string(&remote_len); | 1156 | remote_host = packet_get_string(&remote_len); |
1199 | else | 1157 | else |
1200 | remote_host = xstrdup("unknown (remote did not supply name)"); | 1158 | remote_host = xstrdup("unknown (remote did not supply name)"); |
1201 | 1159 | ||
1202 | debug("Received X11 open request."); | 1160 | debug("Received X11 open request."); |
1203 | packet_integrity_check(payload_len, 4 + 4+remote_len, SSH_SMSG_X11_OPEN); | 1161 | packet_integrity_check(payload_len, 4 + 4 + remote_len, SSH_SMSG_X11_OPEN); |
1204 | 1162 | ||
1205 | /* Try to open a socket for the local X server. */ | 1163 | /* Try to open a socket for the local X server. */ |
1206 | display = getenv("DISPLAY"); | 1164 | display = getenv("DISPLAY"); |
1207 | if (!display) | 1165 | if (!display) { |
1208 | { | 1166 | error("DISPLAY not set."); |
1209 | error("DISPLAY not set."); | 1167 | goto fail; |
1210 | goto fail; | 1168 | } |
1211 | } | 1169 | /* Now we decode the value of the DISPLAY variable and make a |
1212 | 1170 | connection to the real X server. */ | |
1213 | /* Now we decode the value of the DISPLAY variable and make a connection | 1171 | |
1214 | to the real X server. */ | 1172 | /* Check if it is a unix domain socket. Unix domain displays are |
1215 | 1173 | in one of the following formats: unix:d[.s], :d[.s], ::d[.s] */ | |
1216 | /* Check if it is a unix domain socket. Unix domain displays are in one | 1174 | if (strncmp(display, "unix:", 5) == 0 || |
1217 | of the following formats: unix:d[.s], :d[.s], ::d[.s] */ | 1175 | display[0] == ':') { |
1218 | if (strncmp(display, "unix:", 5) == 0 || | 1176 | /* Connect to the unix domain socket. */ |
1219 | display[0] == ':') | 1177 | if (sscanf(strrchr(display, ':') + 1, "%d", &display_number) != 1) { |
1220 | { | 1178 | error("Could not parse display number from DISPLAY: %.100s", |
1221 | /* Connect to the unix domain socket. */ | 1179 | display); |
1222 | if (sscanf(strrchr(display, ':') + 1, "%d", &display_number) != 1) | 1180 | goto fail; |
1223 | { | 1181 | } |
1224 | error("Could not parse display number from DISPLAY: %.100s", | 1182 | /* Create a socket. */ |
1225 | display); | 1183 | sock = connect_local_xsocket(display_number); |
1226 | goto fail; | 1184 | if (sock < 0) |
1185 | goto fail; | ||
1186 | |||
1187 | /* OK, we now have a connection to the display. */ | ||
1188 | goto success; | ||
1189 | } | ||
1190 | /* Connect to an inet socket. The DISPLAY value is supposedly | ||
1191 | hostname:d[.s], where hostname may also be numeric IP address. */ | ||
1192 | strncpy(buf, display, sizeof(buf)); | ||
1193 | buf[sizeof(buf) - 1] = 0; | ||
1194 | cp = strchr(buf, ':'); | ||
1195 | if (!cp) { | ||
1196 | error("Could not find ':' in DISPLAY: %.100s", display); | ||
1197 | goto fail; | ||
1198 | } | ||
1199 | *cp = 0; | ||
1200 | /* buf now contains the host name. But first we parse the display | ||
1201 | number. */ | ||
1202 | if (sscanf(cp + 1, "%d", &display_number) != 1) { | ||
1203 | error("Could not parse display number from DISPLAY: %.100s", | ||
1204 | display); | ||
1205 | goto fail; | ||
1206 | } | ||
1207 | /* Try to parse the host name as a numeric IP address. */ | ||
1208 | memset(&sin, 0, sizeof(sin)); | ||
1209 | sin.sin_addr.s_addr = inet_addr(buf); | ||
1210 | if ((sin.sin_addr.s_addr & 0xffffffff) != 0xffffffff) { | ||
1211 | /* It was a valid numeric host address. */ | ||
1212 | sin.sin_family = AF_INET; | ||
1213 | } else { | ||
1214 | /* Not a numeric IP address. */ | ||
1215 | /* Look up the host address from the name servers. */ | ||
1216 | hp = gethostbyname(buf); | ||
1217 | if (!hp) { | ||
1218 | error("%.100s: unknown host.", buf); | ||
1219 | goto fail; | ||
1220 | } | ||
1221 | if (!hp->h_addr_list[0]) { | ||
1222 | error("%.100s: host has no IP address.", buf); | ||
1223 | goto fail; | ||
1224 | } | ||
1225 | sin.sin_family = hp->h_addrtype; | ||
1226 | memcpy(&sin.sin_addr, hp->h_addr_list[0], | ||
1227 | sizeof(sin.sin_addr)); | ||
1227 | } | 1228 | } |
1228 | /* Create a socket. */ | 1229 | /* Set port number. */ |
1229 | sock = connect_local_xsocket(display_number); | 1230 | sin.sin_port = htons(6000 + display_number); |
1230 | if (sock < 0) | 1231 | |
1231 | goto fail; | 1232 | /* Create a socket. */ |
1232 | 1233 | sock = socket(sin.sin_family, SOCK_STREAM, 0); | |
1233 | /* OK, we now have a connection to the display. */ | 1234 | if (sock < 0) { |
1234 | goto success; | 1235 | error("socket: %.100s", strerror(errno)); |
1235 | } | 1236 | goto fail; |
1236 | |||
1237 | /* Connect to an inet socket. The DISPLAY value is supposedly | ||
1238 | hostname:d[.s], where hostname may also be numeric IP address. */ | ||
1239 | strncpy(buf, display, sizeof(buf)); | ||
1240 | buf[sizeof(buf) - 1] = 0; | ||
1241 | cp = strchr(buf, ':'); | ||
1242 | if (!cp) | ||
1243 | { | ||
1244 | error("Could not find ':' in DISPLAY: %.100s", display); | ||
1245 | goto fail; | ||
1246 | } | ||
1247 | *cp = 0; | ||
1248 | /* buf now contains the host name. But first we parse the display number. */ | ||
1249 | if (sscanf(cp + 1, "%d", &display_number) != 1) | ||
1250 | { | ||
1251 | error("Could not parse display number from DISPLAY: %.100s", | ||
1252 | display); | ||
1253 | goto fail; | ||
1254 | } | ||
1255 | |||
1256 | /* Try to parse the host name as a numeric IP address. */ | ||
1257 | memset(&sin, 0, sizeof(sin)); | ||
1258 | sin.sin_addr.s_addr = inet_addr(buf); | ||
1259 | if ((sin.sin_addr.s_addr & 0xffffffff) != 0xffffffff) | ||
1260 | { | ||
1261 | /* It was a valid numeric host address. */ | ||
1262 | sin.sin_family = AF_INET; | ||
1263 | } | ||
1264 | else | ||
1265 | { | ||
1266 | /* Not a numeric IP address. */ | ||
1267 | /* Look up the host address from the name servers. */ | ||
1268 | hp = gethostbyname(buf); | ||
1269 | if (!hp) | ||
1270 | { | ||
1271 | error("%.100s: unknown host.", buf); | ||
1272 | goto fail; | ||
1273 | } | 1237 | } |
1274 | if (!hp->h_addr_list[0]) | 1238 | /* Connect it to the display. */ |
1275 | { | 1239 | if (connect(sock, (struct sockaddr *) & sin, sizeof(sin)) < 0) { |
1276 | error("%.100s: host has no IP address.", buf); | 1240 | error("connect %.100s:%d: %.100s", buf, 6000 + display_number, |
1277 | goto fail; | 1241 | strerror(errno)); |
1242 | close(sock); | ||
1243 | goto fail; | ||
1278 | } | 1244 | } |
1279 | sin.sin_family = hp->h_addrtype; | 1245 | success: |
1280 | memcpy(&sin.sin_addr, hp->h_addr_list[0], | 1246 | /* We have successfully obtained a connection to the real X display. */ |
1281 | sizeof(sin.sin_addr)); | 1247 | |
1282 | } | 1248 | /* Allocate a channel for this connection. */ |
1283 | /* Set port number. */ | 1249 | if (x11_saved_proto == NULL) |
1284 | sin.sin_port = htons(6000 + display_number); | 1250 | newch = channel_allocate(SSH_CHANNEL_OPEN, sock, remote_host); |
1285 | 1251 | else | |
1286 | /* Create a socket. */ | 1252 | newch = channel_allocate(SSH_CHANNEL_X11_OPEN, sock, remote_host); |
1287 | sock = socket(sin.sin_family, SOCK_STREAM, 0); | 1253 | channels[newch].remote_id = remote_channel; |
1288 | if (sock < 0) | 1254 | |
1289 | { | 1255 | /* Send a confirmation to the remote host. */ |
1290 | error("socket: %.100s", strerror(errno)); | 1256 | packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); |
1291 | goto fail; | 1257 | packet_put_int(remote_channel); |
1292 | } | 1258 | packet_put_int(newch); |
1293 | /* Connect it to the display. */ | 1259 | packet_send(); |
1294 | if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) | 1260 | |
1295 | { | 1261 | return; |
1296 | error("connect %.100s:%d: %.100s", buf, 6000 + display_number, | 1262 | |
1297 | strerror(errno)); | 1263 | fail: |
1298 | close(sock); | 1264 | /* Send refusal to the remote host. */ |
1299 | goto fail; | 1265 | packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); |
1300 | } | 1266 | packet_put_int(remote_channel); |
1301 | 1267 | packet_send(); | |
1302 | success: | ||
1303 | /* We have successfully obtained a connection to the real X display. */ | ||
1304 | |||
1305 | /* Allocate a channel for this connection. */ | ||
1306 | if (x11_saved_proto == NULL) | ||
1307 | newch = channel_allocate(SSH_CHANNEL_OPEN, sock, remote_host); | ||
1308 | else | ||
1309 | newch = channel_allocate(SSH_CHANNEL_X11_OPEN, sock, remote_host); | ||
1310 | channels[newch].remote_id = remote_channel; | ||
1311 | |||
1312 | /* Send a confirmation to the remote host. */ | ||
1313 | packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); | ||
1314 | packet_put_int(remote_channel); | ||
1315 | packet_put_int(newch); | ||
1316 | packet_send(); | ||
1317 | |||
1318 | return; | ||
1319 | |||
1320 | fail: | ||
1321 | /* Send refusal to the remote host. */ | ||
1322 | packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); | ||
1323 | packet_put_int(remote_channel); | ||
1324 | packet_send(); | ||
1325 | } | 1268 | } |
1326 | 1269 | ||
1327 | /* Requests forwarding of X11 connections, generates fake authentication | 1270 | /* Requests forwarding of X11 connections, generates fake authentication |
1328 | data, and enables authentication spoofing. */ | 1271 | data, and enables authentication spoofing. */ |
1329 | 1272 | ||
1330 | void x11_request_forwarding_with_spoofing(const char *proto, const char *data) | 1273 | void |
1274 | x11_request_forwarding_with_spoofing(const char *proto, const char *data) | ||
1331 | { | 1275 | { |
1332 | unsigned int data_len = (unsigned int)strlen(data) / 2; | 1276 | unsigned int data_len = (unsigned int) strlen(data) / 2; |
1333 | unsigned int i, value; | 1277 | unsigned int i, value; |
1334 | char *new_data; | 1278 | char *new_data; |
1335 | int screen_number; | 1279 | int screen_number; |
1336 | const char *cp; | 1280 | const char *cp; |
1337 | u_int32_t rand = 0; | 1281 | u_int32_t rand = 0; |
1338 | 1282 | ||
1339 | cp = getenv("DISPLAY"); | 1283 | cp = getenv("DISPLAY"); |
1340 | if (cp) | 1284 | if (cp) |
1341 | cp = strchr(cp, ':'); | 1285 | cp = strchr(cp, ':'); |
1342 | if (cp) | 1286 | if (cp) |
1343 | cp = strchr(cp, '.'); | 1287 | cp = strchr(cp, '.'); |
1344 | if (cp) | 1288 | if (cp) |
1345 | screen_number = atoi(cp + 1); | 1289 | screen_number = atoi(cp + 1); |
1346 | else | 1290 | else |
1347 | screen_number = 0; | 1291 | screen_number = 0; |
1348 | 1292 | ||
1349 | /* Save protocol name. */ | 1293 | /* Save protocol name. */ |
1350 | x11_saved_proto = xstrdup(proto); | 1294 | x11_saved_proto = xstrdup(proto); |
1351 | 1295 | ||
1352 | /* Extract real authentication data and generate fake data of the same | 1296 | /* Extract real authentication data and generate fake data of the |
1353 | length. */ | 1297 | same length. */ |
1354 | x11_saved_data = xmalloc(data_len); | 1298 | x11_saved_data = xmalloc(data_len); |
1355 | x11_fake_data = xmalloc(data_len); | 1299 | x11_fake_data = xmalloc(data_len); |
1356 | for (i = 0; i < data_len; i++) | 1300 | for (i = 0; i < data_len; i++) { |
1357 | { | 1301 | if (sscanf(data + 2 * i, "%2x", &value) != 1) |
1358 | if (sscanf(data + 2 * i, "%2x", &value) != 1) | 1302 | fatal("x11_request_forwarding: bad authentication data: %.100s", data); |
1359 | fatal("x11_request_forwarding: bad authentication data: %.100s", data); | 1303 | if (i % 4 == 0) |
1360 | if (i % 4 == 0) | 1304 | rand = arc4random(); |
1361 | rand = arc4random(); | 1305 | x11_saved_data[i] = value; |
1362 | x11_saved_data[i] = value; | 1306 | x11_fake_data[i] = rand & 0xff; |
1363 | x11_fake_data[i] = rand & 0xff; | 1307 | rand >>= 8; |
1364 | rand >>= 8; | 1308 | } |
1365 | } | 1309 | x11_saved_data_len = data_len; |
1366 | x11_saved_data_len = data_len; | 1310 | x11_fake_data_len = data_len; |
1367 | x11_fake_data_len = data_len; | 1311 | |
1368 | 1312 | /* Convert the fake data into hex. */ | |
1369 | /* Convert the fake data into hex. */ | 1313 | new_data = xmalloc(2 * data_len + 1); |
1370 | new_data = xmalloc(2 * data_len + 1); | 1314 | for (i = 0; i < data_len; i++) |
1371 | for (i = 0; i < data_len; i++) | 1315 | sprintf(new_data + 2 * i, "%02x", (unsigned char) x11_fake_data[i]); |
1372 | sprintf(new_data + 2 * i, "%02x", (unsigned char)x11_fake_data[i]); | 1316 | |
1373 | 1317 | /* Send the request packet. */ | |
1374 | /* Send the request packet. */ | 1318 | packet_start(SSH_CMSG_X11_REQUEST_FORWARDING); |
1375 | packet_start(SSH_CMSG_X11_REQUEST_FORWARDING); | 1319 | packet_put_string(proto, strlen(proto)); |
1376 | packet_put_string(proto, strlen(proto)); | 1320 | packet_put_string(new_data, strlen(new_data)); |
1377 | packet_put_string(new_data, strlen(new_data)); | 1321 | packet_put_int(screen_number); |
1378 | packet_put_int(screen_number); | 1322 | packet_send(); |
1379 | packet_send(); | 1323 | packet_write_wait(); |
1380 | packet_write_wait(); | 1324 | xfree(new_data); |
1381 | xfree(new_data); | ||
1382 | } | 1325 | } |
1383 | 1326 | ||
1384 | /* Sends a message to the server to request authentication fd forwarding. */ | 1327 | /* Sends a message to the server to request authentication fd forwarding. */ |
1385 | 1328 | ||
1386 | void auth_request_forwarding() | 1329 | void |
1330 | auth_request_forwarding() | ||
1387 | { | 1331 | { |
1388 | packet_start(SSH_CMSG_AGENT_REQUEST_FORWARDING); | 1332 | packet_start(SSH_CMSG_AGENT_REQUEST_FORWARDING); |
1389 | packet_send(); | 1333 | packet_send(); |
1390 | packet_write_wait(); | 1334 | packet_write_wait(); |
1391 | } | 1335 | } |
1392 | 1336 | ||
1393 | /* Returns the name of the forwarded authentication socket. Returns NULL | 1337 | /* Returns the name of the forwarded authentication socket. Returns NULL |
1394 | if there is no forwarded authentication socket. The returned value | 1338 | if there is no forwarded authentication socket. The returned value |
1395 | points to a static buffer. */ | 1339 | points to a static buffer. */ |
1396 | 1340 | ||
1397 | char *auth_get_socket_name() | 1341 | char * |
1342 | auth_get_socket_name() | ||
1398 | { | 1343 | { |
1399 | return channel_forwarded_auth_socket_name; | 1344 | return channel_forwarded_auth_socket_name; |
1400 | } | 1345 | } |
1401 | 1346 | ||
1402 | /* removes the agent forwarding socket */ | 1347 | /* removes the agent forwarding socket */ |
1403 | 1348 | ||
1404 | void cleanup_socket(void) { | 1349 | void |
1405 | remove(channel_forwarded_auth_socket_name); | 1350 | cleanup_socket(void) |
1406 | rmdir(channel_forwarded_auth_socket_dir); | 1351 | { |
1352 | remove(channel_forwarded_auth_socket_name); | ||
1353 | rmdir(channel_forwarded_auth_socket_dir); | ||
1407 | } | 1354 | } |
1408 | 1355 | ||
1409 | /* This if called to process SSH_CMSG_AGENT_REQUEST_FORWARDING on the server. | 1356 | /* This if called to process SSH_CMSG_AGENT_REQUEST_FORWARDING on the server. |
1410 | This starts forwarding authentication requests. */ | 1357 | This starts forwarding authentication requests. */ |
1411 | 1358 | ||
1412 | void auth_input_request_forwarding(struct passwd *pw) | 1359 | void |
1360 | auth_input_request_forwarding(struct passwd * pw) | ||
1413 | { | 1361 | { |
1414 | int sock, newch; | 1362 | int sock, newch; |
1415 | struct sockaddr_un sunaddr; | 1363 | struct sockaddr_un sunaddr; |
1416 | 1364 | ||
1417 | if (auth_get_socket_name() != NULL) | 1365 | if (auth_get_socket_name() != NULL) |
1418 | fatal("Protocol error: authentication forwarding requested twice."); | 1366 | fatal("Protocol error: authentication forwarding requested twice."); |
1419 | 1367 | ||
1420 | /* Temporarily drop privileged uid for mkdir/bind. */ | 1368 | /* Temporarily drop privileged uid for mkdir/bind. */ |
1421 | temporarily_use_uid(pw->pw_uid); | 1369 | temporarily_use_uid(pw->pw_uid); |
1422 | 1370 | ||
1423 | /* Allocate a buffer for the socket name, and format the name. */ | 1371 | /* Allocate a buffer for the socket name, and format the name. */ |
1424 | channel_forwarded_auth_socket_name = xmalloc(MAX_SOCKET_NAME); | 1372 | channel_forwarded_auth_socket_name = xmalloc(MAX_SOCKET_NAME); |
1425 | channel_forwarded_auth_socket_dir = xmalloc(MAX_SOCKET_NAME); | 1373 | channel_forwarded_auth_socket_dir = xmalloc(MAX_SOCKET_NAME); |
1426 | strlcpy(channel_forwarded_auth_socket_dir, "/tmp/ssh-XXXXXXXX", MAX_SOCKET_NAME); | 1374 | strlcpy(channel_forwarded_auth_socket_dir, "/tmp/ssh-XXXXXXXX", MAX_SOCKET_NAME); |
1427 | 1375 | ||
1428 | /* Create private directory for socket */ | 1376 | /* Create private directory for socket */ |
1429 | if (mkdtemp(channel_forwarded_auth_socket_dir) == NULL) | 1377 | if (mkdtemp(channel_forwarded_auth_socket_dir) == NULL) |
1430 | packet_disconnect("mkdtemp: %.100s", strerror(errno)); | 1378 | packet_disconnect("mkdtemp: %.100s", strerror(errno)); |
1431 | snprintf(channel_forwarded_auth_socket_name, MAX_SOCKET_NAME, | 1379 | snprintf(channel_forwarded_auth_socket_name, MAX_SOCKET_NAME, "%s/agent.%d", |
1432 | "%s/agent.%d", channel_forwarded_auth_socket_dir, (int)getpid()); | 1380 | channel_forwarded_auth_socket_dir, (int) getpid()); |
1433 | 1381 | ||
1434 | if (atexit(cleanup_socket) < 0) { | 1382 | if (atexit(cleanup_socket) < 0) { |
1435 | int saved=errno; | 1383 | int saved = errno; |
1436 | cleanup_socket(); | 1384 | cleanup_socket(); |
1437 | packet_disconnect("socket: %.100s", strerror(saved)); | 1385 | packet_disconnect("socket: %.100s", strerror(saved)); |
1438 | } | 1386 | } |
1439 | 1387 | /* Create the socket. */ | |
1440 | /* Create the socket. */ | 1388 | sock = socket(AF_UNIX, SOCK_STREAM, 0); |
1441 | sock = socket(AF_UNIX, SOCK_STREAM, 0); | 1389 | if (sock < 0) |
1442 | if (sock < 0) | 1390 | packet_disconnect("socket: %.100s", strerror(errno)); |
1443 | packet_disconnect("socket: %.100s", strerror(errno)); | 1391 | |
1444 | 1392 | /* Bind it to the name. */ | |
1445 | /* Bind it to the name. */ | 1393 | memset(&sunaddr, 0, sizeof(sunaddr)); |
1446 | memset(&sunaddr, 0, sizeof(sunaddr)); | 1394 | sunaddr.sun_family = AF_UNIX; |
1447 | sunaddr.sun_family = AF_UNIX; | 1395 | strncpy(sunaddr.sun_path, channel_forwarded_auth_socket_name, |
1448 | strncpy(sunaddr.sun_path, channel_forwarded_auth_socket_name, | 1396 | sizeof(sunaddr.sun_path)); |
1449 | sizeof(sunaddr.sun_path)); | 1397 | |
1450 | 1398 | if (bind(sock, (struct sockaddr *) & sunaddr, sizeof(sunaddr)) < 0) | |
1451 | if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0) | 1399 | packet_disconnect("bind: %.100s", strerror(errno)); |
1452 | packet_disconnect("bind: %.100s", strerror(errno)); | 1400 | |
1453 | 1401 | /* Restore the privileged uid. */ | |
1454 | /* Restore the privileged uid. */ | 1402 | restore_uid(); |
1455 | restore_uid(); | 1403 | |
1456 | 1404 | /* Start listening on the socket. */ | |
1457 | /* Start listening on the socket. */ | 1405 | if (listen(sock, 5) < 0) |
1458 | if (listen(sock, 5) < 0) | 1406 | packet_disconnect("listen: %.100s", strerror(errno)); |
1459 | packet_disconnect("listen: %.100s", strerror(errno)); | 1407 | |
1460 | 1408 | /* Allocate a channel for the authentication agent socket. */ | |
1461 | /* Allocate a channel for the authentication agent socket. */ | 1409 | newch = channel_allocate(SSH_CHANNEL_AUTH_SOCKET, sock, |
1462 | newch = channel_allocate(SSH_CHANNEL_AUTH_SOCKET, sock, | 1410 | xstrdup("auth socket")); |
1463 | xstrdup("auth socket")); | 1411 | strcpy(channels[newch].path, channel_forwarded_auth_socket_name); |
1464 | strcpy(channels[newch].path, channel_forwarded_auth_socket_name); | ||
1465 | } | 1412 | } |
1466 | 1413 | ||
1467 | /* This is called to process an SSH_SMSG_AGENT_OPEN message. */ | 1414 | /* This is called to process an SSH_SMSG_AGENT_OPEN message. */ |
1468 | 1415 | ||
1469 | void auth_input_open_request() | 1416 | void |
1417 | auth_input_open_request() | ||
1470 | { | 1418 | { |
1471 | int remch, sock, newch; | 1419 | int remch, sock, newch; |
1472 | char *dummyname; | 1420 | char *dummyname; |
1473 | 1421 | ||
1474 | /* Read the remote channel number from the message. */ | 1422 | /* Read the remote channel number from the message. */ |
1475 | remch = packet_get_int(); | 1423 | remch = packet_get_int(); |
1476 | 1424 | ||
1477 | /* Get a connection to the local authentication agent (this may again get | 1425 | /* Get a connection to the local authentication agent (this may |
1478 | forwarded). */ | 1426 | again get forwarded). */ |
1479 | sock = ssh_get_authentication_socket(); | 1427 | sock = ssh_get_authentication_socket(); |
1480 | 1428 | ||
1481 | /* If we could not connect the agent, send an error message back to | 1429 | /* If we could not connect the agent, send an error message back |
1482 | the server. This should never happen unless the agent | 1430 | to the server. This should never happen unless the agent dies, |
1483 | dies, because authentication forwarding is only enabled if we have an | 1431 | because authentication forwarding is only enabled if we have an |
1484 | agent. */ | 1432 | agent. */ |
1485 | if (sock < 0){ | 1433 | if (sock < 0) { |
1486 | packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); | 1434 | packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); |
1487 | packet_put_int(remch); | 1435 | packet_put_int(remch); |
1488 | packet_send(); | 1436 | packet_send(); |
1489 | return; | 1437 | return; |
1490 | } | 1438 | } |
1491 | 1439 | debug("Forwarding authentication connection."); | |
1492 | debug("Forwarding authentication connection."); | 1440 | |
1493 | 1441 | /* Dummy host name. This will be freed when the channel is freed; | |
1494 | /* Dummy host name. This will be freed when the channel is freed; it will | 1442 | it will still be valid in the packet_put_string below since the |
1495 | still be valid in the packet_put_string below since the channel cannot | 1443 | channel cannot yet be freed at that point. */ |
1496 | yet be freed at that point. */ | 1444 | dummyname = xstrdup("authentication agent connection"); |
1497 | dummyname = xstrdup("authentication agent connection"); | 1445 | |
1498 | 1446 | newch = channel_allocate(SSH_CHANNEL_OPEN, sock, dummyname); | |
1499 | newch = channel_allocate(SSH_CHANNEL_OPEN, sock, dummyname); | 1447 | channels[newch].remote_id = remch; |
1500 | channels[newch].remote_id = remch; | 1448 | |
1501 | 1449 | /* Send a confirmation to the remote host. */ | |
1502 | /* Send a confirmation to the remote host. */ | 1450 | packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); |
1503 | packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); | 1451 | packet_put_int(remch); |
1504 | packet_put_int(remch); | 1452 | packet_put_int(newch); |
1505 | packet_put_int(newch); | 1453 | packet_send(); |
1506 | packet_send(); | ||
1507 | } | 1454 | } |
diff --git a/channels.h b/channels.h index 608c774db..5851257ca 100644 --- a/channels.h +++ b/channels.h | |||
@@ -1,40 +1,46 @@ | |||
1 | /* RCSID("$Id: channels.h,v 1.2 1999/10/30 01:39:56 damien Exp $"); */ | 1 | /* RCSID("$Id: channels.h,v 1.3 1999/11/24 13:26:22 damien Exp $"); */ |
2 | 2 | ||
3 | #ifndef CHANNELS_H | 3 | #ifndef CHANNELS_H |
4 | #define CHANNELS_H | 4 | #define CHANNELS_H |
5 | 5 | ||
6 | /* Definitions for channel types. */ | 6 | /* Definitions for channel types. */ |
7 | #define SSH_CHANNEL_FREE 0 /* This channel is free (unused). */ | 7 | #define SSH_CHANNEL_FREE 0 /* This channel is free |
8 | #define SSH_CHANNEL_X11_LISTENER 1 /* Listening for inet X11 conn. */ | 8 | * (unused). */ |
9 | #define SSH_CHANNEL_PORT_LISTENER 2 /* Listening on a port. */ | 9 | #define SSH_CHANNEL_X11_LISTENER 1 /* Listening for inet X11 |
10 | #define SSH_CHANNEL_OPENING 3 /* waiting for confirmation */ | 10 | * conn. */ |
11 | #define SSH_CHANNEL_OPEN 4 /* normal open two-way channel */ | 11 | #define SSH_CHANNEL_PORT_LISTENER 2 /* Listening on a port. */ |
12 | #define SSH_CHANNEL_CLOSED 5 /* waiting for close confirmation */ | 12 | #define SSH_CHANNEL_OPENING 3 /* waiting for confirmation */ |
13 | #define SSH_CHANNEL_OPEN 4 /* normal open two-way channel */ | ||
14 | #define SSH_CHANNEL_CLOSED 5 /* waiting for close | ||
15 | * confirmation */ | ||
13 | /* SSH_CHANNEL_AUTH_FD 6 authentication fd */ | 16 | /* SSH_CHANNEL_AUTH_FD 6 authentication fd */ |
14 | #define SSH_CHANNEL_AUTH_SOCKET 7 /* authentication socket */ | 17 | #define SSH_CHANNEL_AUTH_SOCKET 7 /* authentication socket */ |
15 | /* SSH_CHANNEL_AUTH_SOCKET_FD 8 connection to auth socket */ | 18 | /* SSH_CHANNEL_AUTH_SOCKET_FD 8 connection to auth socket */ |
16 | #define SSH_CHANNEL_X11_OPEN 9 /* reading first X11 packet */ | 19 | #define SSH_CHANNEL_X11_OPEN 9 /* reading first X11 packet */ |
17 | #define SSH_CHANNEL_INPUT_DRAINING 10 /* sending remaining data to conn */ | 20 | #define SSH_CHANNEL_INPUT_DRAINING 10 /* sending remaining data to |
18 | #define SSH_CHANNEL_OUTPUT_DRAINING 11 /* sending remaining data to app */ | 21 | * conn */ |
22 | #define SSH_CHANNEL_OUTPUT_DRAINING 11 /* sending remaining data to | ||
23 | * app */ | ||
19 | 24 | ||
20 | /* Data structure for channel data. This is iniailized in channel_allocate | 25 | /* Data structure for channel data. This is iniailized in channel_allocate |
21 | and cleared in channel_free. */ | 26 | and cleared in channel_free. */ |
22 | 27 | ||
23 | typedef struct Channel | 28 | typedef struct Channel { |
24 | { | 29 | int type; /* channel type/state */ |
25 | int type; /* channel type/state */ | 30 | int self; /* my own channel identifier */ |
26 | int self; /* my own channel identifier */ | 31 | int remote_id; /* channel identifier for remote peer */ |
27 | int remote_id; /* channel identifier for remote peer */ | 32 | /* peer can be reached over encrypted connection, via packet-sent */ |
28 | /* peer can be reached over encrypted connection, via packet-sent */ | 33 | int istate; /* input from channel (state of receive half) */ |
29 | int istate; /* input from channel (state of receive half) */ | 34 | int ostate; /* output to channel (state of transmit half) */ |
30 | int ostate; /* output to channel (state of transmit half) */ | 35 | int sock; /* data socket, linked to this channel */ |
31 | int sock; /* data socket, linked to this channel */ | 36 | Buffer input; /* data read from socket, to be sent over |
32 | Buffer input; /* data read from socket, to be sent over encrypted connection */ | 37 | * encrypted connection */ |
33 | Buffer output; /* data received over encrypted connection for send on socket */ | 38 | Buffer output; /* data received over encrypted connection for |
34 | char path[200]; /* path for unix domain sockets, or host name for forwards */ | 39 | * send on socket */ |
35 | int listening_port; /* port being listened for forwards */ | 40 | char path[200]; /* path for unix domain sockets, or host name |
36 | int host_port; /* remote port to connect for forwards */ | 41 | * for forwards */ |
37 | char *remote_name; /* remote hostname */ | 42 | int listening_port; /* port being listened for forwards */ |
38 | } Channel; | 43 | int host_port; /* remote port to connect for forwards */ |
39 | 44 | char *remote_name; /* remote hostname */ | |
45 | } Channel; | ||
40 | #endif | 46 | #endif |
@@ -1,18 +1,18 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * | |
3 | cipher.c | 3 | * cipher.c |
4 | 4 | * | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
6 | 6 | * | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
8 | All rights reserved | 8 | * All rights reserved |
9 | 9 | * | |
10 | Created: Wed Apr 19 17:41:39 1995 ylo | 10 | * Created: Wed Apr 19 17:41:39 1995 ylo |
11 | 11 | * | |
12 | */ | 12 | */ |
13 | 13 | ||
14 | #include "includes.h" | 14 | #include "includes.h" |
15 | RCSID("$Id: cipher.c,v 1.6 1999/11/16 02:37:16 damien Exp $"); | 15 | RCSID("$Id: cipher.c,v 1.7 1999/11/24 13:26:22 damien Exp $"); |
16 | 16 | ||
17 | #include "ssh.h" | 17 | #include "ssh.h" |
18 | #include "cipher.h" | 18 | #include "cipher.h" |
@@ -38,124 +38,124 @@ RCSID("$Id: cipher.c,v 1.6 1999/11/16 02:37:16 damien Exp $"); | |||
38 | */ | 38 | */ |
39 | void | 39 | void |
40 | SSH_3CBC_ENCRYPT(des_key_schedule ks1, | 40 | SSH_3CBC_ENCRYPT(des_key_schedule ks1, |
41 | des_key_schedule ks2, des_cblock *iv2, | 41 | des_key_schedule ks2, des_cblock * iv2, |
42 | des_key_schedule ks3, des_cblock *iv3, | 42 | des_key_schedule ks3, des_cblock * iv3, |
43 | void *dest, void *src, | 43 | void *dest, void *src, |
44 | unsigned int len) | 44 | unsigned int len) |
45 | { | 45 | { |
46 | des_cblock iv1; | 46 | des_cblock iv1; |
47 | 47 | ||
48 | memcpy(&iv1, iv2, 8); | 48 | memcpy(&iv1, iv2, 8); |
49 | 49 | ||
50 | des_cbc_encrypt(src, dest, len, ks1, &iv1, DES_ENCRYPT); | 50 | des_cbc_encrypt(src, dest, len, ks1, &iv1, DES_ENCRYPT); |
51 | memcpy(&iv1, dest + len - 8, 8); | 51 | memcpy(&iv1, dest + len - 8, 8); |
52 | 52 | ||
53 | des_cbc_encrypt(dest, dest, len, ks2, iv2, DES_DECRYPT); | 53 | des_cbc_encrypt(dest, dest, len, ks2, iv2, DES_DECRYPT); |
54 | memcpy(iv2, &iv1, 8); /* Note how iv1 == iv2 on entry and exit. */ | 54 | memcpy(iv2, &iv1, 8); /* Note how iv1 == iv2 on entry and exit. */ |
55 | 55 | ||
56 | des_cbc_encrypt(dest, dest, len, ks3, iv3, DES_ENCRYPT); | 56 | des_cbc_encrypt(dest, dest, len, ks3, iv3, DES_ENCRYPT); |
57 | memcpy(iv3, dest + len - 8, 8); | 57 | memcpy(iv3, dest + len - 8, 8); |
58 | } | 58 | } |
59 | 59 | ||
60 | void | 60 | void |
61 | SSH_3CBC_DECRYPT(des_key_schedule ks1, | 61 | SSH_3CBC_DECRYPT(des_key_schedule ks1, |
62 | des_key_schedule ks2, des_cblock *iv2, | 62 | des_key_schedule ks2, des_cblock * iv2, |
63 | des_key_schedule ks3, des_cblock *iv3, | 63 | des_key_schedule ks3, des_cblock * iv3, |
64 | void *dest, void *src, | 64 | void *dest, void *src, |
65 | unsigned int len) | 65 | unsigned int len) |
66 | { | 66 | { |
67 | des_cblock iv1; | 67 | des_cblock iv1; |
68 | 68 | ||
69 | memcpy(&iv1, iv2, 8); | 69 | memcpy(&iv1, iv2, 8); |
70 | 70 | ||
71 | des_cbc_encrypt(src, dest, len, ks3, iv3, DES_DECRYPT); | 71 | des_cbc_encrypt(src, dest, len, ks3, iv3, DES_DECRYPT); |
72 | memcpy(iv3, src + len - 8, 8); | 72 | memcpy(iv3, src + len - 8, 8); |
73 | 73 | ||
74 | des_cbc_encrypt(dest, dest, len, ks2, iv2, DES_ENCRYPT); | 74 | des_cbc_encrypt(dest, dest, len, ks2, iv2, DES_ENCRYPT); |
75 | memcpy(iv2, dest + len - 8, 8); | 75 | memcpy(iv2, dest + len - 8, 8); |
76 | 76 | ||
77 | des_cbc_encrypt(dest, dest, len, ks1, &iv1, DES_DECRYPT); | 77 | des_cbc_encrypt(dest, dest, len, ks1, &iv1, DES_DECRYPT); |
78 | /* memcpy(&iv1, iv2, 8); */ /* Note how iv1 == iv2 on entry and exit. */ | 78 | /* memcpy(&iv1, iv2, 8); */ |
79 | /* Note how iv1 == iv2 on entry and exit. */ | ||
79 | } | 80 | } |
80 | 81 | ||
81 | /* | 82 | /* |
82 | * SSH uses a variation on Blowfish, all bytes must be swapped before | 83 | * SSH uses a variation on Blowfish, all bytes must be swapped before |
83 | * and after encryption/decryption. Thus the swap_bytes stuff (yuk). | 84 | * and after encryption/decryption. Thus the swap_bytes stuff (yuk). |
84 | */ | 85 | */ |
85 | static | 86 | static void |
86 | void | ||
87 | swap_bytes(const unsigned char *src, unsigned char *dst_, int n) | 87 | swap_bytes(const unsigned char *src, unsigned char *dst_, int n) |
88 | { | 88 | { |
89 | u_int32_t *dst = (u_int32_t *)dst_; /* dst must be properly aligned. */ | 89 | /* dst must be properly aligned. */ |
90 | union { | 90 | u_int32_t *dst = (u_int32_t *) dst_; |
91 | u_int32_t i; | 91 | union { |
92 | char c[4]; | 92 | u_int32_t i; |
93 | } t; | 93 | char c[4]; |
94 | 94 | } t; | |
95 | /* Process 8 bytes every lap. */ | 95 | |
96 | for (n = n / 8; n > 0; n--) | 96 | /* Process 8 bytes every lap. */ |
97 | { | 97 | for (n = n / 8; n > 0; n--) { |
98 | t.c[3] = *src++; | 98 | t.c[3] = *src++; |
99 | t.c[2] = *src++; | 99 | t.c[2] = *src++; |
100 | t.c[1] = *src++; | 100 | t.c[1] = *src++; |
101 | t.c[0] = *src++; | 101 | t.c[0] = *src++; |
102 | *dst++ = t.i; | 102 | *dst++ = t.i; |
103 | 103 | ||
104 | t.c[3] = *src++; | 104 | t.c[3] = *src++; |
105 | t.c[2] = *src++; | 105 | t.c[2] = *src++; |
106 | t.c[1] = *src++; | 106 | t.c[1] = *src++; |
107 | t.c[0] = *src++; | 107 | t.c[0] = *src++; |
108 | *dst++ = t.i; | 108 | *dst++ = t.i; |
109 | } | 109 | } |
110 | } | 110 | } |
111 | 111 | ||
112 | void (*cipher_attack_detected)(const char *fmt, ...) = fatal; | 112 | void (*cipher_attack_detected) (const char *fmt,...) = fatal; |
113 | 113 | ||
114 | static inline | 114 | static inline void |
115 | void | ||
116 | detect_cbc_attack(const unsigned char *src, | 115 | detect_cbc_attack(const unsigned char *src, |
117 | unsigned int len) | 116 | unsigned int len) |
118 | { | 117 | { |
119 | return; | 118 | return; |
120 | 119 | ||
121 | log("CRC-32 CBC insertion attack detected"); | 120 | log("CRC-32 CBC insertion attack detected"); |
122 | cipher_attack_detected("CRC-32 CBC insertion attack detected"); | 121 | cipher_attack_detected("CRC-32 CBC insertion attack detected"); |
123 | } | 122 | } |
124 | 123 | ||
125 | /* Names of all encryption algorithms. These must match the numbers defined | 124 | /* Names of all encryption algorithms. These must match the numbers defined |
126 | int cipher.h. */ | 125 | int cipher.h. */ |
127 | static char *cipher_names[] = | 126 | static char *cipher_names[] = |
128 | { | 127 | { |
129 | "none", | 128 | "none", |
130 | "idea", | 129 | "idea", |
131 | "des", | 130 | "des", |
132 | "3des", | 131 | "3des", |
133 | "tss", | 132 | "tss", |
134 | "rc4", | 133 | "rc4", |
135 | "blowfish" | 134 | "blowfish" |
136 | }; | 135 | }; |
137 | 136 | ||
138 | /* Returns a bit mask indicating which ciphers are supported by this | 137 | /* Returns a bit mask indicating which ciphers are supported by this |
139 | implementation. The bit mask has the corresponding bit set of each | 138 | implementation. The bit mask has the corresponding bit set of each |
140 | supported cipher. */ | 139 | supported cipher. */ |
141 | 140 | ||
142 | unsigned int cipher_mask() | 141 | unsigned int |
142 | cipher_mask() | ||
143 | { | 143 | { |
144 | unsigned int mask = 0; | 144 | unsigned int mask = 0; |
145 | mask |= 1 << SSH_CIPHER_3DES; /* Mandatory */ | 145 | mask |= 1 << SSH_CIPHER_3DES; /* Mandatory */ |
146 | mask |= 1 << SSH_CIPHER_BLOWFISH; | 146 | mask |= 1 << SSH_CIPHER_BLOWFISH; |
147 | return mask; | 147 | return mask; |
148 | } | 148 | } |
149 | 149 | ||
150 | /* Returns the name of the cipher. */ | 150 | /* Returns the name of the cipher. */ |
151 | 151 | ||
152 | const | 152 | const char * |
153 | char *cipher_name(int cipher) | 153 | cipher_name(int cipher) |
154 | { | 154 | { |
155 | if (cipher < 0 || cipher >= sizeof(cipher_names) / sizeof(cipher_names[0]) || | 155 | if (cipher < 0 || cipher >= sizeof(cipher_names) / sizeof(cipher_names[0]) || |
156 | cipher_names[cipher] == NULL) | 156 | cipher_names[cipher] == NULL) |
157 | fatal("cipher_name: bad cipher number: %d", cipher); | 157 | fatal("cipher_name: bad cipher number: %d", cipher); |
158 | return cipher_names[cipher]; | 158 | return cipher_names[cipher]; |
159 | } | 159 | } |
160 | 160 | ||
161 | /* Parses the name of the cipher. Returns the number of the corresponding | 161 | /* Parses the name of the cipher. Returns the number of the corresponding |
@@ -164,146 +164,151 @@ char *cipher_name(int cipher) | |||
164 | int | 164 | int |
165 | cipher_number(const char *name) | 165 | cipher_number(const char *name) |
166 | { | 166 | { |
167 | int i; | 167 | int i; |
168 | for (i = 0; i < sizeof(cipher_names) / sizeof(cipher_names[0]); i++) | 168 | for (i = 0; i < sizeof(cipher_names) / sizeof(cipher_names[0]); i++) |
169 | if (strcmp(cipher_names[i], name) == 0 && | 169 | if (strcmp(cipher_names[i], name) == 0 && |
170 | (cipher_mask() & (1 << i))) | 170 | (cipher_mask() & (1 << i))) |
171 | return i; | 171 | return i; |
172 | return -1; | 172 | return -1; |
173 | } | 173 | } |
174 | 174 | ||
175 | /* Selects the cipher, and keys if by computing the MD5 checksum of the | 175 | /* Selects the cipher, and keys if by computing the MD5 checksum of the |
176 | passphrase and using the resulting 16 bytes as the key. */ | 176 | passphrase and using the resulting 16 bytes as the key. */ |
177 | 177 | ||
178 | void cipher_set_key_string(CipherContext *context, int cipher, | 178 | void |
179 | const char *passphrase, int for_encryption) | 179 | cipher_set_key_string(CipherContext *context, int cipher, |
180 | const char *passphrase, int for_encryption) | ||
180 | { | 181 | { |
181 | MD5_CTX md; | 182 | MD5_CTX md; |
182 | unsigned char digest[16]; | 183 | unsigned char digest[16]; |
183 | 184 | ||
184 | MD5_Init(&md); | 185 | MD5_Init(&md); |
185 | MD5_Update(&md, (const unsigned char *)passphrase, strlen(passphrase)); | 186 | MD5_Update(&md, (const unsigned char *) passphrase, strlen(passphrase)); |
186 | MD5_Final(digest, &md); | 187 | MD5_Final(digest, &md); |
187 | 188 | ||
188 | cipher_set_key(context, cipher, digest, 16, for_encryption); | 189 | cipher_set_key(context, cipher, digest, 16, for_encryption); |
189 | 190 | ||
190 | memset(digest, 0, sizeof(digest)); | 191 | memset(digest, 0, sizeof(digest)); |
191 | memset(&md, 0, sizeof(md)); | 192 | memset(&md, 0, sizeof(md)); |
192 | } | 193 | } |
193 | 194 | ||
194 | /* Selects the cipher to use and sets the key. */ | 195 | /* Selects the cipher to use and sets the key. */ |
195 | 196 | ||
196 | void cipher_set_key(CipherContext *context, int cipher, | 197 | void |
197 | const unsigned char *key, int keylen, int for_encryption) | 198 | cipher_set_key(CipherContext *context, int cipher, |
199 | const unsigned char *key, int keylen, int for_encryption) | ||
198 | { | 200 | { |
199 | unsigned char padded[32]; | 201 | unsigned char padded[32]; |
200 | 202 | ||
201 | /* Set cipher type. */ | 203 | /* Set cipher type. */ |
202 | context->type = cipher; | 204 | context->type = cipher; |
203 | 205 | ||
204 | /* Get 32 bytes of key data. Pad if necessary. (So that code below does | 206 | /* Get 32 bytes of key data. Pad if necessary. (So that code |
205 | not need to worry about key size). */ | 207 | below does not need to worry about key size). */ |
206 | memset(padded, 0, sizeof(padded)); | 208 | memset(padded, 0, sizeof(padded)); |
207 | memcpy(padded, key, keylen < sizeof(padded) ? keylen : sizeof(padded)); | 209 | memcpy(padded, key, keylen < sizeof(padded) ? keylen : sizeof(padded)); |
208 | 210 | ||
209 | /* Initialize the initialization vector. */ | 211 | /* Initialize the initialization vector. */ |
210 | switch (cipher) | 212 | switch (cipher) { |
211 | { | 213 | case SSH_CIPHER_NONE: |
212 | case SSH_CIPHER_NONE: | 214 | /* Has to stay for authfile saving of private key with |
213 | /* Has to stay for authfile saving of private key with no passphrase */ | 215 | no passphrase */ |
214 | break; | 216 | break; |
215 | 217 | ||
216 | case SSH_CIPHER_3DES: | 218 | case SSH_CIPHER_3DES: |
217 | /* Note: the least significant bit of each byte of key is parity, | 219 | /* Note: the least significant bit of each byte of key is |
218 | and must be ignored by the implementation. 16 bytes of key are | 220 | parity, and must be ignored by the implementation. 16 |
219 | used (first and last keys are the same). */ | 221 | bytes of key are used (first and last keys are the |
220 | if (keylen < 16) | 222 | same). */ |
221 | error("Key length %d is insufficient for 3DES.", keylen); | 223 | if (keylen < 16) |
222 | des_set_key((void*)padded, context->u.des3.key1); | 224 | error("Key length %d is insufficient for 3DES.", keylen); |
223 | des_set_key((void*)(padded + 8), context->u.des3.key2); | 225 | des_set_key((void *) padded, context->u.des3.key1); |
224 | if (keylen <= 16) | 226 | des_set_key((void *) (padded + 8), context->u.des3.key2); |
225 | des_set_key((void*)padded, context->u.des3.key3); | 227 | if (keylen <= 16) |
226 | else | 228 | des_set_key((void *) padded, context->u.des3.key3); |
227 | des_set_key((void*)(padded + 16), context->u.des3.key3); | 229 | else |
228 | memset(context->u.des3.iv2, 0, sizeof(context->u.des3.iv2)); | 230 | des_set_key((void *) (padded + 16), context->u.des3.key3); |
229 | memset(context->u.des3.iv3, 0, sizeof(context->u.des3.iv3)); | 231 | memset(context->u.des3.iv2, 0, sizeof(context->u.des3.iv2)); |
230 | break; | 232 | memset(context->u.des3.iv3, 0, sizeof(context->u.des3.iv3)); |
231 | 233 | break; | |
232 | case SSH_CIPHER_BLOWFISH: | 234 | |
233 | BF_set_key(&context->u.bf.key, keylen, padded); | 235 | case SSH_CIPHER_BLOWFISH: |
234 | memset(context->u.bf.iv, 0, 8); | 236 | BF_set_key(&context->u.bf.key, keylen, padded); |
235 | break; | 237 | memset(context->u.bf.iv, 0, 8); |
236 | 238 | break; | |
237 | default: | 239 | |
238 | fatal("cipher_set_key: unknown cipher: %s", cipher_name(cipher)); | 240 | default: |
239 | } | 241 | fatal("cipher_set_key: unknown cipher: %s", cipher_name(cipher)); |
240 | memset(padded, 0, sizeof(padded)); | 242 | } |
243 | memset(padded, 0, sizeof(padded)); | ||
241 | } | 244 | } |
242 | 245 | ||
243 | /* Encrypts data using the cipher. */ | 246 | /* Encrypts data using the cipher. */ |
244 | 247 | ||
245 | void cipher_encrypt(CipherContext *context, unsigned char *dest, | 248 | void |
246 | const unsigned char *src, unsigned int len) | 249 | cipher_encrypt(CipherContext *context, unsigned char *dest, |
250 | const unsigned char *src, unsigned int len) | ||
247 | { | 251 | { |
248 | if ((len & 7) != 0) | 252 | if ((len & 7) != 0) |
249 | fatal("cipher_encrypt: bad plaintext length %d", len); | 253 | fatal("cipher_encrypt: bad plaintext length %d", len); |
250 | 254 | ||
251 | switch (context->type) | 255 | switch (context->type) { |
252 | { | 256 | case SSH_CIPHER_NONE: |
253 | case SSH_CIPHER_NONE: | 257 | memcpy(dest, src, len); |
254 | memcpy(dest, src, len); | 258 | break; |
255 | break; | 259 | |
256 | 260 | case SSH_CIPHER_3DES: | |
257 | case SSH_CIPHER_3DES: | 261 | SSH_3CBC_ENCRYPT(context->u.des3.key1, |
258 | SSH_3CBC_ENCRYPT(context->u.des3.key1, | 262 | context->u.des3.key2, &context->u.des3.iv2, |
259 | context->u.des3.key2, &context->u.des3.iv2, | 263 | context->u.des3.key3, &context->u.des3.iv3, |
260 | context->u.des3.key3, &context->u.des3.iv3, | 264 | dest, (void *) src, len); |
261 | dest, (void*)src, len); | 265 | break; |
262 | break; | 266 | |
263 | 267 | case SSH_CIPHER_BLOWFISH: | |
264 | case SSH_CIPHER_BLOWFISH: | 268 | swap_bytes(src, dest, len); |
265 | swap_bytes(src, dest, len); | 269 | BF_cbc_encrypt(dest, dest, len, |
266 | BF_cbc_encrypt(dest, dest, len, | 270 | &context->u.bf.key, context->u.bf.iv, |
267 | &context->u.bf.key, context->u.bf.iv, BF_ENCRYPT); | 271 | BF_ENCRYPT); |
268 | swap_bytes(dest, dest, len); | 272 | swap_bytes(dest, dest, len); |
269 | break; | 273 | break; |
270 | 274 | ||
271 | default: | 275 | default: |
272 | fatal("cipher_encrypt: unknown cipher: %d", context->type); | 276 | fatal("cipher_encrypt: unknown cipher: %s", cipher_name(context->type)); |
273 | } | 277 | } |
274 | } | 278 | } |
275 | 279 | ||
276 | /* Decrypts data using the cipher. */ | 280 | /* Decrypts data using the cipher. */ |
277 | 281 | ||
278 | void cipher_decrypt(CipherContext *context, unsigned char *dest, | 282 | void |
279 | const unsigned char *src, unsigned int len) | 283 | cipher_decrypt(CipherContext *context, unsigned char *dest, |
284 | const unsigned char *src, unsigned int len) | ||
280 | { | 285 | { |
281 | if ((len & 7) != 0) | 286 | if ((len & 7) != 0) |
282 | fatal("cipher_decrypt: bad ciphertext length %d", len); | 287 | fatal("cipher_decrypt: bad ciphertext length %d", len); |
283 | 288 | ||
284 | switch (context->type) | 289 | switch (context->type) { |
285 | { | 290 | case SSH_CIPHER_NONE: |
286 | case SSH_CIPHER_NONE: | 291 | memcpy(dest, src, len); |
287 | memcpy(dest, src, len); | 292 | break; |
288 | break; | 293 | |
289 | 294 | case SSH_CIPHER_3DES: | |
290 | case SSH_CIPHER_3DES: | 295 | /* CRC-32 attack? */ |
291 | /* CRC-32 attack? */ | 296 | SSH_3CBC_DECRYPT(context->u.des3.key1, |
292 | SSH_3CBC_DECRYPT(context->u.des3.key1, | 297 | context->u.des3.key2, &context->u.des3.iv2, |
293 | context->u.des3.key2, &context->u.des3.iv2, | 298 | context->u.des3.key3, &context->u.des3.iv3, |
294 | context->u.des3.key3, &context->u.des3.iv3, | 299 | dest, (void *) src, len); |
295 | dest, (void*)src, len); | 300 | break; |
296 | break; | 301 | |
297 | 302 | case SSH_CIPHER_BLOWFISH: | |
298 | case SSH_CIPHER_BLOWFISH: | 303 | detect_cbc_attack(src, len); |
299 | detect_cbc_attack(src, len); | 304 | swap_bytes(src, dest, len); |
300 | swap_bytes(src, dest, len); | 305 | BF_cbc_encrypt((void *) dest, dest, len, |
301 | BF_cbc_encrypt((void*)dest, dest, len, | 306 | &context->u.bf.key, context->u.bf.iv, |
302 | &context->u.bf.key, context->u.bf.iv, BF_DECRYPT); | 307 | BF_DECRYPT); |
303 | swap_bytes(dest, dest, len); | 308 | swap_bytes(dest, dest, len); |
304 | break; | 309 | break; |
305 | 310 | ||
306 | default: | 311 | default: |
307 | fatal("cipher_decrypt: unknown cipher: %d", context->type); | 312 | fatal("cipher_decrypt: unknown cipher: %s", cipher_name(context->type)); |
308 | } | 313 | } |
309 | } | 314 | } |
@@ -1,17 +1,17 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * | |
3 | cipher.h | 3 | * cipher.h |
4 | 4 | * | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
6 | 6 | * | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
8 | All rights reserved | 8 | * All rights reserved |
9 | 9 | * | |
10 | Created: Wed Apr 19 16:50:42 1995 ylo | 10 | * Created: Wed Apr 19 16:50:42 1995 ylo |
11 | 11 | * | |
12 | */ | 12 | */ |
13 | 13 | ||
14 | /* RCSID("$Id: cipher.h,v 1.3 1999/11/16 02:37:16 damien Exp $"); */ | 14 | /* RCSID("$Id: cipher.h,v 1.4 1999/11/24 13:26:22 damien Exp $"); */ |
15 | 15 | ||
16 | #ifndef CIPHER_H | 16 | #ifndef CIPHER_H |
17 | #define CIPHER_H | 17 | #define CIPHER_H |
@@ -29,32 +29,31 @@ Created: Wed Apr 19 16:50:42 1995 ylo | |||
29 | 29 | ||
30 | /* Cipher types. New types can be added, but old types should not be removed | 30 | /* Cipher types. New types can be added, but old types should not be removed |
31 | for compatibility. The maximum allowed value is 31. */ | 31 | for compatibility. The maximum allowed value is 31. */ |
32 | #define SSH_CIPHER_NOT_SET -1 /* None selected (invalid number). */ | 32 | #define SSH_CIPHER_NOT_SET -1 /* None selected (invalid number). */ |
33 | #define SSH_CIPHER_NONE 0 /* no encryption */ | 33 | #define SSH_CIPHER_NONE 0 /* no encryption */ |
34 | #define SSH_CIPHER_IDEA 1 /* IDEA CFB */ | 34 | #define SSH_CIPHER_IDEA 1 /* IDEA CFB */ |
35 | #define SSH_CIPHER_DES 2 /* DES CBC */ | 35 | #define SSH_CIPHER_DES 2 /* DES CBC */ |
36 | #define SSH_CIPHER_3DES 3 /* 3DES CBC */ | 36 | #define SSH_CIPHER_3DES 3 /* 3DES CBC */ |
37 | #define SSH_CIPHER_BROKEN_TSS 4 /* TRI's Simple Stream encryption CBC */ | 37 | #define SSH_CIPHER_BROKEN_TSS 4 /* TRI's Simple Stream encryption CBC */ |
38 | #define SSH_CIPHER_BROKEN_RC4 5 /* Alleged RC4 */ | 38 | #define SSH_CIPHER_BROKEN_RC4 5 /* Alleged RC4 */ |
39 | #define SSH_CIPHER_BLOWFISH 6 | 39 | #define SSH_CIPHER_BLOWFISH 6 |
40 | 40 | ||
41 | typedef struct { | 41 | typedef struct { |
42 | unsigned int type; | 42 | unsigned int type; |
43 | union { | 43 | union { |
44 | struct { | 44 | struct { |
45 | des_key_schedule key1; | 45 | des_key_schedule key1; |
46 | des_key_schedule key2; | 46 | des_key_schedule key2; |
47 | des_cblock iv2; | 47 | des_cblock iv2; |
48 | des_key_schedule key3; | 48 | des_key_schedule key3; |
49 | des_cblock iv3; | 49 | des_cblock iv3; |
50 | } des3; | 50 | } des3; |
51 | struct { | 51 | struct { |
52 | struct bf_key_st key; | 52 | struct bf_key_st key; |
53 | unsigned char iv[8]; | 53 | unsigned char iv[8]; |
54 | } bf; | 54 | } bf; |
55 | } u; | 55 | } u; |
56 | } CipherContext; | 56 | } CipherContext; |
57 | |||
58 | /* Returns a bit mask indicating which ciphers are supported by this | 57 | /* Returns a bit mask indicating which ciphers are supported by this |
59 | implementation. The bit mask has the corresponding bit set of each | 58 | implementation. The bit mask has the corresponding bit set of each |
60 | supported cipher. */ | 59 | supported cipher. */ |
@@ -65,28 +64,32 @@ const char *cipher_name(int cipher); | |||
65 | 64 | ||
66 | /* Parses the name of the cipher. Returns the number of the corresponding | 65 | /* Parses the name of the cipher. Returns the number of the corresponding |
67 | cipher, or -1 on error. */ | 66 | cipher, or -1 on error. */ |
68 | int cipher_number(const char *name); | 67 | int cipher_number(const char *name); |
69 | 68 | ||
70 | /* Selects the cipher to use and sets the key. If for_encryption is true, | 69 | /* Selects the cipher to use and sets the key. If for_encryption is true, |
71 | the key is setup for encryption; otherwise it is setup for decryption. */ | 70 | the key is setup for encryption; otherwise it is setup for decryption. */ |
72 | void cipher_set_key(CipherContext *context, int cipher, | 71 | void |
73 | const unsigned char *key, int keylen, int for_encryption); | 72 | cipher_set_key(CipherContext * context, int cipher, |
73 | const unsigned char *key, int keylen, int for_encryption); | ||
74 | 74 | ||
75 | /* Sets key for the cipher by computing the MD5 checksum of the passphrase, | 75 | /* Sets key for the cipher by computing the MD5 checksum of the passphrase, |
76 | and using the resulting 16 bytes as the key. */ | 76 | and using the resulting 16 bytes as the key. */ |
77 | void cipher_set_key_string(CipherContext *context, int cipher, | 77 | void |
78 | const char *passphrase, int for_encryption); | 78 | cipher_set_key_string(CipherContext * context, int cipher, |
79 | const char *passphrase, int for_encryption); | ||
79 | 80 | ||
80 | /* Encrypts data using the cipher. */ | 81 | /* Encrypts data using the cipher. */ |
81 | void cipher_encrypt(CipherContext *context, unsigned char *dest, | 82 | void |
82 | const unsigned char *src, unsigned int len); | 83 | cipher_encrypt(CipherContext * context, unsigned char *dest, |
84 | const unsigned char *src, unsigned int len); | ||
83 | 85 | ||
84 | /* Decrypts data using the cipher. */ | 86 | /* Decrypts data using the cipher. */ |
85 | void cipher_decrypt(CipherContext *context, unsigned char *dest, | 87 | void |
86 | const unsigned char *src, unsigned int len); | 88 | cipher_decrypt(CipherContext * context, unsigned char *dest, |
89 | const unsigned char *src, unsigned int len); | ||
87 | 90 | ||
88 | /* If and CRC-32 attack is detected this function is called. Defaults | 91 | /* If and CRC-32 attack is detected this function is called. Defaults |
89 | * to fatal, changed to packet_disconnect in sshd and ssh. */ | 92 | * to fatal, changed to packet_disconnect in sshd and ssh. */ |
90 | extern void (*cipher_attack_detected)(const char *fmt, ...); | 93 | extern void (*cipher_attack_detected) (const char *fmt,...); |
91 | 94 | ||
92 | #endif /* CIPHER_H */ | 95 | #endif /* CIPHER_H */ |
diff --git a/clientloop.c b/clientloop.c index 8e8d7627d..c49346c2c 100644 --- a/clientloop.c +++ b/clientloop.c | |||
@@ -1,21 +1,21 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * | |
3 | clientloop.c | 3 | * clientloop.c |
4 | 4 | * | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
6 | 6 | * | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
8 | All rights reserved | 8 | * All rights reserved |
9 | 9 | * | |
10 | 10 | * | |
11 | Created: Sat Sep 23 12:23:57 1995 ylo | 11 | * Created: Sat Sep 23 12:23:57 1995 ylo |
12 | 12 | * | |
13 | The main loop for the interactive session (client side). | 13 | * The main loop for the interactive session (client side). |
14 | 14 | * | |
15 | */ | 15 | */ |
16 | 16 | ||
17 | #include "includes.h" | 17 | #include "includes.h" |
18 | RCSID("$Id: clientloop.c,v 1.4 1999/11/21 02:23:53 damien Exp $"); | 18 | RCSID("$Id: clientloop.c,v 1.5 1999/11/24 13:26:22 damien Exp $"); |
19 | 19 | ||
20 | #include "xmalloc.h" | 20 | #include "xmalloc.h" |
21 | #include "ssh.h" | 21 | #include "ssh.h" |
@@ -49,292 +49,294 @@ static int in_raw_mode = 0; | |||
49 | static int in_non_blocking_mode = 0; | 49 | static int in_non_blocking_mode = 0; |
50 | 50 | ||
51 | /* Common data for the client loop code. */ | 51 | /* Common data for the client loop code. */ |
52 | static int escape_pending; /* Last character was the escape character */ | 52 | static int escape_pending; /* Last character was the escape character */ |
53 | static int last_was_cr; /* Last character was a newline. */ | 53 | static int last_was_cr; /* Last character was a newline. */ |
54 | static int exit_status; /* Used to store the exit status of the command. */ | 54 | static int exit_status; /* Used to store the exit status of the command. */ |
55 | static int stdin_eof; /* EOF has been encountered on standard error. */ | 55 | static int stdin_eof; /* EOF has been encountered on standard error. */ |
56 | static Buffer stdin_buffer; /* Buffer for stdin data. */ | 56 | static Buffer stdin_buffer; /* Buffer for stdin data. */ |
57 | static Buffer stdout_buffer; /* Buffer for stdout data. */ | 57 | static Buffer stdout_buffer; /* Buffer for stdout data. */ |
58 | static Buffer stderr_buffer; /* Buffer for stderr data. */ | 58 | static Buffer stderr_buffer; /* Buffer for stderr data. */ |
59 | static unsigned int buffer_high; /* Soft max buffer size. */ | 59 | static unsigned int buffer_high;/* Soft max buffer size. */ |
60 | static int max_fd; /* Maximum file descriptor number in select(). */ | 60 | static int max_fd; /* Maximum file descriptor number in select(). */ |
61 | static int connection_in; /* Connection to server (input). */ | 61 | static int connection_in; /* Connection to server (input). */ |
62 | static int connection_out; /* Connection to server (output). */ | 62 | static int connection_out; /* Connection to server (output). */ |
63 | static unsigned long stdin_bytes, stdout_bytes, stderr_bytes; | 63 | static unsigned long stdin_bytes, stdout_bytes, stderr_bytes; |
64 | static int quit_pending; /* Set to non-zero to quit the client loop. */ | 64 | static int quit_pending; /* Set to non-zero to quit the client loop. */ |
65 | static int escape_char; /* Escape character. */ | 65 | static int escape_char; /* Escape character. */ |
66 | 66 | ||
67 | /* Returns the user\'s terminal to normal mode if it had been put in raw | 67 | /* Returns the user\'s terminal to normal mode if it had been put in raw |
68 | mode. */ | 68 | mode. */ |
69 | 69 | ||
70 | void leave_raw_mode() | 70 | void |
71 | leave_raw_mode() | ||
71 | { | 72 | { |
72 | if (!in_raw_mode) | 73 | if (!in_raw_mode) |
73 | return; | 74 | return; |
74 | in_raw_mode = 0; | 75 | in_raw_mode = 0; |
75 | if (tcsetattr(fileno(stdin), TCSADRAIN, &saved_tio) < 0) | 76 | if (tcsetattr(fileno(stdin), TCSADRAIN, &saved_tio) < 0) |
76 | perror("tcsetattr"); | 77 | perror("tcsetattr"); |
77 | 78 | ||
78 | fatal_remove_cleanup((void (*)(void *))leave_raw_mode, NULL); | 79 | fatal_remove_cleanup((void (*) (void *)) leave_raw_mode, NULL); |
79 | } | 80 | } |
80 | 81 | ||
81 | /* Puts the user\'s terminal in raw mode. */ | 82 | /* Puts the user\'s terminal in raw mode. */ |
82 | 83 | ||
83 | void enter_raw_mode() | 84 | void |
85 | enter_raw_mode() | ||
84 | { | 86 | { |
85 | struct termios tio; | 87 | struct termios tio; |
86 | 88 | ||
87 | if (tcgetattr(fileno(stdin), &tio) < 0) | 89 | if (tcgetattr(fileno(stdin), &tio) < 0) |
88 | perror("tcgetattr"); | 90 | perror("tcgetattr"); |
89 | saved_tio = tio; | 91 | saved_tio = tio; |
90 | tio.c_iflag |= IGNPAR; | 92 | tio.c_iflag |= IGNPAR; |
91 | tio.c_iflag &= ~(ISTRIP|INLCR|IGNCR|ICRNL|IXON|IXANY|IXOFF); | 93 | tio.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF); |
92 | tio.c_lflag &= ~(ISIG|ICANON|ECHO|ECHOE|ECHOK|ECHONL); | 94 | tio.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL); |
93 | #ifdef IEXTEN | 95 | #ifdef IEXTEN |
94 | tio.c_lflag &= ~IEXTEN; | 96 | tio.c_lflag &= ~IEXTEN; |
95 | #endif /* IEXTEN */ | 97 | #endif /* IEXTEN */ |
96 | tio.c_oflag &= ~OPOST; | 98 | tio.c_oflag &= ~OPOST; |
97 | tio.c_cc[VMIN] = 1; | 99 | tio.c_cc[VMIN] = 1; |
98 | tio.c_cc[VTIME] = 0; | 100 | tio.c_cc[VTIME] = 0; |
99 | if (tcsetattr(fileno(stdin), TCSADRAIN, &tio) < 0) | 101 | if (tcsetattr(fileno(stdin), TCSADRAIN, &tio) < 0) |
100 | perror("tcsetattr"); | 102 | perror("tcsetattr"); |
101 | in_raw_mode = 1; | 103 | in_raw_mode = 1; |
102 | 104 | ||
103 | fatal_add_cleanup((void (*)(void *))leave_raw_mode, NULL); | 105 | fatal_add_cleanup((void (*) (void *)) leave_raw_mode, NULL); |
104 | } | 106 | } |
105 | |||
106 | /* Puts stdin terminal in non-blocking mode. */ | ||
107 | 107 | ||
108 | /* Restores stdin to blocking mode. */ | 108 | /* Restores stdin to blocking mode. */ |
109 | 109 | ||
110 | void leave_non_blocking() | 110 | void |
111 | leave_non_blocking() | ||
111 | { | 112 | { |
112 | if (in_non_blocking_mode) | 113 | if (in_non_blocking_mode) { |
113 | { | 114 | (void) fcntl(fileno(stdin), F_SETFL, 0); |
114 | (void)fcntl(fileno(stdin), F_SETFL, 0); | 115 | in_non_blocking_mode = 0; |
115 | in_non_blocking_mode = 0; | 116 | fatal_remove_cleanup((void (*) (void *)) leave_non_blocking, NULL); |
116 | fatal_remove_cleanup((void (*)(void *))leave_non_blocking, NULL); | 117 | } |
117 | } | ||
118 | } | 118 | } |
119 | 119 | ||
120 | void enter_non_blocking() | 120 | /* Puts stdin terminal in non-blocking mode. */ |
121 | |||
122 | void | ||
123 | enter_non_blocking() | ||
121 | { | 124 | { |
122 | in_non_blocking_mode = 1; | 125 | in_non_blocking_mode = 1; |
123 | (void)fcntl(fileno(stdin), F_SETFL, O_NONBLOCK); | 126 | (void) fcntl(fileno(stdin), F_SETFL, O_NONBLOCK); |
124 | fatal_add_cleanup((void (*)(void *))leave_non_blocking, NULL); | 127 | fatal_add_cleanup((void (*) (void *)) leave_non_blocking, NULL); |
125 | } | 128 | } |
126 | 129 | ||
127 | /* Signal handler for the window change signal (SIGWINCH). This just | 130 | /* Signal handler for the window change signal (SIGWINCH). This just |
128 | sets a flag indicating that the window has changed. */ | 131 | sets a flag indicating that the window has changed. */ |
129 | 132 | ||
130 | void window_change_handler(int sig) | 133 | void |
134 | window_change_handler(int sig) | ||
131 | { | 135 | { |
132 | received_window_change_signal = 1; | 136 | received_window_change_signal = 1; |
133 | signal(SIGWINCH, window_change_handler); | 137 | signal(SIGWINCH, window_change_handler); |
134 | } | 138 | } |
135 | 139 | ||
136 | /* Signal handler for signals that cause the program to terminate. These | 140 | /* Signal handler for signals that cause the program to terminate. These |
137 | signals must be trapped to restore terminal modes. */ | 141 | signals must be trapped to restore terminal modes. */ |
138 | 142 | ||
139 | void signal_handler(int sig) | 143 | void |
144 | signal_handler(int sig) | ||
140 | { | 145 | { |
141 | if (in_raw_mode) | 146 | if (in_raw_mode) |
142 | leave_raw_mode(); | 147 | leave_raw_mode(); |
143 | if (in_non_blocking_mode) | 148 | if (in_non_blocking_mode) |
144 | leave_non_blocking(); | 149 | leave_non_blocking(); |
145 | channel_stop_listening(); | 150 | channel_stop_listening(); |
146 | packet_close(); | 151 | packet_close(); |
147 | fatal("Killed by signal %d.", sig); | 152 | fatal("Killed by signal %d.", sig); |
148 | } | 153 | } |
149 | 154 | ||
150 | /* Returns current time in seconds from Jan 1, 1970 with the maximum available | 155 | /* Returns current time in seconds from Jan 1, 1970 with the maximum available |
151 | resolution. */ | 156 | resolution. */ |
152 | 157 | ||
153 | double get_current_time() | 158 | double |
159 | get_current_time() | ||
154 | { | 160 | { |
155 | struct timeval tv; | 161 | struct timeval tv; |
156 | gettimeofday(&tv, NULL); | 162 | gettimeofday(&tv, NULL); |
157 | return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0; | 163 | return (double) tv.tv_sec + (double) tv.tv_usec / 1000000.0; |
158 | } | 164 | } |
159 | 165 | ||
160 | /* This is called when the interactive is entered. This checks if there | 166 | /* This is called when the interactive is entered. This checks if there |
161 | is an EOF coming on stdin. We must check this explicitly, as select() | 167 | is an EOF coming on stdin. We must check this explicitly, as select() |
162 | does not appear to wake up when redirecting from /dev/null. */ | 168 | does not appear to wake up when redirecting from /dev/null. */ |
163 | 169 | ||
164 | void client_check_initial_eof_on_stdin() | 170 | void |
171 | client_check_initial_eof_on_stdin() | ||
165 | { | 172 | { |
166 | int len; | 173 | int len; |
167 | char buf[1]; | 174 | char buf[1]; |
168 | 175 | ||
169 | /* If standard input is to be "redirected from /dev/null", we simply | 176 | /* If standard input is to be "redirected from /dev/null", we |
170 | mark that we have seen an EOF and send an EOF message to the server. | 177 | simply mark that we have seen an EOF and send an EOF message to |
171 | Otherwise, we try to read a single character; it appears that for some | 178 | the server. Otherwise, we try to read a single character; it |
172 | files, such /dev/null, select() never wakes up for read for this | 179 | appears that for some files, such /dev/null, select() never |
173 | descriptor, which means that we never get EOF. This way we will get | 180 | wakes up for read for this descriptor, which means that we |
174 | the EOF if stdin comes from /dev/null or similar. */ | 181 | never get EOF. This way we will get the EOF if stdin comes |
175 | if (stdin_null_flag) | 182 | from /dev/null or similar. */ |
176 | { | 183 | if (stdin_null_flag) { |
177 | /* Fake EOF on stdin. */ | 184 | /* Fake EOF on stdin. */ |
178 | debug("Sending eof."); | 185 | debug("Sending eof."); |
179 | stdin_eof = 1; | 186 | stdin_eof = 1; |
180 | packet_start(SSH_CMSG_EOF); | 187 | packet_start(SSH_CMSG_EOF); |
181 | packet_send(); | 188 | packet_send(); |
182 | } | 189 | } else { |
183 | else | 190 | /* Enter non-blocking mode for stdin. */ |
184 | { | 191 | enter_non_blocking(); |
185 | /* Enter non-blocking mode for stdin. */ | 192 | |
186 | enter_non_blocking(); | 193 | /* Check for immediate EOF on stdin. */ |
187 | 194 | len = read(fileno(stdin), buf, 1); | |
188 | /* Check for immediate EOF on stdin. */ | 195 | if (len == 0) { |
189 | len = read(fileno(stdin), buf, 1); | 196 | /* EOF. Record that we have seen it and send EOF |
190 | if (len == 0) | 197 | to server. */ |
191 | { | 198 | debug("Sending eof."); |
192 | /* EOF. Record that we have seen it and send EOF to server. */ | 199 | stdin_eof = 1; |
193 | debug("Sending eof."); | 200 | packet_start(SSH_CMSG_EOF); |
194 | stdin_eof = 1; | 201 | packet_send(); |
195 | packet_start(SSH_CMSG_EOF); | 202 | } else if (len > 0) { |
196 | packet_send(); | 203 | /* Got data. We must store the data in the |
204 | buffer, and also process it as an escape | ||
205 | character if appropriate. */ | ||
206 | if ((unsigned char) buf[0] == escape_char) | ||
207 | escape_pending = 1; | ||
208 | else { | ||
209 | buffer_append(&stdin_buffer, buf, 1); | ||
210 | stdin_bytes += 1; | ||
211 | } | ||
212 | } | ||
213 | /* Leave non-blocking mode. */ | ||
214 | leave_non_blocking(); | ||
197 | } | 215 | } |
198 | else | ||
199 | if (len > 0) | ||
200 | { | ||
201 | /* Got data. We must store the data in the buffer, and also | ||
202 | process it as an escape character if appropriate. */ | ||
203 | if ((unsigned char)buf[0] == escape_char) | ||
204 | escape_pending = 1; | ||
205 | else | ||
206 | { | ||
207 | buffer_append(&stdin_buffer, buf, 1); | ||
208 | stdin_bytes += 1; | ||
209 | } | ||
210 | } | ||
211 | |||
212 | /* Leave non-blocking mode. */ | ||
213 | leave_non_blocking(); | ||
214 | } | ||
215 | } | 216 | } |
216 | 217 | ||
217 | /* Get packets from the connection input buffer, and process them as long | 218 | /* Get packets from the connection input buffer, and process them as long |
218 | as there are packets available. */ | 219 | as there are packets available. */ |
219 | 220 | ||
220 | void client_process_buffered_input_packets() | 221 | void |
222 | client_process_buffered_input_packets() | ||
221 | { | 223 | { |
222 | int type; | 224 | int type; |
223 | char *data; | 225 | char *data; |
224 | unsigned int data_len; | 226 | unsigned int data_len; |
225 | int payload_len; | 227 | int payload_len; |
226 | 228 | ||
227 | /* Process any buffered packets from the server. */ | 229 | /* Process any buffered packets from the server. */ |
228 | while (!quit_pending && (type = packet_read_poll(&payload_len)) != SSH_MSG_NONE) | 230 | while (!quit_pending && |
229 | { | 231 | (type = packet_read_poll(&payload_len)) != SSH_MSG_NONE) { |
230 | switch (type) | 232 | switch (type) { |
231 | { | 233 | |
232 | 234 | case SSH_SMSG_STDOUT_DATA: | |
233 | case SSH_SMSG_STDOUT_DATA: | 235 | data = packet_get_string(&data_len); |
234 | data = packet_get_string(&data_len); | 236 | packet_integrity_check(payload_len, 4 + data_len, type); |
235 | packet_integrity_check(payload_len, 4 + data_len, type); | 237 | buffer_append(&stdout_buffer, data, data_len); |
236 | buffer_append(&stdout_buffer, data, data_len); | 238 | stdout_bytes += data_len; |
237 | stdout_bytes += data_len; | 239 | memset(data, 0, data_len); |
238 | memset(data, 0, data_len); | 240 | xfree(data); |
239 | xfree(data); | 241 | break; |
240 | break; | 242 | |
241 | 243 | case SSH_SMSG_STDERR_DATA: | |
242 | case SSH_SMSG_STDERR_DATA: | 244 | data = packet_get_string(&data_len); |
243 | data = packet_get_string(&data_len); | 245 | packet_integrity_check(payload_len, 4 + data_len, type); |
244 | packet_integrity_check(payload_len, 4 + data_len, type); | 246 | buffer_append(&stderr_buffer, data, data_len); |
245 | buffer_append(&stderr_buffer, data, data_len); | 247 | stdout_bytes += data_len; |
246 | stdout_bytes += data_len; | 248 | memset(data, 0, data_len); |
247 | memset(data, 0, data_len); | 249 | xfree(data); |
248 | xfree(data); | 250 | break; |
249 | break; | 251 | |
250 | 252 | case SSH_SMSG_EXITSTATUS: | |
251 | case SSH_SMSG_EXITSTATUS: | 253 | packet_integrity_check(payload_len, 4, type); |
252 | packet_integrity_check(payload_len, 4, type); | 254 | exit_status = packet_get_int(); |
253 | exit_status = packet_get_int(); | 255 | /* Acknowledge the exit. */ |
254 | /* Acknowledge the exit. */ | 256 | packet_start(SSH_CMSG_EXIT_CONFIRMATION); |
255 | packet_start(SSH_CMSG_EXIT_CONFIRMATION); | 257 | packet_send(); |
256 | packet_send(); | 258 | /* Must wait for packet to be sent since we are |
257 | /* Must wait for packet to be sent since we are exiting the | 259 | exiting the loop. */ |
258 | loop. */ | 260 | packet_write_wait(); |
259 | packet_write_wait(); | 261 | /* Flag that we want to exit. */ |
260 | /* Flag that we want to exit. */ | 262 | quit_pending = 1; |
261 | quit_pending = 1; | 263 | break; |
262 | break; | 264 | |
263 | 265 | case SSH_SMSG_X11_OPEN: | |
264 | case SSH_SMSG_X11_OPEN: | 266 | x11_input_open(payload_len); |
265 | x11_input_open(payload_len); | 267 | break; |
266 | break; | 268 | |
267 | 269 | case SSH_MSG_PORT_OPEN: | |
268 | case SSH_MSG_PORT_OPEN: | 270 | channel_input_port_open(payload_len); |
269 | channel_input_port_open(payload_len); | 271 | break; |
270 | break; | 272 | |
271 | 273 | case SSH_SMSG_AGENT_OPEN: | |
272 | case SSH_SMSG_AGENT_OPEN: | 274 | packet_integrity_check(payload_len, 4, type); |
273 | packet_integrity_check(payload_len, 4, type); | 275 | auth_input_open_request(); |
274 | auth_input_open_request(); | 276 | break; |
275 | break; | 277 | |
276 | 278 | case SSH_MSG_CHANNEL_OPEN_CONFIRMATION: | |
277 | case SSH_MSG_CHANNEL_OPEN_CONFIRMATION: | 279 | packet_integrity_check(payload_len, 4 + 4, type); |
278 | packet_integrity_check(payload_len, 4 + 4, type); | 280 | channel_input_open_confirmation(); |
279 | channel_input_open_confirmation(); | 281 | break; |
280 | break; | 282 | |
281 | 283 | case SSH_MSG_CHANNEL_OPEN_FAILURE: | |
282 | case SSH_MSG_CHANNEL_OPEN_FAILURE: | 284 | packet_integrity_check(payload_len, 4, type); |
283 | packet_integrity_check(payload_len, 4, type); | 285 | channel_input_open_failure(); |
284 | channel_input_open_failure(); | 286 | break; |
285 | break; | 287 | |
286 | 288 | case SSH_MSG_CHANNEL_DATA: | |
287 | case SSH_MSG_CHANNEL_DATA: | 289 | channel_input_data(payload_len); |
288 | channel_input_data(payload_len); | 290 | break; |
289 | break; | 291 | |
290 | 292 | case SSH_MSG_CHANNEL_CLOSE: | |
291 | case SSH_MSG_CHANNEL_CLOSE: | 293 | packet_integrity_check(payload_len, 4, type); |
292 | packet_integrity_check(payload_len, 4, type); | 294 | channel_input_close(); |
293 | channel_input_close(); | 295 | break; |
294 | break; | 296 | |
295 | 297 | case SSH_MSG_CHANNEL_CLOSE_CONFIRMATION: | |
296 | case SSH_MSG_CHANNEL_CLOSE_CONFIRMATION: | 298 | packet_integrity_check(payload_len, 4, type); |
297 | packet_integrity_check(payload_len, 4, type); | 299 | channel_input_close_confirmation(); |
298 | channel_input_close_confirmation(); | 300 | break; |
299 | break; | 301 | |
300 | 302 | default: | |
301 | default: | 303 | /* Any unknown packets received during the actual |
302 | /* Any unknown packets received during the actual session | 304 | session cause the session to terminate. This |
303 | cause the session to terminate. This is intended to make | 305 | is intended to make debugging easier since no |
304 | debugging easier since no confirmations are sent. Any | 306 | confirmations are sent. Any compatible |
305 | compatible protocol extensions must be negotiated during | 307 | protocol extensions must be negotiated during |
306 | the preparatory phase. */ | 308 | the preparatory phase. */ |
307 | packet_disconnect("Protocol error during session: type %d", | 309 | packet_disconnect("Protocol error during session: type %d", |
308 | type); | 310 | type); |
311 | } | ||
309 | } | 312 | } |
310 | } | ||
311 | } | 313 | } |
312 | 314 | ||
313 | /* Make packets from buffered stdin data, and buffer them for sending to | 315 | /* Make packets from buffered stdin data, and buffer them for sending to |
314 | the connection. */ | 316 | the connection. */ |
315 | 317 | ||
316 | void client_make_packets_from_stdin_data() | 318 | void |
319 | client_make_packets_from_stdin_data() | ||
317 | { | 320 | { |
318 | unsigned int len; | 321 | unsigned int len; |
319 | 322 | ||
320 | /* Send buffered stdin data to the server. */ | 323 | /* Send buffered stdin data to the server. */ |
321 | while (buffer_len(&stdin_buffer) > 0 && | 324 | while (buffer_len(&stdin_buffer) > 0 && |
322 | packet_not_very_much_data_to_write()) | 325 | packet_not_very_much_data_to_write()) { |
323 | { | 326 | len = buffer_len(&stdin_buffer); |
324 | len = buffer_len(&stdin_buffer); | 327 | /* Keep the packets at reasonable size. */ |
325 | if (len > packet_get_maxsize()) | 328 | if (len > packet_get_maxsize()) |
326 | len = packet_get_maxsize(); /* Keep the packets at reasonable size. */ | 329 | len = packet_get_maxsize(); |
327 | packet_start(SSH_CMSG_STDIN_DATA); | 330 | packet_start(SSH_CMSG_STDIN_DATA); |
328 | packet_put_string(buffer_ptr(&stdin_buffer), len); | 331 | packet_put_string(buffer_ptr(&stdin_buffer), len); |
329 | packet_send(); | 332 | packet_send(); |
330 | buffer_consume(&stdin_buffer, len); | 333 | buffer_consume(&stdin_buffer, len); |
331 | /* If we have a pending EOF, send it now. */ | 334 | /* If we have a pending EOF, send it now. */ |
332 | if (stdin_eof && buffer_len(&stdin_buffer) == 0) | 335 | if (stdin_eof && buffer_len(&stdin_buffer) == 0) { |
333 | { | 336 | packet_start(SSH_CMSG_EOF); |
334 | packet_start(SSH_CMSG_EOF); | 337 | packet_send(); |
335 | packet_send(); | 338 | } |
336 | } | 339 | } |
337 | } | ||
338 | } | 340 | } |
339 | 341 | ||
340 | /* Checks if the client window has changed, and sends a packet about it to | 342 | /* Checks if the client window has changed, and sends a packet about it to |
@@ -342,303 +344,286 @@ void client_make_packets_from_stdin_data() | |||
342 | interrupt on Unix); this just checks the flag and sends a message if | 344 | interrupt on Unix); this just checks the flag and sends a message if |
343 | appropriate. */ | 345 | appropriate. */ |
344 | 346 | ||
345 | void client_check_window_change() | 347 | void |
348 | client_check_window_change() | ||
346 | { | 349 | { |
347 | /* Send possible window change message to the server. */ | 350 | /* Send possible window change message to the server. */ |
348 | if (received_window_change_signal) | 351 | if (received_window_change_signal) { |
349 | { | 352 | struct winsize ws; |
350 | struct winsize ws; | 353 | |
351 | 354 | /* Clear the window change indicator. */ | |
352 | /* Clear the window change indicator. */ | 355 | received_window_change_signal = 0; |
353 | received_window_change_signal = 0; | 356 | |
354 | 357 | /* Read new window size. */ | |
355 | /* Read new window size. */ | 358 | if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) >= 0) { |
356 | if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) >= 0) | 359 | /* Successful, send the packet now. */ |
357 | { | 360 | packet_start(SSH_CMSG_WINDOW_SIZE); |
358 | /* Successful, send the packet now. */ | 361 | packet_put_int(ws.ws_row); |
359 | packet_start(SSH_CMSG_WINDOW_SIZE); | 362 | packet_put_int(ws.ws_col); |
360 | packet_put_int(ws.ws_row); | 363 | packet_put_int(ws.ws_xpixel); |
361 | packet_put_int(ws.ws_col); | 364 | packet_put_int(ws.ws_ypixel); |
362 | packet_put_int(ws.ws_xpixel); | 365 | packet_send(); |
363 | packet_put_int(ws.ws_ypixel); | 366 | } |
364 | packet_send(); | ||
365 | } | 367 | } |
366 | } | ||
367 | } | 368 | } |
368 | 369 | ||
369 | /* Waits until the client can do something (some data becomes available on | 370 | /* Waits until the client can do something (some data becomes available on |
370 | one of the file descriptors). */ | 371 | one of the file descriptors). */ |
371 | 372 | ||
372 | void client_wait_until_can_do_something(fd_set *readset, fd_set *writeset) | 373 | void |
374 | client_wait_until_can_do_something(fd_set * readset, fd_set * writeset) | ||
373 | { | 375 | { |
374 | /* Initialize select masks. */ | 376 | /* Initialize select masks. */ |
375 | FD_ZERO(readset); | 377 | FD_ZERO(readset); |
376 | 378 | ||
377 | /* Read from the connection, unless our buffers are full. */ | 379 | /* Read from the connection, unless our buffers are full. */ |
378 | if (buffer_len(&stdout_buffer) < buffer_high && | 380 | if (buffer_len(&stdout_buffer) < buffer_high && |
379 | buffer_len(&stderr_buffer) < buffer_high && | 381 | buffer_len(&stderr_buffer) < buffer_high && |
380 | channel_not_very_much_buffered_data()) | 382 | channel_not_very_much_buffered_data()) |
381 | FD_SET(connection_in, readset); | 383 | FD_SET(connection_in, readset); |
382 | 384 | ||
383 | /* Read from stdin, unless we have seen EOF or have very much buffered | 385 | /* Read from stdin, unless we have seen EOF or have very much |
384 | data to send to the server. */ | 386 | buffered data to send to the server. */ |
385 | if (!stdin_eof && packet_not_very_much_data_to_write()) | 387 | if (!stdin_eof && packet_not_very_much_data_to_write()) |
386 | FD_SET(fileno(stdin), readset); | 388 | FD_SET(fileno(stdin), readset); |
387 | 389 | ||
388 | FD_ZERO(writeset); | 390 | FD_ZERO(writeset); |
389 | 391 | ||
390 | /* Add any selections by the channel mechanism. */ | 392 | /* Add any selections by the channel mechanism. */ |
391 | channel_prepare_select(readset, writeset); | 393 | channel_prepare_select(readset, writeset); |
392 | 394 | ||
393 | /* Select server connection if have data to write to the server. */ | 395 | /* Select server connection if have data to write to the server. */ |
394 | if (packet_have_data_to_write()) | 396 | if (packet_have_data_to_write()) |
395 | FD_SET(connection_out, writeset); | 397 | FD_SET(connection_out, writeset); |
396 | 398 | ||
397 | /* Select stdout if have data in buffer. */ | 399 | /* Select stdout if have data in buffer. */ |
398 | if (buffer_len(&stdout_buffer) > 0) | 400 | if (buffer_len(&stdout_buffer) > 0) |
399 | FD_SET(fileno(stdout), writeset); | 401 | FD_SET(fileno(stdout), writeset); |
400 | 402 | ||
401 | /* Select stderr if have data in buffer. */ | 403 | /* Select stderr if have data in buffer. */ |
402 | if (buffer_len(&stderr_buffer) > 0) | 404 | if (buffer_len(&stderr_buffer) > 0) |
403 | FD_SET(fileno(stderr), writeset); | 405 | FD_SET(fileno(stderr), writeset); |
404 | 406 | ||
405 | /* Update maximum file descriptor number, if appropriate. */ | 407 | /* Update maximum file descriptor number, if appropriate. */ |
406 | if (channel_max_fd() > max_fd) | 408 | if (channel_max_fd() > max_fd) |
407 | max_fd = channel_max_fd(); | 409 | max_fd = channel_max_fd(); |
408 | 410 | ||
409 | /* Wait for something to happen. This will suspend the process until | 411 | /* Wait for something to happen. This will suspend the process |
410 | some selected descriptor can be read, written, or has some other | 412 | until some selected descriptor can be read, written, or has |
411 | event pending. Note: if you want to implement SSH_MSG_IGNORE | 413 | some other event pending. |
412 | messages to fool traffic analysis, this might be the place to do | 414 | Note: if you want to implement SSH_MSG_IGNORE messages to fool |
413 | it: just have a random timeout for the select, and send a random | 415 | traffic analysis, this might be the place to do it: |
414 | SSH_MSG_IGNORE packet when the timeout expires. */ | 416 | just have a random timeout for the select, and send a random |
415 | if (select(max_fd + 1, readset, writeset, NULL, NULL) < 0) | 417 | SSH_MSG_IGNORE packet when the timeout expires. */ |
416 | { | 418 | |
417 | char buf[100]; | 419 | if (select(max_fd + 1, readset, writeset, NULL, NULL) < 0) { |
418 | /* Some systems fail to clear these automatically. */ | 420 | char buf[100]; |
419 | FD_ZERO(readset); | 421 | /* Some systems fail to clear these automatically. */ |
420 | FD_ZERO(writeset); | 422 | FD_ZERO(readset); |
421 | if (errno == EINTR) | 423 | FD_ZERO(writeset); |
422 | return; | 424 | if (errno == EINTR) |
423 | /* Note: we might still have data in the buffers. */ | 425 | return; |
424 | snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno)); | 426 | /* Note: we might still have data in the buffers. */ |
425 | buffer_append(&stderr_buffer, buf, strlen(buf)); | 427 | snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno)); |
426 | stderr_bytes += strlen(buf); | 428 | buffer_append(&stderr_buffer, buf, strlen(buf)); |
427 | quit_pending = 1; | 429 | stderr_bytes += strlen(buf); |
428 | } | 430 | quit_pending = 1; |
431 | } | ||
429 | } | 432 | } |
430 | 433 | ||
431 | void client_suspend_self() | 434 | void |
435 | client_suspend_self() | ||
432 | { | 436 | { |
433 | struct winsize oldws, newws; | 437 | struct winsize oldws, newws; |
434 | 438 | ||
435 | /* Flush stdout and stderr buffers. */ | 439 | /* Flush stdout and stderr buffers. */ |
436 | if (buffer_len(&stdout_buffer) > 0) | 440 | if (buffer_len(&stdout_buffer) > 0) |
437 | write(fileno(stdout), | 441 | write(fileno(stdout), |
438 | buffer_ptr(&stdout_buffer), | 442 | buffer_ptr(&stdout_buffer), |
439 | buffer_len(&stdout_buffer)); | 443 | buffer_len(&stdout_buffer)); |
440 | if (buffer_len(&stderr_buffer) > 0) | 444 | if (buffer_len(&stderr_buffer) > 0) |
441 | write(fileno(stderr), | 445 | write(fileno(stderr), |
442 | buffer_ptr(&stderr_buffer), | 446 | buffer_ptr(&stderr_buffer), |
443 | buffer_len(&stderr_buffer)); | 447 | buffer_len(&stderr_buffer)); |
444 | 448 | ||
445 | /* Leave raw mode. */ | 449 | /* Leave raw mode. */ |
446 | leave_raw_mode(); | 450 | leave_raw_mode(); |
447 | 451 | ||
448 | /* Free (and clear) the buffer to reduce the | 452 | /* Free (and clear) the buffer to reduce the amount of data that |
449 | amount of data that gets written to swap. */ | 453 | gets written to swap. */ |
450 | buffer_free(&stdin_buffer); | 454 | buffer_free(&stdin_buffer); |
451 | buffer_free(&stdout_buffer); | 455 | buffer_free(&stdout_buffer); |
452 | buffer_free(&stderr_buffer); | 456 | buffer_free(&stderr_buffer); |
453 | 457 | ||
454 | /* Save old window size. */ | 458 | /* Save old window size. */ |
455 | ioctl(fileno(stdin), TIOCGWINSZ, &oldws); | 459 | ioctl(fileno(stdin), TIOCGWINSZ, &oldws); |
456 | 460 | ||
457 | /* Send the suspend signal to the program | 461 | /* Send the suspend signal to the program itself. */ |
458 | itself. */ | 462 | kill(getpid(), SIGTSTP); |
459 | kill(getpid(), SIGTSTP); | 463 | |
460 | 464 | /* Check if the window size has changed. */ | |
461 | /* Check if the window size has changed. */ | 465 | if (ioctl(fileno(stdin), TIOCGWINSZ, &newws) >= 0 && |
462 | if (ioctl(fileno(stdin), TIOCGWINSZ, &newws) >= 0 && | 466 | (oldws.ws_row != newws.ws_row || |
463 | (oldws.ws_row != newws.ws_row || oldws.ws_col != newws.ws_col || | 467 | oldws.ws_col != newws.ws_col || |
464 | oldws.ws_xpixel != newws.ws_xpixel || | 468 | oldws.ws_xpixel != newws.ws_xpixel || |
465 | oldws.ws_ypixel != newws.ws_ypixel)) | 469 | oldws.ws_ypixel != newws.ws_ypixel)) |
466 | received_window_change_signal = 1; | 470 | received_window_change_signal = 1; |
467 | 471 | ||
468 | /* OK, we have been continued by the user. | 472 | /* OK, we have been continued by the user. Reinitialize buffers. */ |
469 | Reinitialize buffers. */ | 473 | buffer_init(&stdin_buffer); |
470 | buffer_init(&stdin_buffer); | 474 | buffer_init(&stdout_buffer); |
471 | buffer_init(&stdout_buffer); | 475 | buffer_init(&stderr_buffer); |
472 | buffer_init(&stderr_buffer); | 476 | |
473 | 477 | /* Re-enter raw mode. */ | |
474 | /* Re-enter raw mode. */ | 478 | enter_raw_mode(); |
475 | enter_raw_mode(); | ||
476 | } | 479 | } |
477 | 480 | ||
478 | void client_process_input(fd_set *readset) | 481 | void |
482 | client_process_input(fd_set * readset) | ||
479 | { | 483 | { |
480 | int len, pid; | 484 | int len, pid; |
481 | char buf[8192], *s; | 485 | char buf[8192], *s; |
482 | 486 | ||
483 | /* Read input from the server, and add any such data to the buffer of the | 487 | /* Read input from the server, and add any such data to the buffer |
484 | packet subsystem. */ | 488 | of the packet subsystem. */ |
485 | if (FD_ISSET(connection_in, readset)) | 489 | if (FD_ISSET(connection_in, readset)) { |
486 | { | 490 | /* Read as much as possible. */ |
487 | /* Read as much as possible. */ | 491 | len = read(connection_in, buf, sizeof(buf)); |
488 | len = read(connection_in, buf, sizeof(buf)); | 492 | if (len == 0) { |
489 | if (len == 0) | 493 | /* Received EOF. The remote host has closed the connection. */ |
490 | { | 494 | snprintf(buf, sizeof buf, "Connection to %.300s closed by remote host.\r\n", |
491 | /* Received EOF. The remote host has closed the connection. */ | 495 | host); |
492 | snprintf(buf, sizeof buf, "Connection to %.300s closed by remote host.\r\n", | ||
493 | host); | ||
494 | buffer_append(&stderr_buffer, buf, strlen(buf)); | ||
495 | stderr_bytes += strlen(buf); | ||
496 | quit_pending = 1; | ||
497 | return; | ||
498 | } | ||
499 | |||
500 | /* There is a kernel bug on Solaris that causes select to sometimes | ||
501 | wake up even though there is no data available. */ | ||
502 | if (len < 0 && errno == EAGAIN) | ||
503 | len = 0; | ||
504 | |||
505 | if (len < 0) | ||
506 | { | ||
507 | /* An error has encountered. Perhaps there is a network | ||
508 | problem. */ | ||
509 | snprintf(buf, sizeof buf, "Read from remote host %.300s: %.100s\r\n", | ||
510 | host, strerror(errno)); | ||
511 | buffer_append(&stderr_buffer, buf, strlen(buf)); | ||
512 | stderr_bytes += strlen(buf); | ||
513 | quit_pending = 1; | ||
514 | return; | ||
515 | } | ||
516 | packet_process_incoming(buf, len); | ||
517 | } | ||
518 | |||
519 | /* Read input from stdin. */ | ||
520 | if (FD_ISSET(fileno(stdin), readset)) | ||
521 | { | ||
522 | /* Read as much as possible. */ | ||
523 | len = read(fileno(stdin), buf, sizeof(buf)); | ||
524 | if (len <= 0) | ||
525 | { | ||
526 | /* Received EOF or error. They are treated similarly, | ||
527 | except that an error message is printed if it was | ||
528 | an error condition. */ | ||
529 | if (len < 0) | ||
530 | { | ||
531 | snprintf(buf, sizeof buf, "read: %.100s\r\n", strerror(errno)); | ||
532 | buffer_append(&stderr_buffer, buf, strlen(buf)); | ||
533 | stderr_bytes += strlen(buf); | ||
534 | } | ||
535 | /* Mark that we have seen EOF. */ | ||
536 | stdin_eof = 1; | ||
537 | /* Send an EOF message to the server unless there is data | ||
538 | in the buffer. If there is data in the buffer, no message | ||
539 | will be sent now. Code elsewhere will send the EOF | ||
540 | when the buffer becomes empty if stdin_eof is set. */ | ||
541 | if (buffer_len(&stdin_buffer) == 0) | ||
542 | { | ||
543 | packet_start(SSH_CMSG_EOF); | ||
544 | packet_send(); | ||
545 | } | ||
546 | } | ||
547 | else | ||
548 | if (escape_char == -1) | ||
549 | { | ||
550 | /* Normal successful read, and no escape character. Just | ||
551 | append the data to buffer. */ | ||
552 | buffer_append(&stdin_buffer, buf, len); | ||
553 | stdin_bytes += len; | ||
554 | } | ||
555 | else | ||
556 | { | ||
557 | /* Normal, successful read. But we have an escape character | ||
558 | and have to process the characters one by one. */ | ||
559 | unsigned int i; | ||
560 | for (i = 0; i < len; i++) | ||
561 | { | ||
562 | unsigned char ch; | ||
563 | /* Get one character at a time. */ | ||
564 | ch = buf[i]; | ||
565 | |||
566 | /* Check if we have a pending escape character. */ | ||
567 | if (escape_pending) | ||
568 | { | ||
569 | /* We have previously seen an escape character. */ | ||
570 | /* Clear the flag now. */ | ||
571 | escape_pending = 0; | ||
572 | /* Process the escaped character. */ | ||
573 | switch (ch) | ||
574 | { | ||
575 | case '.': | ||
576 | /* Terminate the connection. */ | ||
577 | snprintf(buf, sizeof buf, "%c.\r\n", escape_char); | ||
578 | buffer_append(&stderr_buffer, buf, strlen(buf)); | 496 | buffer_append(&stderr_buffer, buf, strlen(buf)); |
579 | stderr_bytes += strlen(buf); | 497 | stderr_bytes += strlen(buf); |
580 | quit_pending = 1; | 498 | quit_pending = 1; |
581 | return; | 499 | return; |
582 | 500 | } | |
583 | case 'Z' - 64: | 501 | /* There is a kernel bug on Solaris that causes select to |
584 | /* Suspend the program. */ | 502 | sometimes wake up even though there is no data |
585 | /* Print a message to that effect to the user. */ | 503 | available. */ |
586 | snprintf(buf, sizeof buf, "%c^Z\r\n", escape_char); | 504 | if (len < 0 && errno == EAGAIN) |
587 | buffer_append(&stderr_buffer, buf, strlen(buf)); | 505 | len = 0; |
588 | stderr_bytes += strlen(buf); | 506 | |
589 | 507 | if (len < 0) { | |
590 | /* Restore terminal modes and suspend. */ | 508 | /* An error has encountered. Perhaps there is a network problem. */ |
591 | client_suspend_self(); | 509 | snprintf(buf, sizeof buf, "Read from remote host %.300s: %.100s\r\n", |
592 | 510 | host, strerror(errno)); | |
593 | /* We have been continued. */ | 511 | buffer_append(&stderr_buffer, buf, strlen(buf)); |
594 | continue; | 512 | stderr_bytes += strlen(buf); |
595 | 513 | quit_pending = 1; | |
596 | case '&': | 514 | return; |
597 | /* Detach the program (continue to serve connections, | 515 | } |
598 | but put in background and no more new | 516 | packet_process_incoming(buf, len); |
599 | connections). */ | 517 | } |
600 | if (!stdin_eof) | 518 | /* Read input from stdin. */ |
601 | { | 519 | if (FD_ISSET(fileno(stdin), readset)) { |
602 | /* Sending SSH_CMSG_EOF alone does not always | 520 | /* Read as much as possible. */ |
603 | appear to be enough. So we try to send an | 521 | len = read(fileno(stdin), buf, sizeof(buf)); |
604 | EOF character first. */ | 522 | if (len <= 0) { |
605 | packet_start(SSH_CMSG_STDIN_DATA); | 523 | /* Received EOF or error. They are treated |
606 | packet_put_string("\004", 1); | 524 | similarly, except that an error message is |
607 | packet_send(); | 525 | printed if it was an error condition. */ |
608 | /* Close stdin. */ | 526 | if (len < 0) { |
609 | stdin_eof = 1; | 527 | snprintf(buf, sizeof buf, "read: %.100s\r\n", strerror(errno)); |
610 | if (buffer_len(&stdin_buffer) == 0) | 528 | buffer_append(&stderr_buffer, buf, strlen(buf)); |
611 | { | 529 | stderr_bytes += strlen(buf); |
530 | } | ||
531 | /* Mark that we have seen EOF. */ | ||
532 | stdin_eof = 1; | ||
533 | /* Send an EOF message to the server unless there | ||
534 | is data in the buffer. If there is data in the | ||
535 | buffer, no message will be sent now. Code | ||
536 | elsewhere will send the EOF when the buffer | ||
537 | becomes empty if stdin_eof is set. */ | ||
538 | if (buffer_len(&stdin_buffer) == 0) { | ||
612 | packet_start(SSH_CMSG_EOF); | 539 | packet_start(SSH_CMSG_EOF); |
613 | packet_send(); | 540 | packet_send(); |
614 | } | 541 | } |
615 | } | 542 | } else if (escape_char == -1) { |
616 | /* Restore tty modes. */ | 543 | /* Normal successful read, and no escape |
617 | leave_raw_mode(); | 544 | character. Just append the data to buffer. */ |
618 | 545 | buffer_append(&stdin_buffer, buf, len); | |
619 | /* Stop listening for new connections. */ | 546 | stdin_bytes += len; |
620 | channel_stop_listening(); | 547 | } else { |
621 | 548 | /* Normal, successful read. But we have an escape | |
622 | printf("%c& [backgrounded]\n", escape_char); | 549 | character and have to process the characters |
623 | 550 | one by one. */ | |
624 | /* Fork into background. */ | 551 | unsigned int i; |
625 | pid = fork(); | 552 | for (i = 0; i < len; i++) { |
626 | if (pid < 0) | 553 | unsigned char ch; |
627 | { | 554 | /* Get one character at a time. */ |
628 | error("fork: %.100s", strerror(errno)); | 555 | ch = buf[i]; |
629 | continue; | 556 | |
630 | } | 557 | /* Check if we have a pending escape |
631 | if (pid != 0) | 558 | character. */ |
632 | { /* This is the parent. */ | 559 | if (escape_pending) { |
633 | /* The parent just exits. */ | 560 | /* We have previously seen an escape character. */ |
634 | exit(0); | 561 | /* Clear the flag now. */ |
635 | } | 562 | escape_pending = 0; |
636 | 563 | /* Process the escaped character. */ | |
637 | /* The child continues serving connections. */ | 564 | switch (ch) { |
638 | continue; | 565 | case '.': |
639 | 566 | /* Terminate the connection. */ | |
640 | case '?': | 567 | snprintf(buf, sizeof buf, "%c.\r\n", escape_char); |
641 | snprintf(buf, sizeof buf, "%c?\r\n\ | 568 | buffer_append(&stderr_buffer, buf, strlen(buf)); |
569 | stderr_bytes += strlen(buf); | ||
570 | quit_pending = 1; | ||
571 | return; | ||
572 | |||
573 | case 'Z' - 64: | ||
574 | /* Suspend the program. */ | ||
575 | /* Print a message to that effect to the user. */ | ||
576 | snprintf(buf, sizeof buf, "%c^Z\r\n", escape_char); | ||
577 | buffer_append(&stderr_buffer, buf, strlen(buf)); | ||
578 | stderr_bytes += strlen(buf); | ||
579 | |||
580 | /* Restore terminal modes and suspend. */ | ||
581 | client_suspend_self(); | ||
582 | |||
583 | /* We have been continued. */ | ||
584 | continue; | ||
585 | |||
586 | case '&': | ||
587 | /* Detach the program (continue to serve connections, | ||
588 | but put in background and no more new connections). */ | ||
589 | if (!stdin_eof) { | ||
590 | /* Sending SSH_CMSG_EOF alone does not always appear | ||
591 | to be enough. So we try to send an EOF character | ||
592 | first. */ | ||
593 | packet_start(SSH_CMSG_STDIN_DATA); | ||
594 | packet_put_string("\004", 1); | ||
595 | packet_send(); | ||
596 | /* Close stdin. */ | ||
597 | stdin_eof = 1; | ||
598 | if (buffer_len(&stdin_buffer) == 0) { | ||
599 | packet_start(SSH_CMSG_EOF); | ||
600 | packet_send(); | ||
601 | } | ||
602 | } | ||
603 | /* Restore tty modes. */ | ||
604 | leave_raw_mode(); | ||
605 | |||
606 | /* Stop listening for new connections. */ | ||
607 | channel_stop_listening(); | ||
608 | |||
609 | printf("%c& [backgrounded]\n", escape_char); | ||
610 | |||
611 | /* Fork into background. */ | ||
612 | pid = fork(); | ||
613 | if (pid < 0) { | ||
614 | error("fork: %.100s", strerror(errno)); | ||
615 | continue; | ||
616 | } | ||
617 | if (pid != 0) { /* This is the parent. */ | ||
618 | /* The parent just exits. */ | ||
619 | exit(0); | ||
620 | } | ||
621 | /* The child continues serving connections. */ | ||
622 | continue; | ||
623 | |||
624 | case '?': | ||
625 | snprintf(buf, sizeof buf, | ||
626 | "%c?\r\n\ | ||
642 | Supported escape sequences:\r\n\ | 627 | Supported escape sequences:\r\n\ |
643 | ~. - terminate connection\r\n\ | 628 | ~. - terminate connection\r\n\ |
644 | ~^Z - suspend ssh\r\n\ | 629 | ~^Z - suspend ssh\r\n\ |
@@ -647,110 +632,100 @@ Supported escape sequences:\r\n\ | |||
647 | ~? - this message\r\n\ | 632 | ~? - this message\r\n\ |
648 | ~~ - send the escape character by typing it twice\r\n\ | 633 | ~~ - send the escape character by typing it twice\r\n\ |
649 | (Note that escapes are only recognized immediately after newline.)\r\n", | 634 | (Note that escapes are only recognized immediately after newline.)\r\n", |
650 | escape_char); | 635 | escape_char); |
651 | buffer_append(&stderr_buffer, buf, strlen(buf)); | 636 | buffer_append(&stderr_buffer, buf, strlen(buf)); |
652 | continue; | 637 | continue; |
653 | 638 | ||
654 | case '#': | 639 | case '#': |
655 | snprintf(buf, sizeof buf, "%c#\r\n", escape_char); | 640 | snprintf(buf, sizeof buf, "%c#\r\n", escape_char); |
656 | buffer_append(&stderr_buffer, buf, strlen(buf)); | 641 | buffer_append(&stderr_buffer, buf, strlen(buf)); |
657 | s = channel_open_message(); | 642 | s = channel_open_message(); |
658 | buffer_append(&stderr_buffer, s, strlen(s)); | 643 | buffer_append(&stderr_buffer, s, strlen(s)); |
659 | xfree(s); | 644 | xfree(s); |
660 | continue; | 645 | continue; |
661 | 646 | ||
662 | default: | 647 | default: |
663 | if (ch != escape_char) | 648 | if (ch != escape_char) { |
664 | { | 649 | /* Escape character followed by non-special character. |
665 | /* Escape character followed by non-special | 650 | Append both to the input buffer. */ |
666 | character. Append both to the input | 651 | buf[0] = escape_char; |
667 | buffer. */ | 652 | buf[1] = ch; |
668 | buf[0] = escape_char; | 653 | buffer_append(&stdin_buffer, buf, 2); |
669 | buf[1] = ch; | 654 | stdin_bytes += 2; |
670 | buffer_append(&stdin_buffer, buf, 2); | 655 | continue; |
671 | stdin_bytes += 2; | 656 | } |
672 | continue; | 657 | /* Note that escape character typed twice |
673 | } | 658 | falls through here; the latter gets processed |
674 | /* Note that escape character typed twice falls through | 659 | as a normal character below. */ |
675 | here; the latter gets processed as a normal | 660 | break; |
676 | character below. */ | 661 | } |
677 | break; | 662 | } else { |
678 | } | 663 | /* The previous character was not an escape char. Check if this |
679 | } | 664 | is an escape. */ |
680 | else | 665 | if (last_was_cr && ch == escape_char) { |
681 | { | 666 | /* It is. Set the flag and continue to next character. */ |
682 | /* The previous character was not an escape char. | 667 | escape_pending = 1; |
683 | Check if this is an escape. */ | 668 | continue; |
684 | if (last_was_cr && ch == escape_char) | 669 | } |
685 | { | 670 | } |
686 | /* It is. Set the flag and continue to next | 671 | |
687 | character. */ | 672 | /* Normal character. Record whether it was a newline, and append it to the |
688 | escape_pending = 1; | 673 | buffer. */ |
689 | continue; | 674 | last_was_cr = (ch == '\r' || ch == '\n'); |
690 | } | 675 | buf[0] = ch; |
691 | } | 676 | buffer_append(&stdin_buffer, buf, 1); |
692 | 677 | stdin_bytes += 1; | |
693 | /* Normal character. Record whether it was a newline, | 678 | continue; |
694 | and append it to the buffer. */ | 679 | } |
695 | last_was_cr = (ch == '\r' || ch == '\n'); | 680 | } |
696 | buf[0] = ch; | 681 | } |
697 | buffer_append(&stdin_buffer, buf, 1); | ||
698 | stdin_bytes += 1; | ||
699 | continue; | ||
700 | } | ||
701 | } | ||
702 | } | ||
703 | } | 682 | } |
704 | 683 | ||
705 | void client_process_output(fd_set *writeset) | 684 | void |
685 | client_process_output(fd_set * writeset) | ||
706 | { | 686 | { |
707 | int len; | 687 | int len; |
708 | char buf[100]; | 688 | char buf[100]; |
709 | 689 | ||
710 | /* Write buffered output to stdout. */ | 690 | /* Write buffered output to stdout. */ |
711 | if (FD_ISSET(fileno(stdout), writeset)) | 691 | if (FD_ISSET(fileno(stdout), writeset)) { |
712 | { | 692 | /* Write as much data as possible. */ |
713 | /* Write as much data as possible. */ | 693 | len = write(fileno(stdout), buffer_ptr(&stdout_buffer), |
714 | len = write(fileno(stdout), buffer_ptr(&stdout_buffer), | 694 | buffer_len(&stdout_buffer)); |
715 | buffer_len(&stdout_buffer)); | 695 | if (len <= 0) { |
716 | if (len <= 0) | 696 | if (errno == EAGAIN) |
717 | { | 697 | len = 0; |
718 | if (errno == EAGAIN) | 698 | else { |
719 | len = 0; | 699 | /* An error or EOF was encountered. Put |
720 | else | 700 | an error message to stderr buffer. */ |
721 | { | 701 | snprintf(buf, sizeof buf, "write stdout: %.50s\r\n", strerror(errno)); |
722 | /* An error or EOF was encountered. Put an error message | 702 | buffer_append(&stderr_buffer, buf, strlen(buf)); |
723 | to stderr buffer. */ | 703 | stderr_bytes += strlen(buf); |
724 | snprintf(buf, sizeof buf, "write stdout: %.50s\r\n", strerror(errno)); | 704 | quit_pending = 1; |
725 | buffer_append(&stderr_buffer, buf, strlen(buf)); | 705 | return; |
726 | stderr_bytes += strlen(buf); | 706 | } |
727 | quit_pending = 1; | 707 | } |
728 | return; | 708 | /* Consume printed data from the buffer. */ |
729 | } | 709 | buffer_consume(&stdout_buffer, len); |
710 | } | ||
711 | /* Write buffered output to stderr. */ | ||
712 | if (FD_ISSET(fileno(stderr), writeset)) { | ||
713 | /* Write as much data as possible. */ | ||
714 | len = write(fileno(stderr), buffer_ptr(&stderr_buffer), | ||
715 | buffer_len(&stderr_buffer)); | ||
716 | if (len <= 0) { | ||
717 | if (errno == EAGAIN) | ||
718 | len = 0; | ||
719 | else { | ||
720 | /* EOF or error, but can't even print | ||
721 | error message. */ | ||
722 | quit_pending = 1; | ||
723 | return; | ||
724 | } | ||
725 | } | ||
726 | /* Consume printed characters from the buffer. */ | ||
727 | buffer_consume(&stderr_buffer, len); | ||
730 | } | 728 | } |
731 | /* Consume printed data from the buffer. */ | ||
732 | buffer_consume(&stdout_buffer, len); | ||
733 | } | ||
734 | |||
735 | /* Write buffered output to stderr. */ | ||
736 | if (FD_ISSET(fileno(stderr), writeset)) | ||
737 | { | ||
738 | /* Write as much data as possible. */ | ||
739 | len = write(fileno(stderr), buffer_ptr(&stderr_buffer), | ||
740 | buffer_len(&stderr_buffer)); | ||
741 | if (len <= 0) { | ||
742 | if (errno == EAGAIN) | ||
743 | len = 0; | ||
744 | else | ||
745 | { | ||
746 | /* EOF or error, but can't even print error message. */ | ||
747 | quit_pending = 1; | ||
748 | return; | ||
749 | } | ||
750 | } | ||
751 | /* Consume printed characters from the buffer. */ | ||
752 | buffer_consume(&stderr_buffer, len); | ||
753 | } | ||
754 | } | 729 | } |
755 | 730 | ||
756 | /* Implements the interactive session with the server. This is called | 731 | /* Implements the interactive session with the server. This is called |
@@ -759,165 +734,160 @@ void client_process_output(fd_set *writeset) | |||
759 | used as an escape character for terminating or suspending the | 734 | used as an escape character for terminating or suspending the |
760 | session. */ | 735 | session. */ |
761 | 736 | ||
762 | int client_loop(int have_pty, int escape_char_arg) | 737 | int |
738 | client_loop(int have_pty, int escape_char_arg) | ||
763 | { | 739 | { |
764 | extern Options options; | 740 | extern Options options; |
765 | double start_time, total_time; | 741 | double start_time, total_time; |
766 | int len; | 742 | int len; |
767 | char buf[100]; | 743 | char buf[100]; |
768 | 744 | ||
769 | debug("Entering interactive session."); | 745 | debug("Entering interactive session."); |
770 | 746 | ||
771 | start_time = get_current_time(); | 747 | start_time = get_current_time(); |
772 | 748 | ||
773 | /* Initialize variables. */ | 749 | /* Initialize variables. */ |
774 | escape_pending = 0; | 750 | escape_pending = 0; |
775 | last_was_cr = 1; | 751 | last_was_cr = 1; |
776 | exit_status = -1; | 752 | exit_status = -1; |
777 | stdin_eof = 0; | 753 | stdin_eof = 0; |
778 | buffer_high = 64 * 1024; | 754 | buffer_high = 64 * 1024; |
779 | connection_in = packet_get_connection_in(); | 755 | connection_in = packet_get_connection_in(); |
780 | connection_out = packet_get_connection_out(); | 756 | connection_out = packet_get_connection_out(); |
781 | max_fd = connection_in; | 757 | max_fd = connection_in; |
782 | if (connection_out > max_fd) | 758 | if (connection_out > max_fd) |
783 | max_fd = connection_out; | 759 | max_fd = connection_out; |
784 | stdin_bytes = 0; | 760 | stdin_bytes = 0; |
785 | stdout_bytes = 0; | 761 | stdout_bytes = 0; |
786 | stderr_bytes = 0; | 762 | stderr_bytes = 0; |
787 | quit_pending = 0; | 763 | quit_pending = 0; |
788 | escape_char = escape_char_arg; | 764 | escape_char = escape_char_arg; |
789 | 765 | ||
790 | /* Initialize buffers. */ | 766 | /* Initialize buffers. */ |
791 | buffer_init(&stdin_buffer); | 767 | buffer_init(&stdin_buffer); |
792 | buffer_init(&stdout_buffer); | 768 | buffer_init(&stdout_buffer); |
793 | buffer_init(&stderr_buffer); | 769 | buffer_init(&stderr_buffer); |
794 | 770 | ||
795 | /* Set signal handlers to restore non-blocking mode. */ | 771 | /* Set signal handlers to restore non-blocking mode. */ |
796 | signal(SIGINT, signal_handler); | 772 | signal(SIGINT, signal_handler); |
797 | signal(SIGQUIT, signal_handler); | 773 | signal(SIGQUIT, signal_handler); |
798 | signal(SIGTERM, signal_handler); | 774 | signal(SIGTERM, signal_handler); |
799 | signal(SIGPIPE, SIG_IGN); | 775 | signal(SIGPIPE, SIG_IGN); |
800 | if (have_pty) | 776 | if (have_pty) |
801 | signal(SIGWINCH, window_change_handler); | 777 | signal(SIGWINCH, window_change_handler); |
802 | 778 | ||
803 | /* Enter raw mode if have a pseudo terminal. */ | 779 | /* Enter raw mode if have a pseudo terminal. */ |
804 | if (have_pty) | 780 | if (have_pty) |
805 | enter_raw_mode(); | 781 | enter_raw_mode(); |
806 | 782 | ||
807 | /* Check if we should immediately send of on stdin. */ | 783 | /* Check if we should immediately send of on stdin. */ |
808 | client_check_initial_eof_on_stdin(); | 784 | client_check_initial_eof_on_stdin(); |
809 | 785 | ||
810 | /* Main loop of the client for the interactive session mode. */ | 786 | /* Main loop of the client for the interactive session mode. */ |
811 | while (!quit_pending) | 787 | while (!quit_pending) { |
812 | { | 788 | fd_set readset, writeset; |
813 | fd_set readset, writeset; | 789 | |
814 | 790 | /* Precess buffered packets sent by the server. */ | |
815 | /* Precess buffered packets sent by the server. */ | 791 | client_process_buffered_input_packets(); |
816 | client_process_buffered_input_packets(); | 792 | |
817 | 793 | /* Make packets of buffered stdin data, and buffer them | |
818 | /* Make packets of buffered stdin data, and buffer them for sending | 794 | for sending to the server. */ |
819 | to the server. */ | 795 | client_make_packets_from_stdin_data(); |
820 | client_make_packets_from_stdin_data(); | 796 | |
821 | 797 | /* Make packets from buffered channel data, and buffer | |
822 | /* Make packets from buffered channel data, and buffer them for sending | 798 | them for sending to the server. */ |
823 | to the server. */ | 799 | if (packet_not_very_much_data_to_write()) |
824 | if (packet_not_very_much_data_to_write()) | 800 | channel_output_poll(); |
825 | channel_output_poll(); | 801 | |
826 | 802 | /* Check if the window size has changed, and buffer a | |
827 | /* Check if the window size has changed, and buffer a message about | 803 | message about it to the server if so. */ |
828 | it to the server if so. */ | 804 | client_check_window_change(); |
829 | client_check_window_change(); | 805 | |
830 | 806 | if (quit_pending) | |
831 | if (quit_pending) | 807 | break; |
832 | break; | 808 | |
833 | 809 | /* Wait until we have something to do (something becomes | |
834 | /* Wait until we have something to do (something becomes available | 810 | available on one of the descriptors). */ |
835 | on one of the descriptors). */ | 811 | client_wait_until_can_do_something(&readset, &writeset); |
836 | client_wait_until_can_do_something(&readset, &writeset); | 812 | |
837 | 813 | if (quit_pending) | |
838 | if (quit_pending) | 814 | break; |
839 | break; | 815 | |
840 | 816 | /* Do channel operations. */ | |
841 | /* Do channel operations. */ | 817 | channel_after_select(&readset, &writeset); |
842 | channel_after_select(&readset, &writeset); | 818 | |
843 | 819 | /* Process input from the connection and from stdin. | |
844 | /* Process input from the connection and from stdin. Buffer any data | 820 | Buffer any data that is available. */ |
845 | that is available. */ | 821 | client_process_input(&readset); |
846 | client_process_input(&readset); | 822 | |
847 | 823 | /* Process output to stdout and stderr. Output to the | |
848 | /* Process output to stdout and stderr. Output to the connection | 824 | connection is processed elsewhere (above). */ |
849 | is processed elsewhere (above). */ | 825 | client_process_output(&writeset); |
850 | client_process_output(&writeset); | 826 | |
851 | 827 | /* Send as much buffered packet data as possible to the | |
852 | /* Send as much buffered packet data as possible to the sender. */ | 828 | sender. */ |
853 | if (FD_ISSET(connection_out, &writeset)) | 829 | if (FD_ISSET(connection_out, &writeset)) |
854 | packet_write_poll(); | 830 | packet_write_poll(); |
855 | } | 831 | } |
856 | 832 | ||
857 | /* Terminate the session. */ | 833 | /* Terminate the session. */ |
858 | 834 | ||
859 | /* Stop watching for window change. */ | 835 | /* Stop watching for window change. */ |
860 | if (have_pty) | 836 | if (have_pty) |
861 | signal(SIGWINCH, SIG_DFL); | 837 | signal(SIGWINCH, SIG_DFL); |
862 | 838 | ||
863 | /* Stop listening for connections. */ | 839 | /* Stop listening for connections. */ |
864 | channel_stop_listening(); | 840 | channel_stop_listening(); |
865 | 841 | ||
866 | /* In interactive mode (with pseudo tty) display a message indicating that | 842 | /* In interactive mode (with pseudo tty) display a message |
867 | the connection has been closed. */ | 843 | indicating that the connection has been closed. */ |
868 | if (have_pty && options.log_level != SYSLOG_LEVEL_QUIET) | 844 | if (have_pty && options.log_level != SYSLOG_LEVEL_QUIET) { |
869 | { | 845 | snprintf(buf, sizeof buf, "Connection to %.64s closed.\r\n", host); |
870 | snprintf(buf, sizeof buf, "Connection to %.64s closed.\r\n", host); | 846 | buffer_append(&stderr_buffer, buf, strlen(buf)); |
871 | buffer_append(&stderr_buffer, buf, strlen(buf)); | 847 | stderr_bytes += strlen(buf); |
872 | stderr_bytes += strlen(buf); | ||
873 | } | ||
874 | |||
875 | /* Output any buffered data for stdout. */ | ||
876 | while (buffer_len(&stdout_buffer) > 0) | ||
877 | { | ||
878 | len = write(fileno(stdout), buffer_ptr(&stdout_buffer), | ||
879 | buffer_len(&stdout_buffer)); | ||
880 | if (len <= 0) | ||
881 | { | ||
882 | error("Write failed flushing stdout buffer."); | ||
883 | break; | ||
884 | } | 848 | } |
885 | buffer_consume(&stdout_buffer, len); | 849 | /* Output any buffered data for stdout. */ |
886 | } | 850 | while (buffer_len(&stdout_buffer) > 0) { |
887 | 851 | len = write(fileno(stdout), buffer_ptr(&stdout_buffer), | |
888 | /* Output any buffered data for stderr. */ | 852 | buffer_len(&stdout_buffer)); |
889 | while (buffer_len(&stderr_buffer) > 0) | 853 | if (len <= 0) { |
890 | { | 854 | error("Write failed flushing stdout buffer."); |
891 | len = write(fileno(stderr), buffer_ptr(&stderr_buffer), | 855 | break; |
892 | buffer_len(&stderr_buffer)); | 856 | } |
893 | if (len <= 0) | 857 | buffer_consume(&stdout_buffer, len); |
894 | { | ||
895 | error("Write failed flushing stderr buffer."); | ||
896 | break; | ||
897 | } | 858 | } |
898 | buffer_consume(&stderr_buffer, len); | 859 | |
899 | } | 860 | /* Output any buffered data for stderr. */ |
900 | 861 | while (buffer_len(&stderr_buffer) > 0) { | |
901 | /* Leave raw mode. */ | 862 | len = write(fileno(stderr), buffer_ptr(&stderr_buffer), |
902 | if (have_pty) | 863 | buffer_len(&stderr_buffer)); |
903 | leave_raw_mode(); | 864 | if (len <= 0) { |
904 | 865 | error("Write failed flushing stderr buffer."); | |
905 | /* Clear and free any buffers. */ | 866 | break; |
906 | memset(buf, 0, sizeof(buf)); | 867 | } |
907 | buffer_free(&stdin_buffer); | 868 | buffer_consume(&stderr_buffer, len); |
908 | buffer_free(&stdout_buffer); | 869 | } |
909 | buffer_free(&stderr_buffer); | 870 | |
910 | 871 | /* Leave raw mode. */ | |
911 | /* Report bytes transferred, and transfer rates. */ | 872 | if (have_pty) |
912 | total_time = get_current_time() - start_time; | 873 | leave_raw_mode(); |
913 | debug("Transferred: stdin %lu, stdout %lu, stderr %lu bytes in %.1f seconds", | 874 | |
914 | stdin_bytes, stdout_bytes, stderr_bytes, total_time); | 875 | /* Clear and free any buffers. */ |
915 | if (total_time > 0) | 876 | memset(buf, 0, sizeof(buf)); |
916 | debug("Bytes per second: stdin %.1f, stdout %.1f, stderr %.1f", | 877 | buffer_free(&stdin_buffer); |
917 | stdin_bytes / total_time, stdout_bytes / total_time, | 878 | buffer_free(&stdout_buffer); |
918 | stderr_bytes / total_time); | 879 | buffer_free(&stderr_buffer); |
919 | 880 | ||
920 | /* Return the exit status of the program. */ | 881 | /* Report bytes transferred, and transfer rates. */ |
921 | debug("Exit status %d", exit_status); | 882 | total_time = get_current_time() - start_time; |
922 | return exit_status; | 883 | debug("Transferred: stdin %lu, stdout %lu, stderr %lu bytes in %.1f seconds", |
884 | stdin_bytes, stdout_bytes, stderr_bytes, total_time); | ||
885 | if (total_time > 0) | ||
886 | debug("Bytes per second: stdin %.1f, stdout %.1f, stderr %.1f", | ||
887 | stdin_bytes / total_time, stdout_bytes / total_time, | ||
888 | stderr_bytes / total_time); | ||
889 | |||
890 | /* Return the exit status of the program. */ | ||
891 | debug("Exit status %d", exit_status); | ||
892 | return exit_status; | ||
923 | } | 893 | } |
@@ -1,10 +1,13 @@ | |||
1 | #include "includes.h" | 1 | #include "includes.h" |
2 | RCSID("$Id: compat.c,v 1.1 1999/10/27 03:42:44 damien Exp $"); | 2 | RCSID("$Id: compat.c,v 1.2 1999/11/24 13:26:22 damien Exp $"); |
3 | 3 | ||
4 | #include "ssh.h" | 4 | #include "ssh.h" |
5 | 5 | ||
6 | int compat13=0; | 6 | int compat13 = 0; |
7 | void enable_compat13(void){ | 7 | |
8 | log("Enabling compatibility mode for protocol 1.3"); | 8 | void |
9 | compat13=1; | 9 | enable_compat13(void) |
10 | { | ||
11 | verbose("Enabling compatibility mode for protocol 1.3"); | ||
12 | compat13 = 1; | ||
10 | } | 13 | } |
@@ -1,7 +1,7 @@ | |||
1 | /* RCSID("$Id: compat.h,v 1.1 1999/10/27 03:42:44 damien Exp $"); */ | 1 | /* RCSID("$Id: compat.h,v 1.2 1999/11/24 13:26:22 damien Exp $"); */ |
2 | 2 | ||
3 | #ifndef COMPAT_H | 3 | #ifndef COMPAT_H |
4 | #define COMPAT_H | 4 | #define COMPAT_H |
5 | void enable_compat13(void); | 5 | void enable_compat13(void); |
6 | extern int compat13; | 6 | extern int compat13; |
7 | #endif | 7 | #endif |
diff --git a/compress.c b/compress.c index c3267f73f..463e7374e 100644 --- a/compress.c +++ b/compress.c | |||
@@ -1,20 +1,20 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * | |
3 | compress.c | 3 | * compress.c |
4 | 4 | * | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
6 | 6 | * | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
8 | All rights reserved | 8 | * All rights reserved |
9 | 9 | * | |
10 | Created: Wed Oct 25 22:12:46 1995 ylo | 10 | * Created: Wed Oct 25 22:12:46 1995 ylo |
11 | 11 | * | |
12 | Interface to packet compression for ssh. | 12 | * Interface to packet compression for ssh. |
13 | 13 | * | |
14 | */ | 14 | */ |
15 | 15 | ||
16 | #include "includes.h" | 16 | #include "includes.h" |
17 | RCSID("$Id: compress.c,v 1.1 1999/10/27 03:42:44 damien Exp $"); | 17 | RCSID("$Id: compress.c,v 1.2 1999/11/24 13:26:22 damien Exp $"); |
18 | 18 | ||
19 | #include "ssh.h" | 19 | #include "ssh.h" |
20 | #include "buffer.h" | 20 | #include "buffer.h" |
@@ -23,32 +23,34 @@ RCSID("$Id: compress.c,v 1.1 1999/10/27 03:42:44 damien Exp $"); | |||
23 | static z_stream incoming_stream; | 23 | static z_stream incoming_stream; |
24 | static z_stream outgoing_stream; | 24 | static z_stream outgoing_stream; |
25 | 25 | ||
26 | /* Initializes compression; level is compression level from 1 to 9 (as in | 26 | /* Initializes compression; level is compression level from 1 to 9 |
27 | gzip). */ | 27 | (as in gzip). */ |
28 | 28 | ||
29 | void buffer_compress_init(int level) | 29 | void |
30 | buffer_compress_init(int level) | ||
30 | { | 31 | { |
31 | debug("Enabling compression at level %d.", level); | 32 | debug("Enabling compression at level %d.", level); |
32 | if (level < 1 || level > 9) | 33 | if (level < 1 || level > 9) |
33 | fatal("Bad compression level %d.", level); | 34 | fatal("Bad compression level %d.", level); |
34 | inflateInit(&incoming_stream); | 35 | inflateInit(&incoming_stream); |
35 | deflateInit(&outgoing_stream, level); | 36 | deflateInit(&outgoing_stream, level); |
36 | } | 37 | } |
37 | 38 | ||
38 | /* Frees any data structures allocated for compression. */ | 39 | /* Frees any data structures allocated for compression. */ |
39 | 40 | ||
40 | void buffer_compress_uninit() | 41 | void |
42 | buffer_compress_uninit() | ||
41 | { | 43 | { |
42 | debug("compress outgoing: raw data %lu, compressed %lu, factor %.2f", | 44 | debug("compress outgoing: raw data %lu, compressed %lu, factor %.2f", |
43 | outgoing_stream.total_in, outgoing_stream.total_out, | 45 | outgoing_stream.total_in, outgoing_stream.total_out, |
44 | outgoing_stream.total_in == 0 ? 0.0 : | 46 | outgoing_stream.total_in == 0 ? 0.0 : |
45 | (double)outgoing_stream.total_out / outgoing_stream.total_in); | 47 | (double) outgoing_stream.total_out / outgoing_stream.total_in); |
46 | debug("compress incoming: raw data %lu, compressed %lu, factor %.2f", | 48 | debug("compress incoming: raw data %lu, compressed %lu, factor %.2f", |
47 | incoming_stream.total_out, incoming_stream.total_in, | 49 | incoming_stream.total_out, incoming_stream.total_in, |
48 | incoming_stream.total_out == 0 ? 0.0 : | 50 | incoming_stream.total_out == 0 ? 0.0 : |
49 | (double)incoming_stream.total_in / incoming_stream.total_out); | 51 | (double) incoming_stream.total_in / incoming_stream.total_out); |
50 | inflateEnd(&incoming_stream); | 52 | inflateEnd(&incoming_stream); |
51 | deflateEnd(&outgoing_stream); | 53 | deflateEnd(&outgoing_stream); |
52 | } | 54 | } |
53 | 55 | ||
54 | /* Compresses the contents of input_buffer into output_buffer. All | 56 | /* Compresses the contents of input_buffer into output_buffer. All |
@@ -59,50 +61,49 @@ void buffer_compress_uninit() | |||
59 | form a single compression stream) by the receiver. This appends | 61 | form a single compression stream) by the receiver. This appends |
60 | the compressed data to the output buffer. */ | 62 | the compressed data to the output buffer. */ |
61 | 63 | ||
62 | void buffer_compress(Buffer *input_buffer, Buffer *output_buffer) | 64 | void |
65 | buffer_compress(Buffer * input_buffer, Buffer * output_buffer) | ||
63 | { | 66 | { |
64 | char buf[4096]; | 67 | char buf[4096]; |
65 | int status; | 68 | int status; |
66 | 69 | ||
67 | /* This case is not handled below. */ | 70 | /* This case is not handled below. */ |
68 | if (buffer_len(input_buffer) == 0) | 71 | if (buffer_len(input_buffer) == 0) |
69 | return; | 72 | return; |
70 | 73 | ||
71 | /* Input is the contents of the input buffer. */ | 74 | /* Input is the contents of the input buffer. */ |
72 | outgoing_stream.next_in = buffer_ptr(input_buffer); | 75 | outgoing_stream.next_in = buffer_ptr(input_buffer); |
73 | outgoing_stream.avail_in = buffer_len(input_buffer); | 76 | outgoing_stream.avail_in = buffer_len(input_buffer); |
74 | 77 | ||
75 | /* Loop compressing until deflate() returns with avail_out != 0. */ | 78 | /* Loop compressing until deflate() returns with avail_out != 0. */ |
76 | do | 79 | do { |
77 | { | 80 | /* Set up fixed-size output buffer. */ |
78 | /* Set up fixed-size output buffer. */ | 81 | outgoing_stream.next_out = buf; |
79 | outgoing_stream.next_out = buf; | 82 | outgoing_stream.avail_out = sizeof(buf); |
80 | outgoing_stream.avail_out = sizeof(buf); | 83 | |
81 | 84 | /* Compress as much data into the buffer as possible. */ | |
82 | /* Compress as much data into the buffer as possible. */ | 85 | status = deflate(&outgoing_stream, Z_PARTIAL_FLUSH); |
83 | status = deflate(&outgoing_stream, Z_PARTIAL_FLUSH); | 86 | switch (status) { |
84 | switch (status) | 87 | case Z_OK: |
85 | { | 88 | /* Append compressed data to output_buffer. */ |
86 | case Z_OK: | 89 | buffer_append(output_buffer, buf, |
87 | /* Append compressed data to output_buffer. */ | 90 | sizeof(buf) - outgoing_stream.avail_out); |
88 | buffer_append(output_buffer, buf, | 91 | break; |
89 | sizeof(buf) - outgoing_stream.avail_out); | 92 | case Z_STREAM_END: |
90 | break; | 93 | fatal("buffer_compress: deflate returned Z_STREAM_END"); |
91 | case Z_STREAM_END: | 94 | /* NOTREACHED */ |
92 | fatal("buffer_compress: deflate returned Z_STREAM_END"); | 95 | case Z_STREAM_ERROR: |
93 | /*NOTREACHED*/ | 96 | fatal("buffer_compress: deflate returned Z_STREAM_ERROR"); |
94 | case Z_STREAM_ERROR: | 97 | /* NOTREACHED */ |
95 | fatal("buffer_compress: deflate returned Z_STREAM_ERROR"); | 98 | case Z_BUF_ERROR: |
96 | /*NOTREACHED*/ | 99 | fatal("buffer_compress: deflate returned Z_BUF_ERROR"); |
97 | case Z_BUF_ERROR: | 100 | /* NOTREACHED */ |
98 | fatal("buffer_compress: deflate returned Z_BUF_ERROR"); | 101 | default: |
99 | /*NOTREACHED*/ | 102 | fatal("buffer_compress: deflate returned %d", status); |
100 | default: | 103 | /* NOTREACHED */ |
101 | fatal("buffer_compress: deflate returned %d", status); | 104 | } |
102 | /*NOTREACHED*/ | ||
103 | } | 105 | } |
104 | } | 106 | while (outgoing_stream.avail_out == 0); |
105 | while (outgoing_stream.avail_out == 0); | ||
106 | } | 107 | } |
107 | 108 | ||
108 | /* Uncompresses the contents of input_buffer into output_buffer. All | 109 | /* Uncompresses the contents of input_buffer into output_buffer. All |
@@ -113,48 +114,46 @@ void buffer_compress(Buffer *input_buffer, Buffer *output_buffer) | |||
113 | same order that buffers compressed with that. This appends the | 114 | same order that buffers compressed with that. This appends the |
114 | uncompressed data to the output buffer. */ | 115 | uncompressed data to the output buffer. */ |
115 | 116 | ||
116 | void buffer_uncompress(Buffer *input_buffer, Buffer *output_buffer) | 117 | void |
118 | buffer_uncompress(Buffer * input_buffer, Buffer * output_buffer) | ||
117 | { | 119 | { |
118 | char buf[4096]; | 120 | char buf[4096]; |
119 | int status; | 121 | int status; |
120 | 122 | ||
121 | incoming_stream.next_in = buffer_ptr(input_buffer); | 123 | incoming_stream.next_in = buffer_ptr(input_buffer); |
122 | incoming_stream.avail_in = buffer_len(input_buffer); | 124 | incoming_stream.avail_in = buffer_len(input_buffer); |
123 | 125 | ||
124 | incoming_stream.next_out = buf; | 126 | incoming_stream.next_out = buf; |
125 | incoming_stream.avail_out = sizeof(buf); | 127 | incoming_stream.avail_out = sizeof(buf); |
126 | 128 | ||
127 | for (;;) | 129 | for (;;) { |
128 | { | 130 | status = inflate(&incoming_stream, Z_PARTIAL_FLUSH); |
129 | status = inflate(&incoming_stream, Z_PARTIAL_FLUSH); | 131 | switch (status) { |
130 | switch (status) | 132 | case Z_OK: |
131 | { | 133 | buffer_append(output_buffer, buf, |
132 | case Z_OK: | 134 | sizeof(buf) - incoming_stream.avail_out); |
133 | buffer_append(output_buffer, buf, | 135 | incoming_stream.next_out = buf; |
134 | sizeof(buf) - incoming_stream.avail_out); | 136 | incoming_stream.avail_out = sizeof(buf); |
135 | incoming_stream.next_out = buf; | 137 | break; |
136 | incoming_stream.avail_out = sizeof(buf); | 138 | case Z_STREAM_END: |
137 | break; | 139 | fatal("buffer_uncompress: inflate returned Z_STREAM_END"); |
138 | case Z_STREAM_END: | 140 | /* NOTREACHED */ |
139 | fatal("buffer_uncompress: inflate returned Z_STREAM_END"); | 141 | case Z_DATA_ERROR: |
140 | /*NOTREACHED*/ | 142 | fatal("buffer_uncompress: inflate returned Z_DATA_ERROR"); |
141 | case Z_DATA_ERROR: | 143 | /* NOTREACHED */ |
142 | fatal("buffer_uncompress: inflate returned Z_DATA_ERROR"); | 144 | case Z_STREAM_ERROR: |
143 | /*NOTREACHED*/ | 145 | fatal("buffer_uncompress: inflate returned Z_STREAM_ERROR"); |
144 | case Z_STREAM_ERROR: | 146 | /* NOTREACHED */ |
145 | fatal("buffer_uncompress: inflate returned Z_STREAM_ERROR"); | 147 | case Z_BUF_ERROR: |
146 | /*NOTREACHED*/ | 148 | /* Comments in zlib.h say that we should keep |
147 | case Z_BUF_ERROR: | 149 | calling inflate() until we get an error. This |
148 | /* Comments in zlib.h say that we should keep calling inflate() | 150 | appears to be the error that we get. */ |
149 | until we get an error. This appears to be the error that we | 151 | return; |
150 | get. */ | 152 | case Z_MEM_ERROR: |
151 | return; | 153 | fatal("buffer_uncompress: inflate returned Z_MEM_ERROR"); |
152 | case Z_MEM_ERROR: | 154 | /* NOTREACHED */ |
153 | fatal("buffer_uncompress: inflate returned Z_MEM_ERROR"); | 155 | default: |
154 | /*NOTREACHED*/ | 156 | fatal("buffer_uncompress: inflate returned %d", status); |
155 | default: | 157 | } |
156 | fatal("buffer_uncompress: inflate returned %d", status); | ||
157 | } | 158 | } |
158 | } | ||
159 | } | 159 | } |
160 | |||
diff --git a/compress.h b/compress.h index b3144d621..e028f71a5 100644 --- a/compress.h +++ b/compress.h | |||
@@ -1,29 +1,29 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * | |
3 | compress.h | 3 | * compress.h |
4 | 4 | * | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
6 | 6 | * | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
8 | All rights reserved | 8 | * All rights reserved |
9 | 9 | * | |
10 | Created: Wed Oct 25 22:12:46 1995 ylo | 10 | * Created: Wed Oct 25 22:12:46 1995 ylo |
11 | 11 | * | |
12 | Interface to packet compression for ssh. | 12 | * Interface to packet compression for ssh. |
13 | 13 | * | |
14 | */ | 14 | */ |
15 | 15 | ||
16 | /* RCSID("$Id: compress.h,v 1.1 1999/10/27 03:42:44 damien Exp $"); */ | 16 | /* RCSID("$Id: compress.h,v 1.2 1999/11/24 13:26:22 damien Exp $"); */ |
17 | 17 | ||
18 | #ifndef COMPRESS_H | 18 | #ifndef COMPRESS_H |
19 | #define COMPRESS_H | 19 | #define COMPRESS_H |
20 | 20 | ||
21 | /* Initializes compression; level is compression level from 1 to 9 (as in | 21 | /* Initializes compression; level is compression level from 1 to 9 (as in |
22 | gzip). */ | 22 | gzip). */ |
23 | void buffer_compress_init(int level); | 23 | void buffer_compress_init(int level); |
24 | 24 | ||
25 | /* Frees any data structures allocated by buffer_compress_init. */ | 25 | /* Frees any data structures allocated by buffer_compress_init. */ |
26 | void buffer_compress_uninit(); | 26 | void buffer_compress_uninit(); |
27 | 27 | ||
28 | /* Compresses the contents of input_buffer into output_buffer. All | 28 | /* Compresses the contents of input_buffer into output_buffer. All |
29 | packets compressed using this function will form a single | 29 | packets compressed using this function will form a single |
@@ -32,7 +32,7 @@ void buffer_compress_uninit(); | |||
32 | independently (but in the appropriate order since they together | 32 | independently (but in the appropriate order since they together |
33 | form a single compression stream) by the receiver. This appends | 33 | form a single compression stream) by the receiver. This appends |
34 | the compressed data to the output buffer. */ | 34 | the compressed data to the output buffer. */ |
35 | void buffer_compress(Buffer *input_buffer, Buffer *output_buffer); | 35 | void buffer_compress(Buffer * input_buffer, Buffer * output_buffer); |
36 | 36 | ||
37 | /* Uncompresses the contents of input_buffer into output_buffer. All | 37 | /* Uncompresses the contents of input_buffer into output_buffer. All |
38 | packets uncompressed using this function will form a single | 38 | packets uncompressed using this function will form a single |
@@ -41,6 +41,6 @@ void buffer_compress(Buffer *input_buffer, Buffer *output_buffer); | |||
41 | same size units that the buffer_compress was called, and in the | 41 | same size units that the buffer_compress was called, and in the |
42 | same order that buffers compressed with that. This appends the | 42 | same order that buffers compressed with that. This appends the |
43 | uncompressed data to the output buffer. */ | 43 | uncompressed data to the output buffer. */ |
44 | void buffer_uncompress(Buffer *input_buffer, Buffer *output_buffer); | 44 | void buffer_uncompress(Buffer * input_buffer, Buffer * output_buffer); |
45 | 45 | ||
46 | #endif /* COMPRESS_H */ | 46 | #endif /* COMPRESS_H */ |
diff --git a/configure.in b/configure.in index 73635272a..9f545d733 100644 --- a/configure.in +++ b/configure.in | |||
@@ -55,7 +55,7 @@ AC_CHECK_LIB(dl, dlopen, , ) | |||
55 | AC_CHECK_LIB(pam, pam_authenticate, , ) | 55 | AC_CHECK_LIB(pam, pam_authenticate, , ) |
56 | 56 | ||
57 | dnl Checks for header files. | 57 | dnl Checks for header files. |
58 | AC_CHECK_HEADERS(pty.h endian.h paths.h lastlog.h shadow.h netgroup.h maillock.h utmp.h sys/select.h sys/time.h) | 58 | AC_CHECK_HEADERS(pty.h endian.h paths.h lastlog.h shadow.h netgroup.h maillock.h utmp.h util.h sys/select.h sys/time.h) |
59 | 59 | ||
60 | dnl Checks for library functions. | 60 | dnl Checks for library functions. |
61 | AC_CHECK_FUNCS(openpty strlcpy strlcat mkdtemp arc4random setproctitle setlogin setenv) | 61 | AC_CHECK_FUNCS(openpty strlcpy strlcat mkdtemp arc4random setproctitle setlogin setenv) |
@@ -1,9 +1,12 @@ | |||
1 | /* The implementation here was originally done by Gary S. Brown. I have | 1 | /* |
2 | borrowed the tables directly, and made some minor changes to the | 2 | * The implementation here was originally done by Gary S. Brown. |
3 | crc32-function (including changing the interface). //ylo */ | 3 | * I have borrowed the tables directly, and made some minor changes |
4 | * to the crc32-function (including changing the interface). | ||
5 | * //ylo | ||
6 | */ | ||
4 | 7 | ||
5 | #include "includes.h" | 8 | #include "includes.h" |
6 | RCSID("$Id: crc32.c,v 1.1 1999/10/27 03:42:44 damien Exp $"); | 9 | RCSID("$Id: crc32.c,v 1.2 1999/11/24 13:26:22 damien Exp $"); |
7 | 10 | ||
8 | #include "crc32.h" | 11 | #include "crc32.h" |
9 | 12 | ||
@@ -48,73 +51,71 @@ RCSID("$Id: crc32.c,v 1.1 1999/10/27 03:42:44 damien Exp $"); | |||
48 | /* -------------------------------------------------------------------- */ | 51 | /* -------------------------------------------------------------------- */ |
49 | 52 | ||
50 | static unsigned int crc32_tab[] = { | 53 | static unsigned int crc32_tab[] = { |
51 | 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, | 54 | 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, |
52 | 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, | 55 | 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, |
53 | 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, | 56 | 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, |
54 | 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, | 57 | 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, |
55 | 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, | 58 | 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, |
56 | 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, | 59 | 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, |
57 | 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, | 60 | 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, |
58 | 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, | 61 | 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, |
59 | 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, | 62 | 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, |
60 | 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, | 63 | 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, |
61 | 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, | 64 | 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, |
62 | 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, | 65 | 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, |
63 | 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, | 66 | 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, |
64 | 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, | 67 | 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, |
65 | 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, | 68 | 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, |
66 | 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, | 69 | 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, |
67 | 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, | 70 | 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, |
68 | 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, | 71 | 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, |
69 | 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, | 72 | 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, |
70 | 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, | 73 | 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, |
71 | 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, | 74 | 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, |
72 | 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, | 75 | 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, |
73 | 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, | 76 | 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, |
74 | 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, | 77 | 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, |
75 | 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, | 78 | 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, |
76 | 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, | 79 | 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, |
77 | 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, | 80 | 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, |
78 | 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, | 81 | 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, |
79 | 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, | 82 | 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, |
80 | 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, | 83 | 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, |
81 | 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, | 84 | 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, |
82 | 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, | 85 | 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, |
83 | 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, | 86 | 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, |
84 | 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, | 87 | 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, |
85 | 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, | 88 | 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, |
86 | 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, | 89 | 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, |
87 | 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, | 90 | 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, |
88 | 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, | 91 | 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, |
89 | 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, | 92 | 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, |
90 | 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, | 93 | 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, |
91 | 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, | 94 | 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, |
92 | 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, | 95 | 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, |
93 | 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, | 96 | 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, |
94 | 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, | 97 | 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, |
95 | 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, | 98 | 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, |
96 | 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, | 99 | 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, |
97 | 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, | 100 | 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, |
98 | 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, | 101 | 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, |
99 | 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, | 102 | 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, |
100 | 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, | 103 | 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, |
101 | 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, | 104 | 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, |
102 | 0x2d02ef8dL | 105 | 0x2d02ef8dL |
103 | }; | 106 | }; |
104 | 107 | ||
105 | /* Return a 32-bit CRC of the contents of the buffer. */ | 108 | /* Return a 32-bit CRC of the contents of the buffer. */ |
106 | 109 | ||
107 | unsigned int crc32(const unsigned char *s, unsigned int len) | 110 | unsigned int |
111 | crc32(const unsigned char *s, unsigned int len) | ||
108 | { | 112 | { |
109 | unsigned int i; | 113 | unsigned int i; |
110 | unsigned int crc32val; | 114 | unsigned int crc32val; |
111 | 115 | ||
112 | crc32val = 0; | 116 | crc32val = 0; |
113 | for (i = 0; i < len; i ++) | 117 | for (i = 0; i < len; i ++) { |
114 | { | 118 | crc32val = crc32_tab[(crc32val ^ s[i]) & 0xff] ^ (crc32val >> 8); |
115 | crc32val = | 119 | } |
116 | crc32_tab[(crc32val ^ s[i]) & 0xff] ^ | 120 | return crc32val; |
117 | (crc32val >> 8); | ||
118 | } | ||
119 | return crc32val; | ||
120 | } | 121 | } |
@@ -1,19 +1,19 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * | |
3 | crc32.h | 3 | * crc32.h |
4 | 4 | * | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
6 | 6 | * | |
7 | Copyright (c) 1992 Tatu Ylonen, Espoo, Finland | 7 | * Copyright (c) 1992 Tatu Ylonen, Espoo, Finland |
8 | All rights reserved | 8 | * All rights reserved |
9 | 9 | * | |
10 | Created: Tue Feb 11 14:37:27 1992 ylo | 10 | * Created: Tue Feb 11 14:37:27 1992 ylo |
11 | 11 | * | |
12 | Functions for computing 32-bit CRC. | 12 | * Functions for computing 32-bit CRC. |
13 | 13 | * | |
14 | */ | 14 | */ |
15 | 15 | ||
16 | /* RCSID("$Id: crc32.h,v 1.1 1999/10/27 03:42:44 damien Exp $"); */ | 16 | /* RCSID("$Id: crc32.h,v 1.2 1999/11/24 13:26:22 damien Exp $"); */ |
17 | 17 | ||
18 | #ifndef CRC32_H | 18 | #ifndef CRC32_H |
19 | #define CRC32_H | 19 | #define CRC32_H |
@@ -22,4 +22,4 @@ Functions for computing 32-bit CRC. | |||
22 | CRC. The polynomial used is 0xedb88320. */ | 22 | CRC. The polynomial used is 0xedb88320. */ |
23 | unsigned int crc32(const unsigned char *buf, unsigned int len); | 23 | unsigned int crc32(const unsigned char *buf, unsigned int len); |
24 | 24 | ||
25 | #endif /* CRC32_H */ | 25 | #endif /* CRC32_H */ |
diff --git a/deattack.c b/deattack.c index afd96e4e4..81b1c8efb 100644 --- a/deattack.c +++ b/deattack.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * $Id: deattack.c,v 1.2 1999/11/08 05:15:55 damien Exp $ | 2 | * $Id: deattack.c,v 1.3 1999/11/24 13:26:22 damien Exp $ |
3 | * Cryptographic attack detector for ssh - source code | 3 | * Cryptographic attack detector for ssh - source code |
4 | * | 4 | * |
5 | * Copyright (c) 1998 CORE SDI S.A., Buenos Aires, Argentina. | 5 | * Copyright (c) 1998 CORE SDI S.A., Buenos Aires, Argentina. |
@@ -15,7 +15,8 @@ | |||
15 | * SOFTWARE. | 15 | * SOFTWARE. |
16 | * | 16 | * |
17 | * Ariel Futoransky <futo@core-sdi.com> | 17 | * Ariel Futoransky <futo@core-sdi.com> |
18 | * <http://www.core-sdi.com> */ | 18 | * <http://www.core-sdi.com> |
19 | */ | ||
19 | 20 | ||
20 | #include "includes.h" | 21 | #include "includes.h" |
21 | #include "deattack.h" | 22 | #include "deattack.h" |
@@ -25,157 +26,130 @@ | |||
25 | #include "xmalloc.h" | 26 | #include "xmalloc.h" |
26 | 27 | ||
27 | /* SSH Constants */ | 28 | /* SSH Constants */ |
28 | #define SSH_MAXBLOCKS (32 * 1024) | 29 | #define SSH_MAXBLOCKS (32 * 1024) |
29 | #define SSH_BLOCKSIZE (8) | 30 | #define SSH_BLOCKSIZE (8) |
30 | 31 | ||
31 | /* Hashing constants */ | 32 | /* Hashing constants */ |
32 | #define HASH_MINSIZE (8 * 1024) | 33 | #define HASH_MINSIZE (8 * 1024) |
33 | #define HASH_ENTRYSIZE (2) | 34 | #define HASH_ENTRYSIZE (2) |
34 | #define HASH_FACTOR(x) ((x)*3/2) | 35 | #define HASH_FACTOR(x) ((x)*3/2) |
35 | #define HASH_UNUSEDCHAR (0xff) | 36 | #define HASH_UNUSEDCHAR (0xff) |
36 | #define HASH_UNUSED (0xffff) | 37 | #define HASH_UNUSED (0xffff) |
37 | #define HASH_IV (0xfffe) | 38 | #define HASH_IV (0xfffe) |
38 | 39 | ||
39 | #define HASH_MINBLOCKS (7*SSH_BLOCKSIZE) | 40 | #define HASH_MINBLOCKS (7*SSH_BLOCKSIZE) |
40 | 41 | ||
41 | 42 | ||
42 | /* Hash function (Input keys are cipher results) */ | 43 | /* Hash function (Input keys are cipher results) */ |
43 | #define HASH(x) GET_32BIT(x) | 44 | #define HASH(x) GET_32BIT(x) |
44 | 45 | ||
45 | #define CMP(a,b) (memcmp(a, b, SSH_BLOCKSIZE)) | 46 | #define CMP(a,b) (memcmp(a, b, SSH_BLOCKSIZE)) |
46 | 47 | ||
47 | 48 | ||
48 | void | 49 | void |
49 | crc_update(u_int32_t * a, u_int32_t b) | 50 | crc_update(u_int32_t *a, u_int32_t b) |
50 | { | 51 | { |
51 | b ^= *a; | 52 | b ^= *a; |
52 | *a = crc32((unsigned char *) &b, sizeof(b)); | 53 | *a = crc32((unsigned char *) &b, sizeof(b)); |
53 | } | 54 | } |
54 | 55 | ||
55 | /* | 56 | /* detect if a block is used in a particular pattern */ |
56 | check_crc | ||
57 | detects if a block is used in a particular pattern | ||
58 | */ | ||
59 | |||
60 | int | 57 | int |
61 | check_crc(unsigned char *S, unsigned char *buf, u_int32_t len, unsigned char *IV) | 58 | check_crc(unsigned char *S, unsigned char *buf, u_int32_t len, |
59 | unsigned char *IV) | ||
62 | { | 60 | { |
63 | u_int32_t crc; | 61 | u_int32_t crc; |
64 | unsigned char *c; | 62 | unsigned char *c; |
65 | 63 | ||
66 | crc = 0; | 64 | crc = 0; |
67 | if (IV && !CMP(S, IV)) | 65 | if (IV && !CMP(S, IV)) { |
68 | { | 66 | crc_update(&crc, 1); |
69 | crc_update(&crc, 1); | 67 | crc_update(&crc, 0); |
70 | crc_update(&crc, 0); | 68 | } |
71 | } | 69 | for (c = buf; c < buf + len; c += SSH_BLOCKSIZE) { |
72 | for (c = buf; c < buf + len; c += SSH_BLOCKSIZE) | 70 | if (!CMP(S, c)) { |
73 | { | 71 | crc_update(&crc, 1); |
74 | if (!CMP(S, c)) | 72 | crc_update(&crc, 0); |
75 | { | 73 | } else { |
76 | crc_update(&crc, 1); | 74 | crc_update(&crc, 0); |
77 | crc_update(&crc, 0); | 75 | crc_update(&crc, 0); |
78 | } else | 76 | } |
79 | { | 77 | } |
80 | crc_update(&crc, 0); | 78 | return (crc == 0); |
81 | crc_update(&crc, 0); | ||
82 | } | ||
83 | } | ||
84 | |||
85 | return (crc == 0); | ||
86 | } | 79 | } |
87 | 80 | ||
88 | 81 | ||
89 | /* | 82 | /* Detect a crc32 compensation attack on a packet */ |
90 | detect_attack | ||
91 | Detects a crc32 compensation attack on a packet | ||
92 | */ | ||
93 | int | 83 | int |
94 | detect_attack(unsigned char *buf, u_int32_t len, unsigned char *IV) | 84 | detect_attack(unsigned char *buf, u_int32_t len, unsigned char *IV) |
95 | { | 85 | { |
96 | static u_int16_t *h = (u_int16_t *) NULL; | 86 | static u_int16_t *h = (u_int16_t *) NULL; |
97 | static u_int16_t n = HASH_MINSIZE / HASH_ENTRYSIZE; | 87 | static u_int16_t n = HASH_MINSIZE / HASH_ENTRYSIZE; |
98 | register u_int32_t i, j; | 88 | register u_int32_t i, j; |
99 | u_int32_t l; | 89 | u_int32_t l; |
100 | register unsigned char *c; | 90 | register unsigned char *c; |
101 | unsigned char *d; | 91 | unsigned char *d; |
102 | 92 | ||
103 | if (len > (SSH_MAXBLOCKS * SSH_BLOCKSIZE) || | 93 | if (len > (SSH_MAXBLOCKS * SSH_BLOCKSIZE) || |
104 | len % SSH_BLOCKSIZE != 0) { | 94 | len % SSH_BLOCKSIZE != 0) { |
105 | fatal("detect_attack: bad length %d", len); | 95 | fatal("detect_attack: bad length %d", len); |
106 | } | 96 | } |
107 | 97 | for (l = n; l < HASH_FACTOR(len / SSH_BLOCKSIZE); l = l << 2) | |
108 | for (l = n; l < HASH_FACTOR(len / SSH_BLOCKSIZE); l = l << 2); | 98 | ; |
109 | 99 | ||
110 | if (h == NULL) | 100 | if (h == NULL) { |
111 | { | 101 | debug("Installing crc compensation attack detector."); |
112 | debug("Installing crc compensation attack detector."); | 102 | n = l; |
113 | n = l; | 103 | h = (u_int16_t *) xmalloc(n * HASH_ENTRYSIZE); |
114 | h = (u_int16_t *) xmalloc(n * HASH_ENTRYSIZE); | 104 | } else { |
115 | } else | 105 | if (l > n) { |
116 | { | 106 | n = l; |
117 | if (l > n) | 107 | h = (u_int16_t *) xrealloc(h, n * HASH_ENTRYSIZE); |
118 | { | 108 | } |
119 | n = l; | 109 | } |
120 | h = (u_int16_t *) xrealloc(h, n * HASH_ENTRYSIZE); | 110 | |
121 | } | 111 | if (len <= HASH_MINBLOCKS) { |
122 | } | 112 | for (c = buf; c < buf + len; c += SSH_BLOCKSIZE) { |
123 | 113 | if (IV && (!CMP(c, IV))) { | |
124 | 114 | if ((check_crc(c, buf, len, IV))) | |
125 | if (len <= HASH_MINBLOCKS) | 115 | return (DEATTACK_DETECTED); |
126 | { | 116 | else |
127 | for (c = buf; c < buf + len; c += SSH_BLOCKSIZE) | 117 | break; |
128 | { | 118 | } |
129 | if (IV && (!CMP(c, IV))) | 119 | for (d = buf; d < c; d += SSH_BLOCKSIZE) { |
130 | { | 120 | if (!CMP(c, d)) { |
131 | if ((check_crc(c, buf, len, IV))) | 121 | if ((check_crc(c, buf, len, IV))) |
132 | return (DEATTACK_DETECTED); | 122 | return (DEATTACK_DETECTED); |
133 | else | 123 | else |
134 | break; | 124 | break; |
135 | } | 125 | } |
136 | for (d = buf; d < c; d += SSH_BLOCKSIZE) | 126 | } |
137 | { | 127 | } |
138 | if (!CMP(c, d)) | 128 | return (DEATTACK_OK); |
139 | { | ||
140 | if ((check_crc(c, buf, len, IV))) | ||
141 | return (DEATTACK_DETECTED); | ||
142 | else | ||
143 | break; | ||
144 | } | 129 | } |
145 | } | 130 | memset(h, HASH_UNUSEDCHAR, n * HASH_ENTRYSIZE); |
146 | } | 131 | |
147 | return (DEATTACK_OK); | 132 | if (IV) |
148 | } | 133 | h[HASH(IV) & (n - 1)] = HASH_IV; |
149 | memset(h, HASH_UNUSEDCHAR, n * HASH_ENTRYSIZE); | 134 | |
150 | 135 | for (c = buf, j = 0; c < (buf + len); c += SSH_BLOCKSIZE, j++) { | |
151 | if (IV) | 136 | for (i = HASH(c) & (n - 1); h[i] != HASH_UNUSED; |
152 | h[HASH(IV) & (n - 1)] = HASH_IV; | 137 | i = (i + 1) & (n - 1)) { |
153 | 138 | if (h[i] == HASH_IV) { | |
154 | 139 | if (!CMP(c, IV)) { | |
155 | for (c = buf, j = 0; c < (buf + len); c += SSH_BLOCKSIZE, j++) | 140 | if (check_crc(c, buf, len, IV)) |
156 | { | 141 | return (DEATTACK_DETECTED); |
157 | for (i = HASH(c) & (n - 1); h[i] != HASH_UNUSED; | 142 | else |
158 | i = (i + 1) & (n - 1)) | 143 | break; |
159 | { | 144 | } |
160 | if (h[i] == HASH_IV) | 145 | } else if (!CMP(c, buf + h[i] * SSH_BLOCKSIZE)) { |
161 | { | 146 | if (check_crc(c, buf, len, IV)) |
162 | if (!CMP(c, IV)) | 147 | return (DEATTACK_DETECTED); |
163 | { | 148 | else |
164 | if (check_crc(c, buf, len, IV)) | 149 | break; |
165 | return (DEATTACK_DETECTED); | 150 | } |
166 | else | 151 | } |
167 | break; | 152 | h[i] = j; |
168 | } | 153 | } |
169 | } else if (!CMP(c, buf + h[i] * SSH_BLOCKSIZE)) | 154 | return (DEATTACK_OK); |
170 | { | ||
171 | if (check_crc(c, buf, len, IV)) | ||
172 | return (DEATTACK_DETECTED); | ||
173 | else | ||
174 | break; | ||
175 | } | ||
176 | } | ||
177 | h[i] = j; | ||
178 | } | ||
179 | |||
180 | return (DEATTACK_OK); | ||
181 | } | 155 | } |
diff --git a/deattack.h b/deattack.h index a0dcf5b6f..6ce54dedb 100644 --- a/deattack.h +++ b/deattack.h | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $Id: deattack.h,v 1.1 1999/10/27 03:42:44 damien Exp $ | 1 | /* |
2 | * Cryptographic attack detector for ssh - Header file | 2 | * Cryptographic attack detector for ssh - Header file |
3 | * | 3 | * |
4 | * Copyright (c) 1998 CORE SDI S.A., Buenos Aires, Argentina. | 4 | * Copyright (c) 1998 CORE SDI S.A., Buenos Aires, Argentina. |
@@ -14,7 +14,8 @@ | |||
14 | * SOFTWARE. | 14 | * SOFTWARE. |
15 | * | 15 | * |
16 | * Ariel Futoransky <futo@core-sdi.com> | 16 | * Ariel Futoransky <futo@core-sdi.com> |
17 | * <http://www.core-sdi.com> */ | 17 | * <http://www.core-sdi.com> |
18 | */ | ||
18 | 19 | ||
19 | #ifndef _DEATTACK_H | 20 | #ifndef _DEATTACK_H |
20 | #define _DEATTACK_H | 21 | #define _DEATTACK_H |
@@ -23,5 +24,5 @@ | |||
23 | #define DEATTACK_OK 0 | 24 | #define DEATTACK_OK 0 |
24 | #define DEATTACK_DETECTED 1 | 25 | #define DEATTACK_DETECTED 1 |
25 | 26 | ||
26 | int detect_attack(unsigned char *buf, u_int32_t len, unsigned char IV[8]); | 27 | int detect_attack(unsigned char *buf, u_int32_t len, unsigned char IV[8]); |
27 | #endif | 28 | #endif |
diff --git a/fingerprint.c b/fingerprint.c index 9a9b63583..54fc7e9b0 100644 --- a/fingerprint.c +++ b/fingerprint.c | |||
@@ -1,5 +1,5 @@ | |||
1 | #include "includes.h" | 1 | #include "includes.h" |
2 | RCSID("$Id: fingerprint.c,v 1.1 1999/11/17 06:29:08 damien Exp $"); | 2 | RCSID("$Id: fingerprint.c,v 1.3 1999/11/24 00:26:01 deraadt Exp $"); |
3 | 3 | ||
4 | #include "ssh.h" | 4 | #include "ssh.h" |
5 | #include "xmalloc.h" | 5 | #include "xmalloc.h" |
@@ -13,17 +13,18 @@ RCSID("$Id: fingerprint.c,v 1.1 1999/11/17 06:29:08 damien Exp $"); | |||
13 | 13 | ||
14 | #define FPRINT "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x" | 14 | #define FPRINT "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x" |
15 | 15 | ||
16 | /* Generate key fingerprint in ascii format. | 16 | /* |
17 | Based on ideas and code from Bjoern Groenvall <bg@sics.se> */ | 17 | * Generate key fingerprint in ascii format. |
18 | 18 | * Based on ideas and code from Bjoern Groenvall <bg@sics.se> | |
19 | */ | ||
19 | char * | 20 | char * |
20 | fingerprint(BIGNUM *e, BIGNUM *n) | 21 | fingerprint(BIGNUM *e, BIGNUM *n) |
21 | { | 22 | { |
22 | static char retval[80]; | 23 | static char retval[80]; |
23 | MD5_CTX md; | 24 | MD5_CTX md; |
24 | unsigned char d[16]; | 25 | unsigned char d[16]; |
25 | char *buf; | 26 | char *buf; |
26 | int nlen, elen; | 27 | int nlen, elen; |
27 | 28 | ||
28 | nlen = BN_num_bytes(n); | 29 | nlen = BN_num_bytes(n); |
29 | elen = BN_num_bytes(e); | 30 | elen = BN_num_bytes(e); |
@@ -37,8 +38,8 @@ fingerprint(BIGNUM *e, BIGNUM *n) | |||
37 | MD5_Update(&md, buf, nlen + elen); | 38 | MD5_Update(&md, buf, nlen + elen); |
38 | MD5_Final(d, &md); | 39 | MD5_Final(d, &md); |
39 | snprintf(retval, sizeof(retval), FPRINT, | 40 | snprintf(retval, sizeof(retval), FPRINT, |
40 | d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], | 41 | d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], |
41 | d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]); | 42 | d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]); |
42 | memset(buf, 0, nlen + elen); | 43 | memset(buf, 0, nlen + elen); |
43 | xfree(buf); | 44 | xfree(buf); |
44 | return retval; | 45 | return retval; |
diff --git a/fingerprint.h b/fingerprint.h index c4ec22fc4..8c603aa16 100644 --- a/fingerprint.h +++ b/fingerprint.h | |||
@@ -1,6 +1,6 @@ | |||
1 | /* RCSID("$Id: fingerprint.h,v 1.1 1999/11/16 22:49:28 markus Exp $"); */ | 1 | /* RCSID("$Id: fingerprint.h,v 1.2 1999/11/24 00:26:02 deraadt Exp $"); */ |
2 | 2 | ||
3 | #ifndef FINGERPRINT_H | 3 | #ifndef FINGERPRINT_H |
4 | #define FINGERPRINT_H | 4 | #define FINGERPRINT_H |
5 | char * fingerprint(BIGNUM *e, BIGNUM *n); | 5 | char *fingerprint(BIGNUM * e, BIGNUM * n); |
6 | #endif | 6 | #endif |
@@ -1,19 +1,19 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * | |
3 | getput.h | 3 | * getput.h |
4 | 4 | * | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
6 | 6 | * | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
8 | All rights reserved | 8 | * All rights reserved |
9 | 9 | * | |
10 | Created: Wed Jun 28 22:36:30 1995 ylo | 10 | * Created: Wed Jun 28 22:36:30 1995 ylo |
11 | 11 | * | |
12 | Macros for storing and retrieving data in msb first and lsb first order. | 12 | * Macros for storing and retrieving data in msb first and lsb first order. |
13 | 13 | * | |
14 | */ | 14 | */ |
15 | 15 | ||
16 | /* RCSID("$Id: getput.h,v 1.1 1999/10/27 03:42:44 damien Exp $"); */ | 16 | /* RCSID("$Id: getput.h,v 1.2 1999/11/24 13:26:22 damien Exp $"); */ |
17 | 17 | ||
18 | #ifndef GETPUT_H | 18 | #ifndef GETPUT_H |
19 | #define GETPUT_H | 19 | #define GETPUT_H |
@@ -60,5 +60,4 @@ Macros for storing and retrieving data in msb first and lsb first order. | |||
60 | (cp)[0] = (value); \ | 60 | (cp)[0] = (value); \ |
61 | (cp)[1] = (value) >> 8; } while (0) | 61 | (cp)[1] = (value) >> 8; } while (0) |
62 | 62 | ||
63 | #endif /* GETPUT_H */ | 63 | #endif /* GETPUT_H */ |
64 | |||
diff --git a/hostfile.c b/hostfile.c index de21c904e..61046f071 100644 --- a/hostfile.c +++ b/hostfile.c | |||
@@ -1,20 +1,20 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * | |
3 | hostfile.c | 3 | * hostfile.c |
4 | 4 | * | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
6 | 6 | * | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
8 | All rights reserved | 8 | * All rights reserved |
9 | 9 | * | |
10 | Created: Thu Jun 29 07:10:56 1995 ylo | 10 | * Created: Thu Jun 29 07:10:56 1995 ylo |
11 | 11 | * | |
12 | Functions for manipulating the known hosts files. | 12 | * Functions for manipulating the known hosts files. |
13 | 13 | * | |
14 | */ | 14 | */ |
15 | 15 | ||
16 | #include "includes.h" | 16 | #include "includes.h" |
17 | RCSID("$Id: hostfile.c,v 1.4 1999/11/17 06:29:08 damien Exp $"); | 17 | RCSID("$Id: hostfile.c,v 1.5 1999/11/24 13:26:22 damien Exp $"); |
18 | 18 | ||
19 | #include "packet.h" | 19 | #include "packet.h" |
20 | #include "ssh.h" | 20 | #include "ssh.h" |
@@ -26,140 +26,136 @@ RCSID("$Id: hostfile.c,v 1.4 1999/11/17 06:29:08 damien Exp $"); | |||
26 | modify the buffer containing the number. */ | 26 | modify the buffer containing the number. */ |
27 | 27 | ||
28 | int | 28 | int |
29 | auth_rsa_read_bignum(char **cpp, BIGNUM *value) | 29 | auth_rsa_read_bignum(char **cpp, BIGNUM * value) |
30 | { | 30 | { |
31 | char *cp = *cpp; | 31 | char *cp = *cpp; |
32 | int len, old; | 32 | int len, old; |
33 | |||
34 | /* Skip any leading whitespace. */ | ||
35 | for (; *cp == ' ' || *cp == '\t'; cp++); | ||
33 | 36 | ||
34 | /* Skip any leading whitespace. */ | 37 | /* Check that it begins with a hex digit. */ |
35 | for (; *cp == ' ' || *cp == '\t'; cp++) | 38 | if (*cp < '0' || *cp > '9') |
36 | ; | 39 | return 0; |
37 | 40 | ||
38 | /* Check that it begins with a hex digit. */ | 41 | /* Save starting position. */ |
39 | if (*cp < '0' || *cp > '9') | 42 | *cpp = cp; |
40 | return 0; | ||
41 | 43 | ||
42 | /* Save starting position. */ | 44 | /* Move forward until all hex digits skipped. */ |
43 | *cpp = cp; | 45 | for (; *cp >= '0' && *cp <= '9'; cp++); |
44 | 46 | ||
45 | /* Move forward until all hex digits skipped. */ | 47 | /* Compute the length of the hex number. */ |
46 | for (; *cp >= '0' && *cp <= '9'; cp++) | 48 | len = cp - *cpp; |
47 | ; | ||
48 | 49 | ||
49 | /* Compute the length of the hex number. */ | 50 | /* Save the old terminating character, and replace it by \0. */ |
50 | len = cp - *cpp; | 51 | old = *cp; |
52 | *cp = 0; | ||
51 | 53 | ||
52 | /* Save the old terminating character, and replace it by \0. */ | ||
53 | old = *cp; | ||
54 | *cp = 0; | ||
55 | 54 | ||
56 | 55 | /* Parse the number. */ | |
57 | /* Parse the number. */ | 56 | if (BN_dec2bn(&value, *cpp) == 0) |
58 | if (BN_dec2bn(&value, *cpp) == 0) | 57 | return 0; |
59 | return 0; | ||
60 | 58 | ||
61 | /* Restore old terminating character. */ | 59 | /* Restore old terminating character. */ |
62 | *cp = old; | 60 | *cp = old; |
63 | 61 | ||
64 | /* Move beyond the number and return success. */ | 62 | /* Move beyond the number and return success. */ |
65 | *cpp = cp; | 63 | *cpp = cp; |
66 | return 1; | 64 | return 1; |
67 | } | 65 | } |
68 | 66 | ||
69 | /* Parses an RSA key (number of bits, e, n) from a string. Moves the pointer | 67 | /* Parses an RSA key (number of bits, e, n) from a string. Moves the pointer |
70 | over the key. Skips any whitespace at the beginning and at end. */ | 68 | over the key. Skips any whitespace at the beginning and at end. */ |
71 | 69 | ||
72 | int | 70 | int |
73 | auth_rsa_read_key(char **cpp, unsigned int *bitsp, BIGNUM *e, BIGNUM *n) | 71 | auth_rsa_read_key(char **cpp, unsigned int *bitsp, BIGNUM * e, BIGNUM * n) |
74 | { | 72 | { |
75 | unsigned int bits; | 73 | unsigned int bits; |
76 | char *cp; | 74 | char *cp; |
77 | 75 | ||
78 | /* Skip leading whitespace. */ | 76 | /* Skip leading whitespace. */ |
79 | for (cp = *cpp; *cp == ' ' || *cp == '\t'; cp++) | 77 | for (cp = *cpp; *cp == ' ' || *cp == '\t'; cp++); |
80 | ; | 78 | |
81 | 79 | /* Get number of bits. */ | |
82 | /* Get number of bits. */ | 80 | if (*cp < '0' || *cp > '9') |
83 | if (*cp < '0' || *cp > '9') | 81 | return 0; /* Bad bit count... */ |
84 | return 0; /* Bad bit count... */ | 82 | for (bits = 0; *cp >= '0' && *cp <= '9'; cp++) |
85 | for (bits = 0; *cp >= '0' && *cp <= '9'; cp++) | 83 | bits = 10 * bits + *cp - '0'; |
86 | bits = 10 * bits + *cp - '0'; | 84 | |
87 | 85 | /* Get public exponent. */ | |
88 | /* Get public exponent. */ | 86 | if (!auth_rsa_read_bignum(&cp, e)) |
89 | if (!auth_rsa_read_bignum(&cp, e)) | 87 | return 0; |
90 | return 0; | 88 | |
91 | 89 | /* Get public modulus. */ | |
92 | /* Get public modulus. */ | 90 | if (!auth_rsa_read_bignum(&cp, n)) |
93 | if (!auth_rsa_read_bignum(&cp, n)) | 91 | return 0; |
94 | return 0; | 92 | |
95 | 93 | /* Skip trailing whitespace. */ | |
96 | /* Skip trailing whitespace. */ | 94 | for (; *cp == ' ' || *cp == '\t'; cp++); |
97 | for (; *cp == ' ' || *cp == '\t'; cp++) | 95 | |
98 | ; | 96 | /* Return results. */ |
99 | 97 | *cpp = cp; | |
100 | /* Return results. */ | 98 | *bitsp = bits; |
101 | *cpp = cp; | 99 | return 1; |
102 | *bitsp = bits; | ||
103 | return 1; | ||
104 | } | 100 | } |
105 | 101 | ||
106 | /* Tries to match the host name (which must be in all lowercase) against the | 102 | /* Tries to match the host name (which must be in all lowercase) against the |
107 | comma-separated sequence of subpatterns (each possibly preceded by ! to | 103 | comma-separated sequence of subpatterns (each possibly preceded by ! to |
108 | indicate negation). Returns true if there is a positive match; zero | 104 | indicate negation). Returns true if there is a positive match; zero |
109 | otherwise. */ | 105 | otherwise. */ |
110 | 106 | ||
111 | int | 107 | int |
112 | match_hostname(const char *host, const char *pattern, unsigned int len) | 108 | match_hostname(const char *host, const char *pattern, unsigned int len) |
113 | { | 109 | { |
114 | char sub[1024]; | 110 | char sub[1024]; |
115 | int negated; | 111 | int negated; |
116 | int got_positive; | 112 | int got_positive; |
117 | unsigned int i, subi; | 113 | unsigned int i, subi; |
118 | 114 | ||
119 | got_positive = 0; | 115 | got_positive = 0; |
120 | for (i = 0; i < len;) | 116 | for (i = 0; i < len;) { |
121 | { | 117 | /* Check if the subpattern is negated. */ |
122 | /* Check if the subpattern is negated. */ | 118 | if (pattern[i] == '!') { |
123 | if (pattern[i] == '!') | 119 | negated = 1; |
124 | { | 120 | i++; |
125 | negated = 1; | 121 | } else |
126 | i++; | 122 | negated = 0; |
123 | |||
124 | /* Extract the subpattern up to a comma or end. Convert | ||
125 | the subpattern to lowercase. */ | ||
126 | for (subi = 0; | ||
127 | i < len && subi < sizeof(sub) - 1 && pattern[i] != ','; | ||
128 | subi++, i++) | ||
129 | sub[subi] = isupper(pattern[i]) ? tolower(pattern[i]) : pattern[i]; | ||
130 | /* If subpattern too long, return failure (no match). */ | ||
131 | if (subi >= sizeof(sub) - 1) | ||
132 | return 0; | ||
133 | |||
134 | /* If the subpattern was terminated by a comma, skip the | ||
135 | comma. */ | ||
136 | if (i < len && pattern[i] == ',') | ||
137 | i++; | ||
138 | |||
139 | /* Null-terminate the subpattern. */ | ||
140 | sub[subi] = '\0'; | ||
141 | |||
142 | /* Try to match the subpattern against the host name. */ | ||
143 | if (match_pattern(host, sub)) { | ||
144 | if (negated) | ||
145 | return 0; /* Fail if host matches | ||
146 | any negated subpattern. */ | ||
147 | else | ||
148 | got_positive = 1; | ||
149 | } | ||
127 | } | 150 | } |
128 | else | 151 | |
129 | negated = 0; | 152 | /* Return success if got a positive match. If there was a |
130 | 153 | negative match, we have already returned zero and never get | |
131 | /* Extract the subpattern up to a comma or end. Convert the subpattern | 154 | here. */ |
132 | to lowercase. */ | 155 | return got_positive; |
133 | for (subi = 0; | ||
134 | i < len && subi < sizeof(sub) - 1 && pattern[i] != ','; | ||
135 | subi++, i++) | ||
136 | sub[subi] = isupper(pattern[i]) ? tolower(pattern[i]) : pattern[i]; | ||
137 | /* If subpattern too long, return failure (no match). */ | ||
138 | if (subi >= sizeof(sub) - 1) | ||
139 | return 0; | ||
140 | |||
141 | /* If the subpattern was terminated by a comma, skip the comma. */ | ||
142 | if (i < len && pattern[i] == ',') | ||
143 | i++; | ||
144 | |||
145 | /* Null-terminate the subpattern. */ | ||
146 | sub[subi] = '\0'; | ||
147 | |||
148 | /* Try to match the subpattern against the host name. */ | ||
149 | if (match_pattern(host, sub)) { | ||
150 | if (negated) | ||
151 | return 0; /* Fail if host matches any negated subpattern. */ | ||
152 | else | ||
153 | got_positive = 1; | ||
154 | } | ||
155 | } | ||
156 | |||
157 | /* Return success if got a positive match. If there was a negative match, | ||
158 | we have already returned zero and never get here. */ | ||
159 | return got_positive; | ||
160 | } | 156 | } |
161 | 157 | ||
162 | /* Checks whether the given host (which must be in all lowercase) is | 158 | /* Checks whether the given host (which must be in all lowercase) is |
163 | already in the list of our known hosts. | 159 | already in the list of our known hosts. |
164 | Returns HOST_OK if the host is known and has the specified key, | 160 | Returns HOST_OK if the host is known and has the specified key, |
165 | HOST_NEW if the host is not known, and HOST_CHANGED if the host is known | 161 | HOST_NEW if the host is not known, and HOST_CHANGED if the host is known |
@@ -167,87 +163,82 @@ match_hostname(const char *host, const char *pattern, unsigned int len) | |||
167 | 163 | ||
168 | HostStatus | 164 | HostStatus |
169 | check_host_in_hostfile(const char *filename, const char *host, | 165 | check_host_in_hostfile(const char *filename, const char *host, |
170 | BIGNUM *e, BIGNUM *n, BIGNUM *ke, BIGNUM *kn) | 166 | BIGNUM * e, BIGNUM * n, BIGNUM * ke, BIGNUM * kn) |
171 | { | 167 | { |
172 | FILE *f; | 168 | FILE *f; |
173 | char line[8192]; | 169 | char line[8192]; |
174 | int linenum = 0; | 170 | int linenum = 0; |
175 | unsigned int bits, kbits, hostlen; | 171 | unsigned int bits, kbits, hostlen; |
176 | char *cp, *cp2; | 172 | char *cp, *cp2; |
177 | HostStatus end_return; | 173 | HostStatus end_return; |
178 | 174 | ||
179 | /* Open the file containing the list of known hosts. */ | 175 | /* Open the file containing the list of known hosts. */ |
180 | f = fopen(filename, "r"); | 176 | f = fopen(filename, "r"); |
181 | if (!f) | 177 | if (!f) |
182 | return HOST_NEW; | 178 | return HOST_NEW; |
183 | 179 | ||
184 | /* Cache the length of the host name. */ | 180 | /* Cache the length of the host name. */ |
185 | hostlen = strlen(host); | 181 | hostlen = strlen(host); |
186 | 182 | ||
187 | /* Return value when the loop terminates. This is set to HOST_CHANGED if | 183 | /* Return value when the loop terminates. This is set to |
188 | we have seen a different key for the host and have not found the proper | 184 | HOST_CHANGED if we have seen a different key for the host and |
189 | one. */ | 185 | have not found the proper one. */ |
190 | end_return = HOST_NEW; | 186 | end_return = HOST_NEW; |
191 | 187 | ||
192 | /* size of modulus 'n' */ | 188 | /* size of modulus 'n' */ |
193 | bits = BN_num_bits(n); | 189 | bits = BN_num_bits(n); |
194 | 190 | ||
195 | /* Go trough the file. */ | 191 | /* Go trough the file. */ |
196 | while (fgets(line, sizeof(line), f)) | 192 | while (fgets(line, sizeof(line), f)) { |
197 | { | 193 | cp = line; |
198 | cp = line; | 194 | linenum++; |
199 | linenum++; | 195 | |
200 | 196 | /* Skip any leading whitespace. */ | |
201 | /* Skip any leading whitespace. */ | 197 | for (; *cp == ' ' || *cp == '\t'; cp++); |
202 | for (; *cp == ' ' || *cp == '\t'; cp++) | 198 | |
203 | ; | 199 | /* Ignore comment lines and empty lines. */ |
204 | 200 | if (!*cp || *cp == '#' || *cp == '\n') | |
205 | /* Ignore comment lines and empty lines. */ | 201 | continue; |
206 | if (!*cp || *cp == '#' || *cp == '\n') | 202 | |
207 | continue; | 203 | /* Find the end of the host name portion. */ |
208 | 204 | for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++); | |
209 | /* Find the end of the host name portion. */ | 205 | |
210 | for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++) | 206 | /* Check if the host name matches. */ |
211 | ; | 207 | if (!match_hostname(host, cp, (unsigned int) (cp2 - cp))) |
212 | 208 | continue; | |
213 | /* Check if the host name matches. */ | 209 | |
214 | if (!match_hostname(host, cp, (unsigned int)(cp2 - cp))) | 210 | /* Got a match. Skip host name. */ |
215 | continue; | 211 | cp = cp2; |
216 | 212 | ||
217 | /* Got a match. Skip host name. */ | 213 | /* Extract the key from the line. This will skip any |
218 | cp = cp2; | 214 | leading whitespace. Ignore badly formatted lines. */ |
219 | 215 | if (!auth_rsa_read_key(&cp, &kbits, ke, kn)) | |
220 | /* Extract the key from the line. This will skip any leading | 216 | continue; |
221 | whitespace. Ignore badly formatted lines. */ | 217 | |
222 | if (!auth_rsa_read_key(&cp, &kbits, ke, kn)) | 218 | if (kbits != BN_num_bits(kn)) { |
223 | continue; | 219 | error("Warning: error in %s, line %d: keysize mismatch for host %s: " |
224 | 220 | "actual size %d vs. announced %d.", | |
225 | if (kbits != BN_num_bits(kn)) { | 221 | filename, linenum, host, BN_num_bits(kn), kbits); |
226 | error("Warning: error in %s, line %d: keysize mismatch for host %s: " | 222 | error("Warning: replace %d with %d in %s, line %d.", |
227 | "actual size %d vs. announced %d.", | 223 | kbits, BN_num_bits(kn), filename, linenum); |
228 | filename, linenum, host, BN_num_bits(kn), kbits); | 224 | } |
229 | error("Warning: replace %d with %d in %s, line %d.", | 225 | /* Check if the current key is the same as the given key. */ |
230 | kbits, BN_num_bits(kn), filename, linenum); | 226 | if (BN_cmp(ke, e) == 0 && BN_cmp(kn, n) == 0) { |
231 | } | 227 | /* Ok, they match. */ |
232 | 228 | fclose(f); | |
233 | /* Check if the current key is the same as the given key. */ | 229 | return HOST_OK; |
234 | if (BN_cmp(ke, e) == 0 && BN_cmp(kn, n) == 0) | 230 | } |
235 | { | 231 | /* They do not match. We will continue to go through the |
236 | /* Ok, they match. */ | 232 | file; however, we note that we will not return that it |
237 | fclose(f); | 233 | is new. */ |
238 | return HOST_OK; | 234 | end_return = HOST_CHANGED; |
239 | } | 235 | } |
240 | 236 | /* Clear variables and close the file. */ | |
241 | /* They do not match. We will continue to go through the file; however, | 237 | fclose(f); |
242 | we note that we will not return that it is new. */ | 238 | |
243 | end_return = HOST_CHANGED; | 239 | /* Return either HOST_NEW or HOST_CHANGED, depending on whether we |
244 | } | 240 | saw a different key for the host. */ |
245 | /* Clear variables and close the file. */ | 241 | return end_return; |
246 | fclose(f); | ||
247 | |||
248 | /* Return either HOST_NEW or HOST_CHANGED, depending on whether we saw a | ||
249 | different key for the host. */ | ||
250 | return end_return; | ||
251 | } | 242 | } |
252 | 243 | ||
253 | /* Appends an entry to the host file. Returns false if the entry | 244 | /* Appends an entry to the host file. Returns false if the entry |
@@ -255,40 +246,40 @@ check_host_in_hostfile(const char *filename, const char *host, | |||
255 | 246 | ||
256 | int | 247 | int |
257 | add_host_to_hostfile(const char *filename, const char *host, | 248 | add_host_to_hostfile(const char *filename, const char *host, |
258 | BIGNUM *e, BIGNUM *n) | 249 | BIGNUM * e, BIGNUM * n) |
259 | { | 250 | { |
260 | FILE *f; | 251 | FILE *f; |
261 | char *buf; | 252 | char *buf; |
262 | unsigned int bits; | 253 | unsigned int bits; |
263 | 254 | ||
264 | /* Open the file for appending. */ | 255 | /* Open the file for appending. */ |
265 | f = fopen(filename, "a"); | 256 | f = fopen(filename, "a"); |
266 | if (!f) | 257 | if (!f) |
267 | return 0; | 258 | return 0; |
268 | 259 | ||
269 | /* size of modulus 'n' */ | 260 | /* size of modulus 'n' */ |
270 | bits = BN_num_bits(n); | 261 | bits = BN_num_bits(n); |
271 | 262 | ||
272 | /* Print the host name and key to the file. */ | 263 | /* Print the host name and key to the file. */ |
273 | fprintf(f, "%s %u ", host, bits); | 264 | fprintf(f, "%s %u ", host, bits); |
274 | buf = BN_bn2dec(e); | 265 | buf = BN_bn2dec(e); |
275 | if (buf == NULL) { | 266 | if (buf == NULL) { |
276 | error("add_host_to_hostfile: BN_bn2dec(e) failed"); | 267 | error("add_host_to_hostfile: BN_bn2dec(e) failed"); |
277 | fclose(f); | 268 | fclose(f); |
278 | return 0; | 269 | return 0; |
279 | } | 270 | } |
280 | fprintf(f, "%s ", buf); | 271 | fprintf(f, "%s ", buf); |
281 | free (buf); | 272 | free(buf); |
282 | buf = BN_bn2dec(n); | 273 | buf = BN_bn2dec(n); |
283 | if (buf == NULL) { | 274 | if (buf == NULL) { |
284 | error("add_host_to_hostfile: BN_bn2dec(n) failed"); | 275 | error("add_host_to_hostfile: BN_bn2dec(n) failed"); |
285 | fclose(f); | 276 | fclose(f); |
286 | return 0; | 277 | return 0; |
287 | } | 278 | } |
288 | fprintf(f, "%s\n", buf); | 279 | fprintf(f, "%s\n", buf); |
289 | free (buf); | 280 | free(buf); |
290 | 281 | ||
291 | /* Close the file. */ | 282 | /* Close the file. */ |
292 | fclose(f); | 283 | fclose(f); |
293 | return 1; | 284 | return 1; |
294 | } | 285 | } |
diff --git a/includes.h b/includes.h index 722c6b3bd..4a2539696 100644 --- a/includes.h +++ b/includes.h | |||
@@ -1,17 +1,17 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * | |
3 | includes.h | 3 | * includes.h |
4 | 4 | * | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
6 | 6 | * | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
8 | All rights reserved | 8 | * All rights reserved |
9 | 9 | * | |
10 | Created: Thu Mar 23 16:29:37 1995 ylo | 10 | * Created: Thu Mar 23 16:29:37 1995 ylo |
11 | 11 | * | |
12 | This file includes most of the needed system headers. | 12 | * This file includes most of the needed system headers. |
13 | 13 | * | |
14 | */ | 14 | */ |
15 | 15 | ||
16 | #ifndef INCLUDES_H | 16 | #ifndef INCLUDES_H |
17 | #define INCLUDES_H | 17 | #define INCLUDES_H |
@@ -91,4 +91,4 @@ static /**/const char *const rcsid[] = { (char *)rcsid, "\100(#)" msg } | |||
91 | client program. Socketpairs do not seem to work on all systems. */ | 91 | client program. Socketpairs do not seem to work on all systems. */ |
92 | #define USE_PIPES 1 | 92 | #define USE_PIPES 1 |
93 | 93 | ||
94 | #endif /* INCLUDES_H */ | 94 | #endif /* INCLUDES_H */ |
diff --git a/log-client.c b/log-client.c index 63cc79445..4d40cf268 100644 --- a/log-client.c +++ b/log-client.c | |||
@@ -1,21 +1,21 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * | |
3 | log-client.c | 3 | * log-client.c |
4 | 4 | * | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
6 | 6 | * | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
8 | All rights reserved | 8 | * All rights reserved |
9 | 9 | * | |
10 | Created: Mon Mar 20 21:13:40 1995 ylo | 10 | * Created: Mon Mar 20 21:13:40 1995 ylo |
11 | 11 | * | |
12 | Client-side versions of debug(), log(), etc. These print to stderr. | 12 | * Client-side versions of debug(), log(), etc. These print to stderr. |
13 | This is a stripped down version of log-server.c. | 13 | * This is a stripped down version of log-server.c. |
14 | 14 | * | |
15 | */ | 15 | */ |
16 | 16 | ||
17 | #include "includes.h" | 17 | #include "includes.h" |
18 | RCSID("$Id: log-client.c,v 1.2 1999/11/11 06:57:39 damien Exp $"); | 18 | RCSID("$Id: log-client.c,v 1.3 1999/11/24 13:26:22 damien Exp $"); |
19 | 19 | ||
20 | #include "xmalloc.h" | 20 | #include "xmalloc.h" |
21 | #include "ssh.h" | 21 | #include "ssh.h" |
@@ -23,27 +23,26 @@ RCSID("$Id: log-client.c,v 1.2 1999/11/11 06:57:39 damien Exp $"); | |||
23 | static LogLevel log_level = SYSLOG_LEVEL_INFO; | 23 | static LogLevel log_level = SYSLOG_LEVEL_INFO; |
24 | 24 | ||
25 | /* Initialize the log. | 25 | /* Initialize the log. |
26 | av0 program name (should be argv[0]) | 26 | * av0 program name (should be argv[0]) |
27 | level logging level | 27 | * level logging level |
28 | */ | 28 | */ |
29 | 29 | ||
30 | void | 30 | void |
31 | log_init(char *av0, LogLevel level, SyslogFacility ignored1, int ignored2) | 31 | log_init(char *av0, LogLevel level, SyslogFacility ignored1, int ignored2) |
32 | { | 32 | { |
33 | switch (level) | 33 | switch (level) { |
34 | { | 34 | case SYSLOG_LEVEL_QUIET: |
35 | case SYSLOG_LEVEL_QUIET: | 35 | case SYSLOG_LEVEL_ERROR: |
36 | case SYSLOG_LEVEL_ERROR: | 36 | case SYSLOG_LEVEL_FATAL: |
37 | case SYSLOG_LEVEL_FATAL: | 37 | case SYSLOG_LEVEL_INFO: |
38 | case SYSLOG_LEVEL_INFO: | 38 | case SYSLOG_LEVEL_VERBOSE: |
39 | case SYSLOG_LEVEL_CHAT: | 39 | case SYSLOG_LEVEL_DEBUG: |
40 | case SYSLOG_LEVEL_DEBUG: | 40 | log_level = level; |
41 | log_level = level; | 41 | break; |
42 | break; | 42 | default: |
43 | default: | 43 | /* unchanged */ |
44 | /* unchanged */ | 44 | break; |
45 | break; | 45 | } |
46 | } | ||
47 | } | 46 | } |
48 | 47 | ||
49 | #define MSGBUFSIZE 1024 | 48 | #define MSGBUFSIZE 1024 |
@@ -51,13 +50,13 @@ log_init(char *av0, LogLevel level, SyslogFacility ignored1, int ignored2) | |||
51 | void | 50 | void |
52 | do_log(LogLevel level, const char *fmt, va_list args) | 51 | do_log(LogLevel level, const char *fmt, va_list args) |
53 | { | 52 | { |
54 | char msgbuf[MSGBUFSIZE]; | 53 | char msgbuf[MSGBUFSIZE]; |
55 | 54 | ||
56 | if (level > log_level) | 55 | if (level > log_level) |
57 | return; | 56 | return; |
58 | if (level == SYSLOG_LEVEL_DEBUG) | 57 | if (level == SYSLOG_LEVEL_DEBUG) |
59 | fprintf(stderr, "debug: "); | 58 | fprintf(stderr, "debug: "); |
60 | vsnprintf(msgbuf, sizeof(msgbuf), fmt, args); | 59 | vsnprintf(msgbuf, sizeof(msgbuf), fmt, args); |
61 | fprintf(stderr, "%s", msgbuf); | 60 | fprintf(stderr, "%s", msgbuf); |
62 | fprintf(stderr, "\r\n"); | 61 | fprintf(stderr, "\r\n"); |
63 | } | 62 | } |
diff --git a/log-server.c b/log-server.c index 42f567af4..34f77b2be 100644 --- a/log-server.c +++ b/log-server.c | |||
@@ -1,21 +1,21 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * | |
3 | log-server.c | 3 | * log-server.c |
4 | 4 | * | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
6 | 6 | * | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
8 | All rights reserved | 8 | * All rights reserved |
9 | 9 | * | |
10 | Created: Mon Mar 20 21:19:30 1995 ylo | 10 | * Created: Mon Mar 20 21:19:30 1995 ylo |
11 | 11 | * | |
12 | Server-side versions of debug(), log(), etc. These normally send the output | 12 | * Server-side versions of debug(), log(), etc. These normally send the output |
13 | to the system log. | 13 | * to the system log. |
14 | 14 | * | |
15 | */ | 15 | */ |
16 | 16 | ||
17 | #include "includes.h" | 17 | #include "includes.h" |
18 | RCSID("$Id: log-server.c,v 1.4 1999/11/15 06:10:57 damien Exp $"); | 18 | RCSID("$Id: log-server.c,v 1.5 1999/11/24 13:26:22 damien Exp $"); |
19 | 19 | ||
20 | #include <syslog.h> | 20 | #include <syslog.h> |
21 | #include "packet.h" | 21 | #include "packet.h" |
@@ -33,72 +33,68 @@ static int log_on_stderr = 0; | |||
33 | static int log_facility = LOG_AUTH; | 33 | static int log_facility = LOG_AUTH; |
34 | 34 | ||
35 | /* Initialize the log. | 35 | /* Initialize the log. |
36 | av0 program name (should be argv[0]) | 36 | * av0 program name (should be argv[0]) |
37 | on_stderr print also on stderr | 37 | * on_stderr print also on stderr |
38 | level logging level | 38 | * level logging level |
39 | */ | 39 | */ |
40 | 40 | ||
41 | void log_init(char *av0, LogLevel level, SyslogFacility facility, int on_stderr) | 41 | void |
42 | log_init(char *av0, LogLevel level, SyslogFacility facility, int on_stderr) | ||
42 | { | 43 | { |
43 | 44 | switch (level) { | |
44 | switch (level) | 45 | case SYSLOG_LEVEL_QUIET: |
45 | { | 46 | case SYSLOG_LEVEL_ERROR: |
46 | case SYSLOG_LEVEL_QUIET: | 47 | case SYSLOG_LEVEL_FATAL: |
47 | case SYSLOG_LEVEL_ERROR: | 48 | case SYSLOG_LEVEL_INFO: |
48 | case SYSLOG_LEVEL_FATAL: | 49 | case SYSLOG_LEVEL_VERBOSE: |
49 | case SYSLOG_LEVEL_INFO: | 50 | case SYSLOG_LEVEL_DEBUG: |
50 | case SYSLOG_LEVEL_CHAT: | 51 | log_level = level; |
51 | case SYSLOG_LEVEL_DEBUG: | 52 | break; |
52 | log_level = level; | 53 | default: |
53 | break; | 54 | fprintf(stderr, "Unrecognized internal syslog level code %d\n", |
54 | default: | 55 | (int) level); |
55 | fprintf(stderr, "Unrecognized internal syslog level code %d\n", | 56 | exit(1); |
56 | (int)level); | 57 | } |
57 | exit(1); | 58 | switch (facility) { |
58 | } | 59 | case SYSLOG_FACILITY_DAEMON: |
59 | 60 | log_facility = LOG_DAEMON; | |
60 | switch (facility) | 61 | break; |
61 | { | 62 | case SYSLOG_FACILITY_USER: |
62 | case SYSLOG_FACILITY_DAEMON: | 63 | log_facility = LOG_USER; |
63 | log_facility = LOG_DAEMON; | 64 | break; |
64 | break; | 65 | case SYSLOG_FACILITY_AUTH: |
65 | case SYSLOG_FACILITY_USER: | 66 | log_facility = LOG_AUTH; |
66 | log_facility = LOG_USER; | 67 | break; |
67 | break; | 68 | case SYSLOG_FACILITY_LOCAL0: |
68 | case SYSLOG_FACILITY_AUTH: | 69 | log_facility = LOG_LOCAL0; |
69 | log_facility = LOG_AUTH; | 70 | break; |
70 | break; | 71 | case SYSLOG_FACILITY_LOCAL1: |
71 | case SYSLOG_FACILITY_LOCAL0: | 72 | log_facility = LOG_LOCAL1; |
72 | log_facility = LOG_LOCAL0; | 73 | break; |
73 | break; | 74 | case SYSLOG_FACILITY_LOCAL2: |
74 | case SYSLOG_FACILITY_LOCAL1: | 75 | log_facility = LOG_LOCAL2; |
75 | log_facility = LOG_LOCAL1; | 76 | break; |
76 | break; | 77 | case SYSLOG_FACILITY_LOCAL3: |
77 | case SYSLOG_FACILITY_LOCAL2: | 78 | log_facility = LOG_LOCAL3; |
78 | log_facility = LOG_LOCAL2; | 79 | break; |
79 | break; | 80 | case SYSLOG_FACILITY_LOCAL4: |
80 | case SYSLOG_FACILITY_LOCAL3: | 81 | log_facility = LOG_LOCAL4; |
81 | log_facility = LOG_LOCAL3; | 82 | break; |
82 | break; | 83 | case SYSLOG_FACILITY_LOCAL5: |
83 | case SYSLOG_FACILITY_LOCAL4: | 84 | log_facility = LOG_LOCAL5; |
84 | log_facility = LOG_LOCAL4; | 85 | break; |
85 | break; | 86 | case SYSLOG_FACILITY_LOCAL6: |
86 | case SYSLOG_FACILITY_LOCAL5: | 87 | log_facility = LOG_LOCAL6; |
87 | log_facility = LOG_LOCAL5; | 88 | break; |
88 | break; | 89 | case SYSLOG_FACILITY_LOCAL7: |
89 | case SYSLOG_FACILITY_LOCAL6: | 90 | log_facility = LOG_LOCAL7; |
90 | log_facility = LOG_LOCAL6; | 91 | break; |
91 | break; | 92 | default: |
92 | case SYSLOG_FACILITY_LOCAL7: | 93 | fprintf(stderr, "Unrecognized internal syslog facility code %d\n", |
93 | log_facility = LOG_LOCAL7; | 94 | (int) facility); |
94 | break; | 95 | exit(1); |
95 | default: | 96 | } |
96 | fprintf(stderr, "Unrecognized internal syslog facility code %d\n", | 97 | log_on_stderr = on_stderr; |
97 | (int)facility); | ||
98 | exit(1); | ||
99 | } | ||
100 | |||
101 | log_on_stderr = on_stderr; | ||
102 | } | 98 | } |
103 | 99 | ||
104 | #define MSGBUFSIZE 1024 | 100 | #define MSGBUFSIZE 1024 |
@@ -106,48 +102,44 @@ void log_init(char *av0, LogLevel level, SyslogFacility facility, int on_stderr) | |||
106 | void | 102 | void |
107 | do_log(LogLevel level, const char *fmt, va_list args) | 103 | do_log(LogLevel level, const char *fmt, va_list args) |
108 | { | 104 | { |
109 | char msgbuf[MSGBUFSIZE]; | 105 | char msgbuf[MSGBUFSIZE]; |
110 | char fmtbuf[MSGBUFSIZE]; | 106 | char fmtbuf[MSGBUFSIZE]; |
111 | char *txt = NULL; | 107 | char *txt = NULL; |
112 | int pri = LOG_INFO; | 108 | int pri = LOG_INFO; |
113 | 109 | ||
114 | if (level > log_level) | 110 | if (level > log_level) |
115 | return; | 111 | return; |
116 | switch (level) | 112 | switch (level) { |
117 | { | 113 | case SYSLOG_LEVEL_ERROR: |
118 | case SYSLOG_LEVEL_ERROR: | 114 | txt = "error"; |
119 | txt = "error"; | 115 | pri = LOG_ERR; |
120 | pri = LOG_ERR; | 116 | break; |
121 | break; | 117 | case SYSLOG_LEVEL_FATAL: |
122 | case SYSLOG_LEVEL_FATAL: | 118 | txt = "fatal"; |
123 | txt = "fatal"; | 119 | pri = LOG_ERR; |
124 | pri = LOG_ERR; | 120 | break; |
125 | break; | 121 | case SYSLOG_LEVEL_INFO: |
126 | case SYSLOG_LEVEL_INFO: | 122 | case SYSLOG_LEVEL_VERBOSE: |
127 | pri = LOG_INFO; | 123 | pri = LOG_INFO; |
128 | break; | 124 | break; |
129 | case SYSLOG_LEVEL_CHAT: | 125 | case SYSLOG_LEVEL_DEBUG: |
130 | pri = LOG_INFO; | 126 | txt = "debug"; |
131 | break; | 127 | pri = LOG_DEBUG; |
132 | case SYSLOG_LEVEL_DEBUG: | 128 | break; |
133 | txt = "debug"; | 129 | default: |
134 | pri = LOG_DEBUG; | 130 | txt = "internal error"; |
135 | break; | 131 | pri = LOG_ERR; |
136 | default: | 132 | break; |
137 | txt = "internal error"; | 133 | } |
138 | pri = LOG_ERR; | 134 | if (txt != NULL) { |
139 | break; | 135 | snprintf(fmtbuf, sizeof(fmtbuf), "%s: %s", txt, fmt); |
140 | } | 136 | vsnprintf(msgbuf, sizeof(msgbuf), fmtbuf, args); |
141 | 137 | } else { | |
142 | if (txt != NULL) { | 138 | vsnprintf(msgbuf, sizeof(msgbuf), fmt, args); |
143 | snprintf(fmtbuf, sizeof(fmtbuf), "%s: %s", txt, fmt); | 139 | } |
144 | vsnprintf(msgbuf, sizeof(msgbuf), fmtbuf, args); | 140 | if (log_on_stderr) |
145 | }else{ | 141 | fprintf(stderr, "%s\n", msgbuf); |
146 | vsnprintf(msgbuf, sizeof(msgbuf), fmt, args); | 142 | openlog(__progname, LOG_PID, log_facility); |
147 | } | 143 | syslog(pri, "%.500s", msgbuf); |
148 | if (log_on_stderr) | 144 | closelog(); |
149 | fprintf(stderr, "%s\n", msgbuf); | ||
150 | openlog(__progname, LOG_PID, log_facility); | ||
151 | syslog(pri, "%.500s", msgbuf); | ||
152 | closelog(); | ||
153 | } | 145 | } |
@@ -1,11 +1,11 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * | |
3 | Shared versions of debug(), log(), etc. | 3 | * Shared versions of debug(), log(), etc. |
4 | 4 | * | |
5 | */ | 5 | */ |
6 | 6 | ||
7 | #include "includes.h" | 7 | #include "includes.h" |
8 | RCSID("$OpenBSD: log.c,v 1.2 1999/11/19 16:04:17 markus Exp $"); | 8 | RCSID("$OpenBSD: log.c,v 1.5 1999/11/24 00:26:02 deraadt Exp $"); |
9 | 9 | ||
10 | #include "ssh.h" | 10 | #include "ssh.h" |
11 | #include "xmalloc.h" | 11 | #include "xmalloc.h" |
@@ -13,66 +13,65 @@ RCSID("$OpenBSD: log.c,v 1.2 1999/11/19 16:04:17 markus Exp $"); | |||
13 | /* Fatal messages. This function never returns. */ | 13 | /* Fatal messages. This function never returns. */ |
14 | 14 | ||
15 | void | 15 | void |
16 | fatal(const char *fmt, ...) | 16 | fatal(const char *fmt,...) |
17 | { | 17 | { |
18 | va_list args; | 18 | va_list args; |
19 | va_start(args, fmt); | 19 | va_start(args, fmt); |
20 | do_log(SYSLOG_LEVEL_FATAL, fmt, args); | 20 | do_log(SYSLOG_LEVEL_FATAL, fmt, args); |
21 | va_end(args); | 21 | va_end(args); |
22 | fatal_cleanup(); | 22 | fatal_cleanup(); |
23 | } | 23 | } |
24 | 24 | ||
25 | /* Error messages that should be logged. */ | 25 | /* Error messages that should be logged. */ |
26 | 26 | ||
27 | void | 27 | void |
28 | error(const char *fmt, ...) | 28 | error(const char *fmt,...) |
29 | { | 29 | { |
30 | va_list args; | 30 | va_list args; |
31 | va_start(args, fmt); | 31 | va_start(args, fmt); |
32 | do_log(SYSLOG_LEVEL_ERROR, fmt, args); | 32 | do_log(SYSLOG_LEVEL_ERROR, fmt, args); |
33 | va_end(args); | 33 | va_end(args); |
34 | } | 34 | } |
35 | 35 | ||
36 | /* Log this message (information that usually should go to the log). */ | 36 | /* Log this message (information that usually should go to the log). */ |
37 | 37 | ||
38 | void | 38 | void |
39 | log(const char *fmt, ...) | 39 | log(const char *fmt,...) |
40 | { | 40 | { |
41 | va_list args; | 41 | va_list args; |
42 | va_start(args, fmt); | 42 | va_start(args, fmt); |
43 | do_log(SYSLOG_LEVEL_INFO, fmt, args); | 43 | do_log(SYSLOG_LEVEL_INFO, fmt, args); |
44 | va_end(args); | 44 | va_end(args); |
45 | } | 45 | } |
46 | 46 | ||
47 | /* More detailed messages (information that does not need to go to the log). */ | 47 | /* More detailed messages (information that does not need to go to the log). */ |
48 | 48 | ||
49 | void | 49 | void |
50 | chat(const char *fmt, ...) | 50 | verbose(const char *fmt,...) |
51 | { | 51 | { |
52 | va_list args; | 52 | va_list args; |
53 | va_start(args, fmt); | 53 | va_start(args, fmt); |
54 | do_log(SYSLOG_LEVEL_CHAT, fmt, args); | 54 | do_log(SYSLOG_LEVEL_VERBOSE, fmt, args); |
55 | va_end(args); | 55 | va_end(args); |
56 | } | 56 | } |
57 | 57 | ||
58 | /* Debugging messages that should not be logged during normal operation. */ | 58 | /* Debugging messages that should not be logged during normal operation. */ |
59 | 59 | ||
60 | void | 60 | void |
61 | debug(const char *fmt, ...) | 61 | debug(const char *fmt,...) |
62 | { | 62 | { |
63 | va_list args; | 63 | va_list args; |
64 | va_start(args, fmt); | 64 | va_start(args, fmt); |
65 | do_log(SYSLOG_LEVEL_DEBUG, fmt, args); | 65 | do_log(SYSLOG_LEVEL_DEBUG, fmt, args); |
66 | va_end(args); | 66 | va_end(args); |
67 | } | 67 | } |
68 | 68 | ||
69 | /* Fatal cleanup */ | 69 | /* Fatal cleanup */ |
70 | 70 | ||
71 | struct fatal_cleanup | 71 | struct fatal_cleanup { |
72 | { | 72 | struct fatal_cleanup *next; |
73 | struct fatal_cleanup *next; | 73 | void (*proc) (void *); |
74 | void (*proc)(void *); | 74 | void *context; |
75 | void *context; | ||
76 | }; | 75 | }; |
77 | 76 | ||
78 | static struct fatal_cleanup *fatal_cleanups = NULL; | 77 | static struct fatal_cleanup *fatal_cleanups = NULL; |
@@ -80,116 +79,108 @@ static struct fatal_cleanup *fatal_cleanups = NULL; | |||
80 | /* Registers a cleanup function to be called by fatal() before exiting. */ | 79 | /* Registers a cleanup function to be called by fatal() before exiting. */ |
81 | 80 | ||
82 | void | 81 | void |
83 | fatal_add_cleanup(void (*proc)(void *), void *context) | 82 | fatal_add_cleanup(void (*proc) (void *), void *context) |
84 | { | 83 | { |
85 | struct fatal_cleanup *cu; | 84 | struct fatal_cleanup *cu; |
86 | 85 | ||
87 | cu = xmalloc(sizeof(*cu)); | 86 | cu = xmalloc(sizeof(*cu)); |
88 | cu->proc = proc; | 87 | cu->proc = proc; |
89 | cu->context = context; | 88 | cu->context = context; |
90 | cu->next = fatal_cleanups; | 89 | cu->next = fatal_cleanups; |
91 | fatal_cleanups = cu; | 90 | fatal_cleanups = cu; |
92 | } | 91 | } |
93 | 92 | ||
94 | /* Removes a cleanup frunction to be called at fatal(). */ | 93 | /* Removes a cleanup frunction to be called at fatal(). */ |
95 | 94 | ||
96 | void | 95 | void |
97 | fatal_remove_cleanup(void (*proc)(void *context), void *context) | 96 | fatal_remove_cleanup(void (*proc) (void *context), void *context) |
98 | { | 97 | { |
99 | struct fatal_cleanup **cup, *cu; | 98 | struct fatal_cleanup **cup, *cu; |
100 | 99 | ||
101 | for (cup = &fatal_cleanups; *cup; cup = &cu->next) | 100 | for (cup = &fatal_cleanups; *cup; cup = &cu->next) { |
102 | { | 101 | cu = *cup; |
103 | cu = *cup; | 102 | if (cu->proc == proc && cu->context == context) { |
104 | if (cu->proc == proc && cu->context == context) | 103 | *cup = cu->next; |
105 | { | 104 | xfree(cu); |
106 | *cup = cu->next; | 105 | return; |
107 | xfree(cu); | 106 | } |
108 | return; | ||
109 | } | 107 | } |
110 | } | 108 | fatal("fatal_remove_cleanup: no such cleanup function: 0x%lx 0x%lx\n", |
111 | fatal("fatal_remove_cleanup: no such cleanup function: 0x%lx 0x%lx\n", | 109 | (unsigned long) proc, (unsigned long) context); |
112 | (unsigned long)proc, (unsigned long)context); | ||
113 | } | 110 | } |
114 | 111 | ||
115 | /* Cleanup and exit */ | 112 | /* Cleanup and exit */ |
116 | void | 113 | void |
117 | fatal_cleanup(void) | 114 | fatal_cleanup(void) |
118 | { | 115 | { |
119 | struct fatal_cleanup *cu, *next_cu; | 116 | struct fatal_cleanup *cu, *next_cu; |
120 | static int called = 0; | 117 | static int called = 0; |
121 | if (called) | 118 | |
122 | exit(255); | 119 | if (called) |
123 | called = 1; | 120 | exit(255); |
124 | 121 | called = 1; | |
125 | /* Call cleanup functions. */ | 122 | /* Call cleanup functions. */ |
126 | for (cu = fatal_cleanups; cu; cu = next_cu) | 123 | for (cu = fatal_cleanups; cu; cu = next_cu) { |
127 | { | 124 | next_cu = cu->next; |
128 | next_cu = cu->next; | 125 | debug("Calling cleanup 0x%lx(0x%lx)", |
129 | debug("Calling cleanup 0x%lx(0x%lx)", | 126 | (unsigned long) cu->proc, (unsigned long) cu->context); |
130 | (unsigned long)cu->proc, (unsigned long)cu->context); | 127 | (*cu->proc) (cu->context); |
131 | (*cu->proc)(cu->context); | 128 | } |
132 | } | 129 | exit(255); |
133 | |||
134 | exit(255); | ||
135 | } | 130 | } |
136 | 131 | ||
137 | /* textual representation of log-facilities/levels */ | 132 | /* textual representation of log-facilities/levels */ |
138 | 133 | ||
139 | 134 | static struct { | |
140 | static struct | 135 | const char *name; |
141 | { | 136 | SyslogFacility val; |
142 | const char *name; | 137 | } log_facilities[] = { |
143 | SyslogFacility val; | 138 | { "DAEMON", SYSLOG_FACILITY_DAEMON }, |
144 | } log_facilities[] = | 139 | { "USER", SYSLOG_FACILITY_USER }, |
145 | { | 140 | { "AUTH", SYSLOG_FACILITY_AUTH }, |
146 | { "DAEMON", SYSLOG_FACILITY_DAEMON }, | 141 | { "LOCAL0", SYSLOG_FACILITY_LOCAL0 }, |
147 | { "USER", SYSLOG_FACILITY_USER }, | 142 | { "LOCAL1", SYSLOG_FACILITY_LOCAL1 }, |
148 | { "AUTH", SYSLOG_FACILITY_AUTH }, | 143 | { "LOCAL2", SYSLOG_FACILITY_LOCAL2 }, |
149 | { "LOCAL0", SYSLOG_FACILITY_LOCAL0 }, | 144 | { "LOCAL3", SYSLOG_FACILITY_LOCAL3 }, |
150 | { "LOCAL1", SYSLOG_FACILITY_LOCAL1 }, | 145 | { "LOCAL4", SYSLOG_FACILITY_LOCAL4 }, |
151 | { "LOCAL2", SYSLOG_FACILITY_LOCAL2 }, | 146 | { "LOCAL5", SYSLOG_FACILITY_LOCAL5 }, |
152 | { "LOCAL3", SYSLOG_FACILITY_LOCAL3 }, | 147 | { "LOCAL6", SYSLOG_FACILITY_LOCAL6 }, |
153 | { "LOCAL4", SYSLOG_FACILITY_LOCAL4 }, | 148 | { "LOCAL7", SYSLOG_FACILITY_LOCAL7 }, |
154 | { "LOCAL5", SYSLOG_FACILITY_LOCAL5 }, | 149 | { NULL, 0 } |
155 | { "LOCAL6", SYSLOG_FACILITY_LOCAL6 }, | ||
156 | { "LOCAL7", SYSLOG_FACILITY_LOCAL7 }, | ||
157 | { NULL, 0 } | ||
158 | }; | 150 | }; |
159 | 151 | ||
160 | static struct | 152 | static struct { |
161 | { | 153 | const char *name; |
162 | const char *name; | 154 | LogLevel val; |
163 | LogLevel val; | ||
164 | } log_levels[] = | 155 | } log_levels[] = |
165 | { | 156 | { |
166 | { "QUIET", SYSLOG_LEVEL_QUIET }, | 157 | { "QUIET", SYSLOG_LEVEL_QUIET }, |
167 | { "FATAL", SYSLOG_LEVEL_FATAL }, | 158 | { "FATAL", SYSLOG_LEVEL_FATAL }, |
168 | { "ERROR", SYSLOG_LEVEL_ERROR }, | 159 | { "ERROR", SYSLOG_LEVEL_ERROR }, |
169 | { "INFO", SYSLOG_LEVEL_INFO }, | 160 | { "INFO", SYSLOG_LEVEL_INFO }, |
170 | { "CHAT", SYSLOG_LEVEL_CHAT }, | 161 | { "VERBOSE", SYSLOG_LEVEL_VERBOSE }, |
171 | { "DEBUG", SYSLOG_LEVEL_DEBUG }, | 162 | { "DEBUG", SYSLOG_LEVEL_DEBUG }, |
172 | { NULL, 0 } | 163 | { NULL, 0 } |
173 | }; | 164 | }; |
174 | 165 | ||
175 | SyslogFacility | 166 | SyslogFacility |
176 | log_facility_number(char *name) | 167 | log_facility_number(char *name) |
177 | { | 168 | { |
178 | int i; | 169 | int i; |
179 | if (name != NULL) | 170 | if (name != NULL) |
180 | for (i = 0; log_facilities[i].name; i++) | 171 | for (i = 0; log_facilities[i].name; i++) |
181 | if (strcasecmp(log_facilities[i].name, name) == 0) | 172 | if (strcasecmp(log_facilities[i].name, name) == 0) |
182 | return log_facilities[i].val; | 173 | return log_facilities[i].val; |
183 | return (SyslogFacility)-1; | 174 | return (SyslogFacility) - 1; |
184 | } | 175 | } |
185 | 176 | ||
186 | LogLevel | 177 | LogLevel |
187 | log_level_number(char *name) | 178 | log_level_number(char *name) |
188 | { | 179 | { |
189 | int i; | 180 | int i; |
190 | if (name != NULL) | 181 | if (name != NULL) |
191 | for (i = 0; log_levels[i].name; i++) | 182 | for (i = 0; log_levels[i].name; i++) |
192 | if (strcasecmp(log_levels[i].name, name) == 0) | 183 | if (strcasecmp(log_levels[i].name, name) == 0) |
193 | return log_levels[i].val; | 184 | return log_levels[i].val; |
194 | return (LogLevel)-1; | 185 | return (LogLevel) - 1; |
195 | } | 186 | } |
@@ -1,128 +1,129 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * | |
3 | login.c | 3 | * login.c |
4 | 4 | * | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
6 | 6 | * | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
8 | All rights reserved | 8 | * All rights reserved |
9 | 9 | * | |
10 | Created: Fri Mar 24 14:51:08 1995 ylo | 10 | * Created: Fri Mar 24 14:51:08 1995 ylo |
11 | 11 | * | |
12 | This file performs some of the things login(1) normally does. We cannot | 12 | * This file performs some of the things login(1) normally does. We cannot |
13 | easily use something like login -p -h host -f user, because there are | 13 | * easily use something like login -p -h host -f user, because there are |
14 | several different logins around, and it is hard to determined what kind of | 14 | * several different logins around, and it is hard to determined what kind of |
15 | login the current system has. Also, we want to be able to execute commands | 15 | * login the current system has. Also, we want to be able to execute commands |
16 | on a tty. | 16 | * on a tty. |
17 | 17 | * | |
18 | */ | 18 | */ |
19 | 19 | ||
20 | #include "includes.h" | 20 | #include "includes.h" |
21 | RCSID("$Id: login.c,v 1.2 1999/11/10 23:40:23 damien Exp $"); | 21 | RCSID("$Id: login.c,v 1.3 1999/11/24 13:26:22 damien Exp $"); |
22 | 22 | ||
23 | #include <utmp.h> | 23 | #include <utmp.h> |
24 | #include "ssh.h" | ||
24 | 25 | ||
26 | #ifdef HAVE_UTIL_H | ||
27 | # include <util.h> | ||
28 | #endif | ||
25 | #ifdef HAVE_LASTLOG_H | 29 | #ifdef HAVE_LASTLOG_H |
26 | # include <lastlog.h> | 30 | # include <lastlog.h> |
27 | #endif | 31 | #endif |
28 | 32 | ||
29 | #include "ssh.h" | 33 | /* Returns the time when the user last logged in. Returns 0 if the |
30 | 34 | information is not available. This must be called before record_login. | |
31 | /* Returns the time when the user last logged in. Returns 0 if the | ||
32 | information is not available. This must be called before record_login. | ||
33 | The host the user logged in from will be returned in buf. */ | 35 | The host the user logged in from will be returned in buf. */ |
34 | 36 | ||
35 | /* Returns the time when the user last logged in (or 0 if no previous login | 37 | /* Returns the time when the user last logged in (or 0 if no previous login |
36 | is found). The name of the host used last time is returned in buf. */ | 38 | is found). The name of the host used last time is returned in buf. */ |
37 | 39 | ||
38 | unsigned long get_last_login_time(uid_t uid, const char *logname, | 40 | unsigned long |
39 | char *buf, unsigned int bufsize) | 41 | get_last_login_time(uid_t uid, const char *logname, |
42 | char *buf, unsigned int bufsize) | ||
40 | { | 43 | { |
41 | struct lastlog ll; | 44 | struct lastlog ll; |
42 | char *lastlog; | 45 | char *lastlog; |
43 | int fd; | 46 | int fd; |
44 | 47 | ||
45 | lastlog = _PATH_LASTLOG; | 48 | lastlog = _PATH_LASTLOG; |
46 | 49 | buf[0] = '\0'; | |
47 | buf[0] = '\0'; | 50 | |
48 | 51 | fd = open(lastlog, O_RDONLY); | |
49 | fd = open(lastlog, O_RDONLY); | 52 | if (fd < 0) |
50 | if (fd < 0) | 53 | return 0; |
51 | return 0; | 54 | lseek(fd, (off_t) ((long) uid * sizeof(ll)), SEEK_SET); |
52 | lseek(fd, (off_t)((long)uid * sizeof(ll)), SEEK_SET); | 55 | if (read(fd, &ll, sizeof(ll)) != sizeof(ll)) { |
53 | if (read(fd, &ll, sizeof(ll)) != sizeof(ll)) | 56 | close(fd); |
54 | { | 57 | return 0; |
55 | close(fd); | 58 | } |
56 | return 0; | 59 | close(fd); |
57 | } | 60 | if (bufsize > sizeof(ll.ll_host) + 1) |
58 | close(fd); | 61 | bufsize = sizeof(ll.ll_host) + 1; |
59 | if (bufsize > sizeof(ll.ll_host) + 1) | 62 | strncpy(buf, ll.ll_host, bufsize - 1); |
60 | bufsize = sizeof(ll.ll_host) + 1; | 63 | buf[bufsize - 1] = 0; |
61 | strncpy(buf, ll.ll_host, bufsize - 1); | 64 | return ll.ll_time; |
62 | buf[bufsize - 1] = 0; | ||
63 | return ll.ll_time; | ||
64 | } | 65 | } |
65 | 66 | ||
66 | /* Records that the user has logged in. I these parts of operating systems | 67 | /* Records that the user has logged in. I these parts of operating systems |
67 | were more standardized. */ | 68 | were more standardized. */ |
68 | 69 | ||
69 | void record_login(int pid, const char *ttyname, const char *user, uid_t uid, | 70 | void |
70 | const char *host, struct sockaddr_in *addr) | 71 | record_login(int pid, const char *ttyname, const char *user, uid_t uid, |
72 | const char *host, struct sockaddr_in * addr) | ||
71 | { | 73 | { |
72 | int fd; | 74 | int fd; |
73 | struct lastlog ll; | 75 | struct lastlog ll; |
74 | char *lastlog; | 76 | char *lastlog; |
75 | 77 | struct utmp u; | |
76 | struct utmp u; | 78 | const char *utmp, *wtmp; |
77 | const char *utmp, *wtmp; | 79 | |
78 | 80 | /* Construct an utmp/wtmp entry. */ | |
79 | /* Construct an utmp/wtmp entry. */ | 81 | memset(&u, 0, sizeof(u)); |
80 | memset(&u, 0, sizeof(u)); | 82 | strncpy(u.ut_line, ttyname + 5, sizeof(u.ut_line)); |
81 | strncpy(u.ut_line, ttyname + 5, sizeof(u.ut_line)); | 83 | u.ut_time = time(NULL); |
82 | u.ut_time = time(NULL); | 84 | strncpy(u.ut_name, user, sizeof(u.ut_name)); |
83 | strncpy(u.ut_name, user, sizeof(u.ut_name)); | ||
84 | #ifdef HAVE_HOST_IN_UTMP | 85 | #ifdef HAVE_HOST_IN_UTMP |
85 | strncpy(u.ut_host, host, sizeof(u.ut_host)); | 86 | strncpy(u.ut_host, host, sizeof(u.ut_host)); |
86 | #endif | 87 | #endif |
87 | 88 | ||
88 | /* Figure out the file names. */ | 89 | /* Figure out the file names. */ |
89 | utmp = _PATH_UTMP; | 90 | utmp = _PATH_UTMP; |
90 | wtmp = _PATH_WTMP; | 91 | wtmp = _PATH_WTMP; |
91 | 92 | ||
92 | login(&u); | 93 | login(&u); |
93 | 94 | lastlog = _PATH_LASTLOG; | |
94 | lastlog = _PATH_LASTLOG; | 95 | |
95 | 96 | /* Update lastlog unless actually recording a logout. */ | |
96 | /* Update lastlog unless actually recording a logout. */ | 97 | if (strcmp(user, "") != 0) { |
97 | if (strcmp(user, "") != 0) | 98 | /* It is safer to bzero the lastlog structure first |
98 | { | 99 | because some systems might have some extra fields in it |
99 | /* It is safer to bzero the lastlog structure first because some | 100 | (e.g. SGI) */ |
100 | systems might have some extra fields in it (e.g. SGI) */ | 101 | memset(&ll, 0, sizeof(ll)); |
101 | memset(&ll, 0, sizeof(ll)); | 102 | |
102 | 103 | /* Update lastlog. */ | |
103 | /* Update lastlog. */ | 104 | ll.ll_time = time(NULL); |
104 | ll.ll_time = time(NULL); | 105 | strncpy(ll.ll_line, ttyname + 5, sizeof(ll.ll_line)); |
105 | strncpy(ll.ll_line, ttyname + 5, sizeof(ll.ll_line)); | 106 | strncpy(ll.ll_host, host, sizeof(ll.ll_host)); |
106 | strncpy(ll.ll_host, host, sizeof(ll.ll_host)); | 107 | fd = open(lastlog, O_RDWR); |
107 | fd = open(lastlog, O_RDWR); | 108 | if (fd >= 0) { |
108 | if (fd >= 0) | 109 | lseek(fd, (off_t) ((long) uid * sizeof(ll)), SEEK_SET); |
109 | { | 110 | if (write(fd, &ll, sizeof(ll)) != sizeof(ll)) |
110 | lseek(fd, (off_t)((long)uid * sizeof(ll)), SEEK_SET); | 111 | log("Could not write %.100s: %.100s", lastlog, strerror(errno)); |
111 | if (write(fd, &ll, sizeof(ll)) != sizeof(ll)) | 112 | close(fd); |
112 | log("Could not write %.100s: %.100s", lastlog, strerror(errno)); | 113 | } |
113 | close(fd); | ||
114 | } | 114 | } |
115 | } | ||
116 | } | 115 | } |
117 | 116 | ||
118 | void record_logout(int pid, const char *ttyname) | 117 | /* Records that the user has logged out. */ |
118 | |||
119 | void | ||
120 | record_logout(int pid, const char *ttyname) | ||
119 | { | 121 | { |
120 | #ifdef HAVE_LIBUTIL_LOGIN | 122 | #ifdef HAVE_LIBUTIL_LOGIN |
121 | const char *line = ttyname + 5; /* /dev/ttyq8 -> ttyq8 */ | 123 | const char *line = ttyname + 5; /* /dev/ttyq8 -> ttyq8 */ |
122 | if (logout(line)) | 124 | if (logout(line)) |
123 | logwtmp(line, "", ""); | 125 | logwtmp(line, "", ""); |
124 | #else /* HAVE_LIBUTIL_LOGIN */ | 126 | #else /* HAVE_LIBUTIL_LOGIN */ |
125 | record_login(pid, ttyname, "", -1, "", NULL); | 127 | record_login(pid, ttyname, "", -1, "", NULL); |
126 | #endif /* HAVE_LIBUTIL_LOGIN */ | 128 | #endif /* HAVE_LIBUTIL_LOGIN */ |
127 | } | 129 | } |
128 | |||
@@ -1,78 +1,77 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * | |
3 | match.c | 3 | * match.c |
4 | 4 | * | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
6 | 6 | * | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
8 | All rights reserved | 8 | * All rights reserved |
9 | 9 | * | |
10 | Created: Thu Jun 22 01:17:50 1995 ylo | 10 | * Created: Thu Jun 22 01:17:50 1995 ylo |
11 | 11 | * | |
12 | Simple pattern matching, with '*' and '?' as wildcards. | 12 | * Simple pattern matching, with '*' and '?' as wildcards. |
13 | 13 | * | |
14 | */ | 14 | */ |
15 | 15 | ||
16 | #include "includes.h" | 16 | #include "includes.h" |
17 | RCSID("$Id: match.c,v 1.1 1999/10/27 03:42:44 damien Exp $"); | 17 | RCSID("$Id: match.c,v 1.2 1999/11/24 13:26:22 damien Exp $"); |
18 | 18 | ||
19 | #include "ssh.h" | 19 | #include "ssh.h" |
20 | 20 | ||
21 | /* Returns true if the given string matches the pattern (which may contain | 21 | /* Returns true if the given string matches the pattern (which may contain |
22 | ? and * as wildcards), and zero if it does not match. */ | 22 | ? and * as wildcards), and zero if it does not match. */ |
23 | |||
24 | int match_pattern(const char *s, const char *pattern) | ||
25 | { | ||
26 | while (1) | ||
27 | { | ||
28 | /* If at end of pattern, accept if also at end of string. */ | ||
29 | if (!*pattern) | ||
30 | return !*s; | ||
31 | 23 | ||
32 | /* Process '*'. */ | 24 | int |
33 | if (*pattern == '*') | 25 | match_pattern(const char *s, const char *pattern) |
34 | { | 26 | { |
35 | /* Skip the asterisk. */ | 27 | for (;;) { |
36 | pattern++; | 28 | /* If at end of pattern, accept if also at end of string. */ |
29 | if (!*pattern) | ||
30 | return !*s; | ||
37 | 31 | ||
38 | /* If at end of pattern, accept immediately. */ | 32 | /* Process '*'. */ |
39 | if (!*pattern) | 33 | if (*pattern == '*') { |
40 | return 1; | 34 | /* Skip the asterisk. */ |
35 | pattern++; | ||
41 | 36 | ||
42 | /* If next character in pattern is known, optimize. */ | 37 | /* If at end of pattern, accept immediately. */ |
43 | if (*pattern != '?' && *pattern != '*') | 38 | if (!*pattern) |
44 | { | 39 | return 1; |
45 | /* Look instances of the next character in pattern, and try | ||
46 | to match starting from those. */ | ||
47 | for (; *s; s++) | ||
48 | if (*s == *pattern && | ||
49 | match_pattern(s + 1, pattern + 1)) | ||
50 | return 1; | ||
51 | /* Failed. */ | ||
52 | return 0; | ||
53 | } | ||
54 | 40 | ||
55 | /* Move ahead one character at a time and try to match at each | 41 | /* If next character in pattern is known, optimize. */ |
56 | position. */ | 42 | if (*pattern != '?' && *pattern != '*') { |
57 | for (; *s; s++) | 43 | /* Look instances of the next character in |
58 | if (match_pattern(s, pattern)) | 44 | pattern, and try to match starting from |
59 | return 1; | 45 | those. */ |
60 | /* Failed. */ | 46 | for (; *s; s++) |
61 | return 0; | 47 | if (*s == *pattern && |
62 | } | 48 | match_pattern(s + 1, pattern + 1)) |
49 | return 1; | ||
50 | /* Failed. */ | ||
51 | return 0; | ||
52 | } | ||
53 | /* Move ahead one character at a time and try to | ||
54 | match at each position. */ | ||
55 | for (; *s; s++) | ||
56 | if (match_pattern(s, pattern)) | ||
57 | return 1; | ||
58 | /* Failed. */ | ||
59 | return 0; | ||
60 | } | ||
61 | /* There must be at least one more character in the | ||
62 | string. If we are at the end, fail. */ | ||
63 | if (!*s) | ||
64 | return 0; | ||
63 | 65 | ||
64 | /* There must be at least one more character in the string. If we are | 66 | /* Check if the next character of the string is |
65 | at the end, fail. */ | 67 | acceptable. */ |
66 | if (!*s) | 68 | if (*pattern != '?' && *pattern != *s) |
67 | return 0; | 69 | return 0; |
68 | 70 | ||
69 | /* Check if the next character of the string is acceptable. */ | 71 | /* Move to the next character, both in string and in |
70 | if (*pattern != '?' && *pattern != *s) | 72 | pattern. */ |
71 | return 0; | 73 | s++; |
72 | 74 | pattern++; | |
73 | /* Move to the next character, both in string and in pattern. */ | 75 | } |
74 | s++; | 76 | /* NOTREACHED */ |
75 | pattern++; | ||
76 | } | ||
77 | /*NOTREACHED*/ | ||
78 | } | 77 | } |
@@ -1,21 +1,24 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * | |
3 | mpaux.c | 3 | * mpaux.c |
4 | 4 | * | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
6 | 6 | * | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
8 | All rights reserved | 8 | * All rights reserved |
9 | 9 | * | |
10 | Created: Sun Jul 16 04:29:30 1995 ylo | 10 | * Created: Sun Jul 16 04:29:30 1995 ylo |
11 | 11 | * | |
12 | This file contains various auxiliary functions related to multiple | 12 | * This file contains various auxiliary functions related to multiple |
13 | precision integers. | 13 | * precision integers. |
14 | 14 | * | |
15 | */ | 15 | */ |
16 | 16 | ||
17 | #include "includes.h" | 17 | #include "includes.h" |
18 | RCSID("$Id: mpaux.c,v 1.6 1999/11/16 02:37:16 damien Exp $"); | 18 | RCSID("$Id: mpaux.c,v 1.7 1999/11/24 13:26:22 damien Exp $"); |
19 | |||
20 | #include "getput.h" | ||
21 | #include "xmalloc.h" | ||
19 | 22 | ||
20 | #ifdef HAVE_OPENSSL | 23 | #ifdef HAVE_OPENSSL |
21 | #include <openssl/bn.h> | 24 | #include <openssl/bn.h> |
@@ -26,29 +29,24 @@ RCSID("$Id: mpaux.c,v 1.6 1999/11/16 02:37:16 damien Exp $"); | |||
26 | #include <ssl/md5.h> | 29 | #include <ssl/md5.h> |
27 | #endif | 30 | #endif |
28 | 31 | ||
29 | #include "getput.h" | ||
30 | #include "xmalloc.h" | ||
31 | |||
32 | |||
33 | void | 32 | void |
34 | compute_session_id(unsigned char session_id[16], | 33 | compute_session_id(unsigned char session_id[16], |
35 | unsigned char cookie[8], | 34 | unsigned char cookie[8], |
36 | BIGNUM *host_key_n, | 35 | BIGNUM* host_key_n, |
37 | BIGNUM *session_key_n) | 36 | BIGNUM* session_key_n) |
38 | { | 37 | { |
39 | unsigned int host_key_bits = BN_num_bits(host_key_n); | 38 | unsigned int host_key_bits = BN_num_bits(host_key_n); |
40 | unsigned int session_key_bits = BN_num_bits(session_key_n); | 39 | unsigned int session_key_bits = BN_num_bits(session_key_n); |
41 | unsigned int bytes = (host_key_bits + 7) / 8 + (session_key_bits + 7) / 8 + 8; | 40 | unsigned int bytes = (host_key_bits + 7) / 8 + (session_key_bits + 7) / 8 + 8; |
42 | unsigned char *buf = xmalloc(bytes); | 41 | unsigned char *buf = xmalloc(bytes); |
43 | MD5_CTX md; | 42 | MD5_CTX md; |
44 | 43 | ||
45 | BN_bn2bin(host_key_n, buf); | 44 | BN_bn2bin(host_key_n, buf); |
46 | BN_bn2bin(session_key_n, buf + (host_key_bits + 7 ) / 8); | 45 | BN_bn2bin(session_key_n, buf + (host_key_bits + 7) / 8); |
47 | memcpy(buf + (host_key_bits + 7) / 8 + (session_key_bits + 7) / 8, | 46 | memcpy(buf + (host_key_bits + 7) / 8 + (session_key_bits + 7) / 8, cookie, 8); |
48 | cookie, 8); | 47 | MD5_Init(&md); |
49 | MD5_Init(&md); | 48 | MD5_Update(&md, buf, bytes); |
50 | MD5_Update(&md, buf, bytes); | 49 | MD5_Final(session_id, &md); |
51 | MD5_Final(session_id, &md); | 50 | memset(buf, 0, bytes); |
52 | memset(buf, 0, bytes); | 51 | xfree(buf); |
53 | xfree(buf); | ||
54 | } | 52 | } |
@@ -1,20 +1,19 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * | |
3 | mpaux.h | 3 | * mpaux.h |
4 | 4 | * | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
6 | 6 | * | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
8 | All rights reserved | 8 | * All rights reserved |
9 | 9 | * | |
10 | Created: Sun Jul 16 04:29:30 1995 ylo | 10 | * Created: Sun Jul 16 04:29:30 1995 ylo |
11 | 11 | * | |
12 | This file contains various auxiliary functions related to multiple | 12 | * This file contains various auxiliary functions related to multiple |
13 | precision integers. | 13 | * precision integers. |
14 | 14 | */ | |
15 | */ | 15 | |
16 | 16 | /* RCSID("$Id: mpaux.h,v 1.3 1999/11/24 13:26:22 damien Exp $"); */ | |
17 | /* RCSID("$Id: mpaux.h,v 1.2 1999/11/16 02:37:16 damien Exp $"); */ | ||
18 | 17 | ||
19 | #ifndef MPAUX_H | 18 | #ifndef MPAUX_H |
20 | #define MPAUX_H | 19 | #define MPAUX_H |
@@ -22,9 +21,10 @@ precision integers. | |||
22 | /* Computes a 16-byte session id in the global variable session_id. | 21 | /* Computes a 16-byte session id in the global variable session_id. |
23 | The session id is computed by concatenating the linearized, msb | 22 | The session id is computed by concatenating the linearized, msb |
24 | first representations of host_key_n, session_key_n, and the cookie. */ | 23 | first representations of host_key_n, session_key_n, and the cookie. */ |
25 | void compute_session_id(unsigned char session_id[16], | 24 | void |
26 | unsigned char cookie[8], | 25 | compute_session_id(unsigned char session_id[16], |
27 | BIGNUM *host_key_n, | 26 | unsigned char cookie[8], |
28 | BIGNUM *session_key_n); | 27 | BIGNUM * host_key_n, |
28 | BIGNUM * session_key_n); | ||
29 | 29 | ||
30 | #endif /* MPAUX_H */ | 30 | #endif /* MPAUX_H */ |
@@ -1,5 +1,5 @@ | |||
1 | #include "includes.h" | 1 | #include "includes.h" |
2 | RCSID("$Id: nchan.c,v 1.1 1999/10/27 03:42:44 damien Exp $"); | 2 | RCSID("$Id: nchan.c,v 1.2 1999/11/24 13:26:22 damien Exp $"); |
3 | 3 | ||
4 | #include "ssh.h" | 4 | #include "ssh.h" |
5 | 5 | ||
@@ -15,122 +15,131 @@ static void chan_shutdown_read(Channel *c); | |||
15 | static void chan_delele_if_full_closed(Channel *c); | 15 | static void chan_delele_if_full_closed(Channel *c); |
16 | 16 | ||
17 | /* | 17 | /* |
18 | * EVENTS: update channel input/output states | 18 | * EVENTS update channel input/output states execute ACTIONS |
19 | * execute ACTIONS | ||
20 | */ | 19 | */ |
20 | |||
21 | /* events concerning the INPUT from socket for channel (istate) */ | 21 | /* events concerning the INPUT from socket for channel (istate) */ |
22 | void | 22 | void |
23 | chan_rcvd_oclose(Channel *c){ | 23 | chan_rcvd_oclose(Channel *c) |
24 | switch(c->istate){ | 24 | { |
25 | switch (c->istate) { | ||
25 | case CHAN_INPUT_WAIT_OCLOSE: | 26 | case CHAN_INPUT_WAIT_OCLOSE: |
26 | debug("channel %d: INPUT_WAIT_OCLOSE -> INPUT_CLOSED [rcvd OCLOSE]", c->self); | 27 | debug("channel %d: INPUT_WAIT_OCLOSE -> INPUT_CLOSED [rcvd OCLOSE]", c->self); |
27 | c->istate=CHAN_INPUT_CLOSED; | 28 | c->istate = CHAN_INPUT_CLOSED; |
28 | chan_delele_if_full_closed(c); | 29 | chan_delele_if_full_closed(c); |
29 | break; | 30 | break; |
30 | case CHAN_INPUT_OPEN: | 31 | case CHAN_INPUT_OPEN: |
31 | debug("channel %d: INPUT_OPEN -> INPUT_CLOSED [rvcd OCLOSE, send IEOF]", c->self); | 32 | debug("channel %d: INPUT_OPEN -> INPUT_CLOSED [rvcd OCLOSE, send IEOF]", c->self); |
32 | chan_shutdown_read(c); | 33 | chan_shutdown_read(c); |
33 | chan_send_ieof(c); | 34 | chan_send_ieof(c); |
34 | c->istate=CHAN_INPUT_CLOSED; | 35 | c->istate = CHAN_INPUT_CLOSED; |
35 | chan_delele_if_full_closed(c); | 36 | chan_delele_if_full_closed(c); |
36 | break; | 37 | break; |
37 | default: | 38 | default: |
38 | debug("protocol error: chan_rcvd_oclose %d for istate %d",c->self,c->istate); | 39 | debug("protocol error: chan_rcvd_oclose %d for istate %d", c->self, c->istate); |
39 | break; | 40 | break; |
40 | } | 41 | } |
41 | } | 42 | } |
42 | void | 43 | void |
43 | chan_read_failed(Channel *c){ | 44 | chan_read_failed(Channel *c) |
44 | switch(c->istate){ | 45 | { |
46 | switch (c->istate) { | ||
45 | case CHAN_INPUT_OPEN: | 47 | case CHAN_INPUT_OPEN: |
46 | debug("channel %d: INPUT_OPEN -> INPUT_WAIT_DRAIN [read failed]", c->self); | 48 | debug("channel %d: INPUT_OPEN -> INPUT_WAIT_DRAIN [read failed]", c->self); |
47 | chan_shutdown_read(c); | 49 | chan_shutdown_read(c); |
48 | c->istate=CHAN_INPUT_WAIT_DRAIN; | 50 | c->istate = CHAN_INPUT_WAIT_DRAIN; |
49 | break; | 51 | break; |
50 | default: | 52 | default: |
51 | debug("internal error: we do not read, but chan_read_failed %d for istate %d", | 53 | debug("internal error: we do not read, but chan_read_failed %d for istate %d", |
52 | c->self,c->istate); | 54 | c->self, c->istate); |
53 | break; | 55 | break; |
54 | } | 56 | } |
55 | } | 57 | } |
56 | void | 58 | void |
57 | chan_ibuf_empty(Channel *c){ | 59 | chan_ibuf_empty(Channel *c) |
58 | if(buffer_len(&c->input)){ | 60 | { |
59 | debug("internal error: chan_ibuf_empty %d for non empty buffer",c->self); | 61 | if (buffer_len(&c->input)) { |
62 | debug("internal error: chan_ibuf_empty %d for non empty buffer", c->self); | ||
60 | return; | 63 | return; |
61 | } | 64 | } |
62 | switch(c->istate){ | 65 | switch (c->istate) { |
63 | case CHAN_INPUT_WAIT_DRAIN: | 66 | case CHAN_INPUT_WAIT_DRAIN: |
64 | debug("channel %d: INPUT_WAIT_DRAIN -> INPUT_WAIT_OCLOSE [inbuf empty, send IEOF]", c->self); | 67 | debug("channel %d: INPUT_WAIT_DRAIN -> INPUT_WAIT_OCLOSE [inbuf empty, send IEOF]", c->self); |
65 | chan_send_ieof(c); | 68 | chan_send_ieof(c); |
66 | c->istate=CHAN_INPUT_WAIT_OCLOSE; | 69 | c->istate = CHAN_INPUT_WAIT_OCLOSE; |
67 | break; | 70 | break; |
68 | default: | 71 | default: |
69 | debug("internal error: chan_ibuf_empty %d for istate %d",c->self,c->istate); | 72 | debug("internal error: chan_ibuf_empty %d for istate %d", c->self, c->istate); |
70 | break; | 73 | break; |
71 | } | 74 | } |
72 | } | 75 | } |
76 | |||
73 | /* events concerning the OUTPUT from channel for socket (ostate) */ | 77 | /* events concerning the OUTPUT from channel for socket (ostate) */ |
74 | void | 78 | void |
75 | chan_rcvd_ieof(Channel *c){ | 79 | chan_rcvd_ieof(Channel *c) |
76 | switch(c->ostate){ | 80 | { |
81 | switch (c->ostate) { | ||
77 | case CHAN_OUTPUT_OPEN: | 82 | case CHAN_OUTPUT_OPEN: |
78 | debug("channel %d: OUTPUT_OPEN -> OUTPUT_WAIT_DRAIN [rvcd IEOF]", c->self); | 83 | debug("channel %d: OUTPUT_OPEN -> OUTPUT_WAIT_DRAIN [rvcd IEOF]", c->self); |
79 | c->ostate=CHAN_OUTPUT_WAIT_DRAIN; | 84 | c->ostate = CHAN_OUTPUT_WAIT_DRAIN; |
80 | break; | 85 | break; |
81 | case CHAN_OUTPUT_WAIT_IEOF: | 86 | case CHAN_OUTPUT_WAIT_IEOF: |
82 | debug("channel %d: OUTPUT_WAIT_IEOF -> OUTPUT_CLOSED [rvcd IEOF]", c->self); | 87 | debug("channel %d: OUTPUT_WAIT_IEOF -> OUTPUT_CLOSED [rvcd IEOF]", c->self); |
83 | c->ostate=CHAN_OUTPUT_CLOSED; | 88 | c->ostate = CHAN_OUTPUT_CLOSED; |
84 | chan_delele_if_full_closed(c); | 89 | chan_delele_if_full_closed(c); |
85 | break; | 90 | break; |
86 | default: | 91 | default: |
87 | debug("protocol error: chan_rcvd_ieof %d for ostate %d", c->self,c->ostate); | 92 | debug("protocol error: chan_rcvd_ieof %d for ostate %d", c->self, c->ostate); |
88 | break; | 93 | break; |
89 | } | 94 | } |
90 | } | 95 | } |
91 | void | 96 | void |
92 | chan_write_failed(Channel *c){ | 97 | chan_write_failed(Channel *c) |
93 | switch(c->ostate){ | 98 | { |
99 | switch (c->ostate) { | ||
94 | case CHAN_OUTPUT_OPEN: | 100 | case CHAN_OUTPUT_OPEN: |
95 | debug("channel %d: OUTPUT_OPEN -> OUTPUT_WAIT_IEOF [write failed]", c->self); | 101 | debug("channel %d: OUTPUT_OPEN -> OUTPUT_WAIT_IEOF [write failed]", c->self); |
96 | chan_send_oclose(c); | 102 | chan_send_oclose(c); |
97 | c->ostate=CHAN_OUTPUT_WAIT_IEOF; | 103 | c->ostate = CHAN_OUTPUT_WAIT_IEOF; |
98 | break; | 104 | break; |
99 | case CHAN_OUTPUT_WAIT_DRAIN: | 105 | case CHAN_OUTPUT_WAIT_DRAIN: |
100 | debug("channel %d: OUTPUT_WAIT_DRAIN -> OUTPUT_CLOSED [write failed]", c->self); | 106 | debug("channel %d: OUTPUT_WAIT_DRAIN -> OUTPUT_CLOSED [write failed]", c->self); |
101 | chan_send_oclose(c); | 107 | chan_send_oclose(c); |
102 | c->ostate=CHAN_OUTPUT_CLOSED; | 108 | c->ostate = CHAN_OUTPUT_CLOSED; |
103 | chan_delele_if_full_closed(c); | 109 | chan_delele_if_full_closed(c); |
104 | break; | 110 | break; |
105 | default: | 111 | default: |
106 | debug("internal error: chan_write_failed %d for ostate %d",c->self,c->ostate); | 112 | debug("internal error: chan_write_failed %d for ostate %d", c->self, c->ostate); |
107 | break; | 113 | break; |
108 | } | 114 | } |
109 | } | 115 | } |
110 | void | 116 | void |
111 | chan_obuf_empty(Channel *c){ | 117 | chan_obuf_empty(Channel *c) |
112 | if(buffer_len(&c->output)){ | 118 | { |
113 | debug("internal error: chan_obuf_empty %d for non empty buffer",c->self); | 119 | if (buffer_len(&c->output)) { |
120 | debug("internal error: chan_obuf_empty %d for non empty buffer", c->self); | ||
114 | return; | 121 | return; |
115 | } | 122 | } |
116 | switch(c->ostate){ | 123 | switch (c->ostate) { |
117 | case CHAN_OUTPUT_WAIT_DRAIN: | 124 | case CHAN_OUTPUT_WAIT_DRAIN: |
118 | debug("channel %d: OUTPUT_WAIT_DRAIN -> OUTPUT_CLOSED [obuf empty, send OCLOSE]", c->self); | 125 | debug("channel %d: OUTPUT_WAIT_DRAIN -> OUTPUT_CLOSED [obuf empty, send OCLOSE]", c->self); |
119 | chan_send_oclose(c); | 126 | chan_send_oclose(c); |
120 | c->ostate=CHAN_OUTPUT_CLOSED; | 127 | c->ostate = CHAN_OUTPUT_CLOSED; |
121 | chan_delele_if_full_closed(c); | 128 | chan_delele_if_full_closed(c); |
122 | break; | 129 | break; |
123 | default: | 130 | default: |
124 | debug("internal error: chan_obuf_empty %d for ostate %d",c->self,c->ostate); | 131 | debug("internal error: chan_obuf_empty %d for ostate %d", c->self, c->ostate); |
125 | break; | 132 | break; |
126 | } | 133 | } |
127 | } | 134 | } |
135 | |||
128 | /* | 136 | /* |
129 | * ACTIONS: should never update c->istate or c->ostate | 137 | * ACTIONS: should never update the channel states: c->istate or c->ostate |
130 | */ | 138 | */ |
131 | static void | 139 | static void |
132 | chan_send_ieof(Channel *c){ | 140 | chan_send_ieof(Channel *c) |
133 | switch(c->istate){ | 141 | { |
142 | switch (c->istate) { | ||
134 | case CHAN_INPUT_OPEN: | 143 | case CHAN_INPUT_OPEN: |
135 | case CHAN_INPUT_WAIT_DRAIN: | 144 | case CHAN_INPUT_WAIT_DRAIN: |
136 | packet_start(SSH_MSG_CHANNEL_INPUT_EOF); | 145 | packet_start(SSH_MSG_CHANNEL_INPUT_EOF); |
@@ -138,13 +147,14 @@ chan_send_ieof(Channel *c){ | |||
138 | packet_send(); | 147 | packet_send(); |
139 | break; | 148 | break; |
140 | default: | 149 | default: |
141 | debug("internal error: channel %d: cannot send IEOF for istate %d",c->self,c->istate); | 150 | debug("internal error: channel %d: cannot send IEOF for istate %d", c->self, c->istate); |
142 | break; | 151 | break; |
143 | } | 152 | } |
144 | } | 153 | } |
145 | static void | 154 | static void |
146 | chan_send_oclose(Channel *c){ | 155 | chan_send_oclose(Channel *c) |
147 | switch(c->ostate){ | 156 | { |
157 | switch (c->ostate) { | ||
148 | case CHAN_OUTPUT_OPEN: | 158 | case CHAN_OUTPUT_OPEN: |
149 | case CHAN_OUTPUT_WAIT_DRAIN: | 159 | case CHAN_OUTPUT_WAIT_DRAIN: |
150 | chan_shutdown_write(c); | 160 | chan_shutdown_write(c); |
@@ -154,34 +164,39 @@ chan_send_oclose(Channel *c){ | |||
154 | packet_send(); | 164 | packet_send(); |
155 | break; | 165 | break; |
156 | default: | 166 | default: |
157 | debug("internal error: channel %d: cannot send OCLOSE for ostate %d",c->self,c->istate); | 167 | debug("internal error: channel %d: cannot send OCLOSE for ostate %d", c->self, c->istate); |
158 | break; | 168 | break; |
159 | } | 169 | } |
160 | } | 170 | } |
171 | |||
161 | /* helper */ | 172 | /* helper */ |
162 | static void | 173 | static void |
163 | chan_shutdown_write(Channel *c){ | 174 | chan_shutdown_write(Channel *c) |
175 | { | ||
164 | debug("channel %d: shutdown_write", c->self); | 176 | debug("channel %d: shutdown_write", c->self); |
165 | if(shutdown(c->sock, SHUT_WR)<0) | 177 | if (shutdown(c->sock, SHUT_WR) < 0) |
166 | error("chan_shutdown_write failed for #%d/fd%d: %.100s", | 178 | error("chan_shutdown_write failed for #%d/fd%d: %.100s", |
167 | c->self, c->sock, strerror(errno)); | 179 | c->self, c->sock, strerror(errno)); |
168 | } | 180 | } |
169 | static void | 181 | static void |
170 | chan_shutdown_read(Channel *c){ | 182 | chan_shutdown_read(Channel *c) |
183 | { | ||
171 | debug("channel %d: shutdown_read", c->self); | 184 | debug("channel %d: shutdown_read", c->self); |
172 | if(shutdown(c->sock, SHUT_RD)<0) | 185 | if (shutdown(c->sock, SHUT_RD) < 0) |
173 | error("chan_shutdown_read failed for #%d/fd%d: %.100s", | 186 | error("chan_shutdown_read failed for #%d/fd%d: %.100s", |
174 | c->self, c->sock, strerror(errno)); | 187 | c->self, c->sock, strerror(errno)); |
175 | } | 188 | } |
176 | static void | 189 | static void |
177 | chan_delele_if_full_closed(Channel *c){ | 190 | chan_delele_if_full_closed(Channel *c) |
178 | if(c->istate==CHAN_INPUT_CLOSED && c->ostate==CHAN_OUTPUT_CLOSED){ | 191 | { |
192 | if (c->istate == CHAN_INPUT_CLOSED && c->ostate == CHAN_OUTPUT_CLOSED) { | ||
179 | debug("channel %d: closing", c->self); | 193 | debug("channel %d: closing", c->self); |
180 | channel_free(c->self); | 194 | channel_free(c->self); |
181 | } | 195 | } |
182 | } | 196 | } |
183 | void | 197 | void |
184 | chan_init_iostates(Channel *c){ | 198 | chan_init_iostates(Channel *c) |
185 | c->ostate=CHAN_OUTPUT_OPEN; | 199 | { |
186 | c->istate=CHAN_INPUT_OPEN; | 200 | c->ostate = CHAN_OUTPUT_OPEN; |
201 | c->istate = CHAN_INPUT_OPEN; | ||
187 | } | 202 | } |
@@ -1,4 +1,4 @@ | |||
1 | /* RCSID("$Id: nchan.h,v 1.1 1999/10/27 03:42:44 damien Exp $"); */ | 1 | /* RCSID("$Id: nchan.h,v 1.2 1999/11/24 13:26:22 damien Exp $"); */ |
2 | 2 | ||
3 | #ifndef NCHAN_H | 3 | #ifndef NCHAN_H |
4 | #define NCHAN_H | 4 | #define NCHAN_H |
@@ -7,24 +7,24 @@ | |||
7 | * SSH Protocol 1.5 aka New Channel Protocol | 7 | * SSH Protocol 1.5 aka New Channel Protocol |
8 | * Thanks to Martina, Axel and everyone who left Erlangen, leaving me bored. | 8 | * Thanks to Martina, Axel and everyone who left Erlangen, leaving me bored. |
9 | * Written by Markus Friedl in October 1999 | 9 | * Written by Markus Friedl in October 1999 |
10 | * | 10 | * |
11 | * Protocol versions 1.3 and 1.5 differ in the handshake protocol used for the | 11 | * Protocol versions 1.3 and 1.5 differ in the handshake protocol used for the |
12 | * tear down of channels: | 12 | * tear down of channels: |
13 | * | 13 | * |
14 | * 1.3: strict request-ack-protocol: | 14 | * 1.3: strict request-ack-protocol: |
15 | * CLOSE -> | 15 | * CLOSE -> |
16 | * <- CLOSE_CONFIRM | 16 | * <- CLOSE_CONFIRM |
17 | * | 17 | * |
18 | * 1.5: uses variations of: | 18 | * 1.5: uses variations of: |
19 | * IEOF -> | 19 | * IEOF -> |
20 | * <- OCLOSE | 20 | * <- OCLOSE |
21 | * <- IEOF | 21 | * <- IEOF |
22 | * OCLOSE -> | 22 | * OCLOSE -> |
23 | * i.e. both sides have to close the channel | 23 | * i.e. both sides have to close the channel |
24 | * | 24 | * |
25 | * See the debugging output from 'ssh -v' and 'sshd -d' of | 25 | * See the debugging output from 'ssh -v' and 'sshd -d' of |
26 | * ssh-1.2.27 as an example. | 26 | * ssh-1.2.27 as an example. |
27 | * | 27 | * |
28 | */ | 28 | */ |
29 | 29 | ||
30 | /* ssh-proto-1.5 overloads prot-1.3-message-types */ | 30 | /* ssh-proto-1.5 overloads prot-1.3-message-types */ |
@@ -44,14 +44,14 @@ | |||
44 | #define CHAN_OUTPUT_CLOSED 0x80 | 44 | #define CHAN_OUTPUT_CLOSED 0x80 |
45 | 45 | ||
46 | /* EVENTS for the input state */ | 46 | /* EVENTS for the input state */ |
47 | void chan_rcvd_oclose(Channel *c); | 47 | void chan_rcvd_oclose(Channel * c); |
48 | void chan_read_failed(Channel *c); | 48 | void chan_read_failed(Channel * c); |
49 | void chan_ibuf_empty(Channel *c); | 49 | void chan_ibuf_empty(Channel * c); |
50 | 50 | ||
51 | /* EVENTS for the output state */ | 51 | /* EVENTS for the output state */ |
52 | void chan_rcvd_ieof(Channel *c); | 52 | void chan_rcvd_ieof(Channel * c); |
53 | void chan_write_failed(Channel *c); | 53 | void chan_write_failed(Channel * c); |
54 | void chan_obuf_empty(Channel *c); | 54 | void chan_obuf_empty(Channel * c); |
55 | 55 | ||
56 | void chan_init_iostates(Channel *c); | 56 | void chan_init_iostates(Channel * c); |
57 | #endif | 57 | #endif |
@@ -1,21 +1,21 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * | |
3 | packet.c | 3 | * packet.c |
4 | 4 | * | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
6 | 6 | * | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
8 | All rights reserved | 8 | * All rights reserved |
9 | 9 | * | |
10 | Created: Sat Mar 18 02:40:40 1995 ylo | 10 | * Created: Sat Mar 18 02:40:40 1995 ylo |
11 | 11 | * | |
12 | This file contains code implementing the packet protocol and communication | 12 | * This file contains code implementing the packet protocol and communication |
13 | with the other side. This same code is used both on client and server side. | 13 | * with the other side. This same code is used both on client and server side. |
14 | 14 | * | |
15 | */ | 15 | */ |
16 | 16 | ||
17 | #include "includes.h" | 17 | #include "includes.h" |
18 | RCSID("$Id: packet.c,v 1.4 1999/11/21 02:23:53 damien Exp $"); | 18 | RCSID("$Id: packet.c,v 1.5 1999/11/24 13:26:22 damien Exp $"); |
19 | 19 | ||
20 | #include "xmalloc.h" | 20 | #include "xmalloc.h" |
21 | #include "buffer.h" | 21 | #include "buffer.h" |
@@ -45,7 +45,8 @@ static unsigned int remote_protocol_flags = 0; | |||
45 | 45 | ||
46 | /* Encryption context for receiving data. This is only used for decryption. */ | 46 | /* Encryption context for receiving data. This is only used for decryption. */ |
47 | static CipherContext receive_context; | 47 | static CipherContext receive_context; |
48 | /* Encryption coontext for sending data. This is only used for encryption. */ | 48 | |
49 | /* Encryption context for sending data. This is only used for encryption. */ | ||
49 | static CipherContext send_context; | 50 | static CipherContext send_context; |
50 | 51 | ||
51 | /* Buffer for raw input data from the socket. */ | 52 | /* Buffer for raw input data from the socket. */ |
@@ -81,22 +82,20 @@ static int interactive_mode = 0; | |||
81 | void | 82 | void |
82 | packet_set_connection(int fd_in, int fd_out) | 83 | packet_set_connection(int fd_in, int fd_out) |
83 | { | 84 | { |
84 | connection_in = fd_in; | 85 | connection_in = fd_in; |
85 | connection_out = fd_out; | 86 | connection_out = fd_out; |
86 | cipher_type = SSH_CIPHER_NONE; | 87 | cipher_type = SSH_CIPHER_NONE; |
87 | cipher_set_key(&send_context, SSH_CIPHER_NONE, (unsigned char *)"", 0, 1); | 88 | cipher_set_key(&send_context, SSH_CIPHER_NONE, (unsigned char *) "", 0, 1); |
88 | cipher_set_key(&receive_context, SSH_CIPHER_NONE, (unsigned char *)"", 0, 0); | 89 | cipher_set_key(&receive_context, SSH_CIPHER_NONE, (unsigned char *) "", 0, 0); |
89 | if (!initialized) | 90 | if (!initialized) { |
90 | { | 91 | initialized = 1; |
91 | initialized = 1; | 92 | buffer_init(&input); |
92 | buffer_init(&input); | 93 | buffer_init(&output); |
93 | buffer_init(&output); | 94 | buffer_init(&outgoing_packet); |
94 | buffer_init(&outgoing_packet); | 95 | buffer_init(&incoming_packet); |
95 | buffer_init(&incoming_packet); | 96 | } |
96 | } | 97 | /* Kludge: arrange the close function to be called from fatal(). */ |
97 | 98 | fatal_add_cleanup((void (*) (void *)) packet_close, NULL); | |
98 | /* Kludge: arrange the close function to be called from fatal(). */ | ||
99 | fatal_add_cleanup((void (*)(void *))packet_close, NULL); | ||
100 | } | 99 | } |
101 | 100 | ||
102 | /* Sets the connection into non-blocking mode. */ | 101 | /* Sets the connection into non-blocking mode. */ |
@@ -104,15 +103,14 @@ packet_set_connection(int fd_in, int fd_out) | |||
104 | void | 103 | void |
105 | packet_set_nonblocking() | 104 | packet_set_nonblocking() |
106 | { | 105 | { |
107 | /* Set the socket into non-blocking mode. */ | 106 | /* Set the socket into non-blocking mode. */ |
108 | if (fcntl(connection_in, F_SETFL, O_NONBLOCK) < 0) | 107 | if (fcntl(connection_in, F_SETFL, O_NONBLOCK) < 0) |
109 | error("fcntl O_NONBLOCK: %.100s", strerror(errno)); | 108 | error("fcntl O_NONBLOCK: %.100s", strerror(errno)); |
110 | 109 | ||
111 | if (connection_out != connection_in) | 110 | if (connection_out != connection_in) { |
112 | { | 111 | if (fcntl(connection_out, F_SETFL, O_NONBLOCK) < 0) |
113 | if (fcntl(connection_out, F_SETFL, O_NONBLOCK) < 0) | 112 | error("fcntl O_NONBLOCK: %.100s", strerror(errno)); |
114 | error("fcntl O_NONBLOCK: %.100s", strerror(errno)); | 113 | } |
115 | } | ||
116 | } | 114 | } |
117 | 115 | ||
118 | /* Returns the socket used for reading. */ | 116 | /* Returns the socket used for reading. */ |
@@ -120,7 +118,7 @@ packet_set_nonblocking() | |||
120 | int | 118 | int |
121 | packet_get_connection_in() | 119 | packet_get_connection_in() |
122 | { | 120 | { |
123 | return connection_in; | 121 | return connection_in; |
124 | } | 122 | } |
125 | 123 | ||
126 | /* Returns the descriptor used for writing. */ | 124 | /* Returns the descriptor used for writing. */ |
@@ -128,7 +126,7 @@ packet_get_connection_in() | |||
128 | int | 126 | int |
129 | packet_get_connection_out() | 127 | packet_get_connection_out() |
130 | { | 128 | { |
131 | return connection_out; | 129 | return connection_out; |
132 | } | 130 | } |
133 | 131 | ||
134 | /* Closes the connection and clears and frees internal data structures. */ | 132 | /* Closes the connection and clears and frees internal data structures. */ |
@@ -136,28 +134,24 @@ packet_get_connection_out() | |||
136 | void | 134 | void |
137 | packet_close() | 135 | packet_close() |
138 | { | 136 | { |
139 | if (!initialized) | 137 | if (!initialized) |
140 | return; | 138 | return; |
141 | initialized = 0; | 139 | initialized = 0; |
142 | if (connection_in == connection_out) | 140 | if (connection_in == connection_out) { |
143 | { | 141 | shutdown(connection_out, SHUT_RDWR); |
144 | shutdown(connection_out, SHUT_RDWR); | 142 | close(connection_out); |
145 | close(connection_out); | 143 | } else { |
146 | } | 144 | close(connection_in); |
147 | else | 145 | close(connection_out); |
148 | { | 146 | } |
149 | close(connection_in); | 147 | buffer_free(&input); |
150 | close(connection_out); | 148 | buffer_free(&output); |
151 | } | 149 | buffer_free(&outgoing_packet); |
152 | buffer_free(&input); | 150 | buffer_free(&incoming_packet); |
153 | buffer_free(&output); | 151 | if (packet_compression) { |
154 | buffer_free(&outgoing_packet); | 152 | buffer_free(&compression_buffer); |
155 | buffer_free(&incoming_packet); | 153 | buffer_compress_uninit(); |
156 | if (packet_compression) | 154 | } |
157 | { | ||
158 | buffer_free(&compression_buffer); | ||
159 | buffer_compress_uninit(); | ||
160 | } | ||
161 | } | 155 | } |
162 | 156 | ||
163 | /* Sets remote side protocol flags. */ | 157 | /* Sets remote side protocol flags. */ |
@@ -165,8 +159,8 @@ packet_close() | |||
165 | void | 159 | void |
166 | packet_set_protocol_flags(unsigned int protocol_flags) | 160 | packet_set_protocol_flags(unsigned int protocol_flags) |
167 | { | 161 | { |
168 | remote_protocol_flags = protocol_flags; | 162 | remote_protocol_flags = protocol_flags; |
169 | channel_set_options((protocol_flags & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) != 0); | 163 | channel_set_options((protocol_flags & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) != 0); |
170 | } | 164 | } |
171 | 165 | ||
172 | /* Returns the remote protocol flags set earlier by the above function. */ | 166 | /* Returns the remote protocol flags set earlier by the above function. */ |
@@ -174,63 +168,60 @@ packet_set_protocol_flags(unsigned int protocol_flags) | |||
174 | unsigned int | 168 | unsigned int |
175 | packet_get_protocol_flags() | 169 | packet_get_protocol_flags() |
176 | { | 170 | { |
177 | return remote_protocol_flags; | 171 | return remote_protocol_flags; |
178 | } | 172 | } |
179 | 173 | ||
180 | /* Starts packet compression from the next packet on in both directions. | 174 | /* Starts packet compression from the next packet on in both directions. |
181 | Level is compression level 1 (fastest) - 9 (slow, best) as in gzip. */ | 175 | Level is compression level 1 (fastest) - 9 (slow, best) as in gzip. */ |
182 | 176 | ||
183 | void | 177 | void |
184 | packet_start_compression(int level) | 178 | packet_start_compression(int level) |
185 | { | 179 | { |
186 | if (packet_compression) | 180 | if (packet_compression) |
187 | fatal("Compression already enabled."); | 181 | fatal("Compression already enabled."); |
188 | packet_compression = 1; | 182 | packet_compression = 1; |
189 | buffer_init(&compression_buffer); | 183 | buffer_init(&compression_buffer); |
190 | buffer_compress_init(level); | 184 | buffer_compress_init(level); |
191 | } | 185 | } |
192 | 186 | ||
193 | /* Encrypts the given number of bytes, copying from src to dest. | 187 | /* Encrypts the given number of bytes, copying from src to dest. |
194 | bytes is known to be a multiple of 8. */ | 188 | bytes is known to be a multiple of 8. */ |
195 | 189 | ||
196 | void | 190 | void |
197 | packet_encrypt(CipherContext *cc, void *dest, void *src, | 191 | packet_encrypt(CipherContext * cc, void *dest, void *src, |
198 | unsigned int bytes) | 192 | unsigned int bytes) |
199 | { | 193 | { |
200 | cipher_encrypt(cc, dest, src, bytes); | 194 | cipher_encrypt(cc, dest, src, bytes); |
201 | } | 195 | } |
202 | 196 | ||
203 | /* Decrypts the given number of bytes, copying from src to dest. | 197 | /* Decrypts the given number of bytes, copying from src to dest. |
204 | bytes is known to be a multiple of 8. */ | 198 | bytes is known to be a multiple of 8. */ |
205 | 199 | ||
206 | void | 200 | void |
207 | packet_decrypt(CipherContext *cc, void *dest, void *src, | 201 | packet_decrypt(CipherContext * cc, void *dest, void *src, |
208 | unsigned int bytes) | 202 | unsigned int bytes) |
209 | { | 203 | { |
210 | int i; | 204 | int i; |
211 | 205 | ||
212 | if ((bytes % 8) != 0) | 206 | if ((bytes % 8) != 0) |
213 | fatal("packet_decrypt: bad ciphertext length %d", bytes); | 207 | fatal("packet_decrypt: bad ciphertext length %d", bytes); |
214 | 208 | ||
215 | /* | 209 | /* Cryptographic attack detector for ssh - Modifications for packet.c |
216 | Cryptographic attack detector for ssh - Modifications for packet.c | 210 | (C)1998 CORE-SDI, Buenos Aires Argentina Ariel Futoransky(futo@core-sdi.com) */ |
217 | (C)1998 CORE-SDI, Buenos Aires Argentina | 211 | |
218 | Ariel Futoransky(futo@core-sdi.com) | 212 | switch (cc->type) { |
219 | */ | 213 | case SSH_CIPHER_NONE: |
220 | switch (cc->type) | 214 | i = DEATTACK_OK; |
221 | { | 215 | break; |
222 | case SSH_CIPHER_NONE: | 216 | default: |
223 | i = DEATTACK_OK; | 217 | i = detect_attack(src, bytes, NULL); |
224 | break; | 218 | break; |
225 | default: | 219 | } |
226 | i = detect_attack(src, bytes, NULL); | 220 | |
227 | break; | 221 | if (i == DEATTACK_DETECTED) |
228 | } | 222 | packet_disconnect("crc32 compensation attack: network attack detected"); |
229 | 223 | ||
230 | if (i == DEATTACK_DETECTED) | 224 | cipher_decrypt(cc, dest, src, bytes); |
231 | packet_disconnect("crc32 compensation attack: network attack detected"); | ||
232 | |||
233 | cipher_decrypt(cc, dest, src, bytes); | ||
234 | } | 225 | } |
235 | 226 | ||
236 | /* Causes any further packets to be encrypted using the given key. The same | 227 | /* Causes any further packets to be encrypted using the given key. The same |
@@ -241,9 +232,9 @@ void | |||
241 | packet_set_encryption_key(const unsigned char *key, unsigned int keylen, | 232 | packet_set_encryption_key(const unsigned char *key, unsigned int keylen, |
242 | int cipher) | 233 | int cipher) |
243 | { | 234 | { |
244 | /* All other ciphers use the same key in both directions for now. */ | 235 | /* All other ciphers use the same key in both directions for now. */ |
245 | cipher_set_key(&receive_context, cipher, key, keylen, 0); | 236 | cipher_set_key(&receive_context, cipher, key, keylen, 0); |
246 | cipher_set_key(&send_context, cipher, key, keylen, 1); | 237 | cipher_set_key(&send_context, cipher, key, keylen, 1); |
247 | } | 238 | } |
248 | 239 | ||
249 | /* Starts constructing a packet to send. */ | 240 | /* Starts constructing a packet to send. */ |
@@ -251,12 +242,12 @@ packet_set_encryption_key(const unsigned char *key, unsigned int keylen, | |||
251 | void | 242 | void |
252 | packet_start(int type) | 243 | packet_start(int type) |
253 | { | 244 | { |
254 | char buf[9]; | 245 | char buf[9]; |
255 | 246 | ||
256 | buffer_clear(&outgoing_packet); | 247 | buffer_clear(&outgoing_packet); |
257 | memset(buf, 0, 8); | 248 | memset(buf, 0, 8); |
258 | buf[8] = type; | 249 | buf[8] = type; |
259 | buffer_append(&outgoing_packet, buf, 9); | 250 | buffer_append(&outgoing_packet, buf, 9); |
260 | } | 251 | } |
261 | 252 | ||
262 | /* Appends a character to the packet data. */ | 253 | /* Appends a character to the packet data. */ |
@@ -264,8 +255,8 @@ packet_start(int type) | |||
264 | void | 255 | void |
265 | packet_put_char(int value) | 256 | packet_put_char(int value) |
266 | { | 257 | { |
267 | char ch = value; | 258 | char ch = value; |
268 | buffer_append(&outgoing_packet, &ch, 1); | 259 | buffer_append(&outgoing_packet, &ch, 1); |
269 | } | 260 | } |
270 | 261 | ||
271 | /* Appends an integer to the packet data. */ | 262 | /* Appends an integer to the packet data. */ |
@@ -273,7 +264,7 @@ packet_put_char(int value) | |||
273 | void | 264 | void |
274 | packet_put_int(unsigned int value) | 265 | packet_put_int(unsigned int value) |
275 | { | 266 | { |
276 | buffer_put_int(&outgoing_packet, value); | 267 | buffer_put_int(&outgoing_packet, value); |
277 | } | 268 | } |
278 | 269 | ||
279 | /* Appends a string to packet data. */ | 270 | /* Appends a string to packet data. */ |
@@ -281,84 +272,85 @@ packet_put_int(unsigned int value) | |||
281 | void | 272 | void |
282 | packet_put_string(const char *buf, unsigned int len) | 273 | packet_put_string(const char *buf, unsigned int len) |
283 | { | 274 | { |
284 | buffer_put_string(&outgoing_packet, buf, len); | 275 | buffer_put_string(&outgoing_packet, buf, len); |
285 | } | 276 | } |
286 | 277 | ||
287 | /* Appends an arbitrary precision integer to packet data. */ | 278 | /* Appends an arbitrary precision integer to packet data. */ |
288 | 279 | ||
289 | void | 280 | void |
290 | packet_put_bignum(BIGNUM *value) | 281 | packet_put_bignum(BIGNUM * value) |
291 | { | 282 | { |
292 | buffer_put_bignum(&outgoing_packet, value); | 283 | buffer_put_bignum(&outgoing_packet, value); |
293 | } | 284 | } |
294 | 285 | ||
295 | /* Finalizes and sends the packet. If the encryption key has been set, | 286 | /* Finalizes and sends the packet. If the encryption key has been set, |
296 | encrypts the packet before sending. */ | 287 | encrypts the packet before sending. */ |
297 | 288 | ||
298 | void | 289 | void |
299 | packet_send() | 290 | packet_send() |
300 | { | 291 | { |
301 | char buf[8], *cp; | 292 | char buf[8], *cp; |
302 | int i, padding, len; | 293 | int i, padding, len; |
303 | unsigned int checksum; | 294 | unsigned int checksum; |
304 | u_int32_t rand = 0; | 295 | u_int32_t rand = 0; |
305 | 296 | ||
306 | /* If using packet compression, compress the payload of the outgoing | 297 | /* If using packet compression, compress the payload of the |
307 | packet. */ | 298 | outgoing packet. */ |
308 | if (packet_compression) | 299 | if (packet_compression) { |
309 | { | 300 | buffer_clear(&compression_buffer); |
310 | buffer_clear(&compression_buffer); | 301 | /* Skip padding. */ |
311 | buffer_consume(&outgoing_packet, 8); /* Skip padding. */ | 302 | buffer_consume(&outgoing_packet, 8); |
312 | buffer_append(&compression_buffer, "\0\0\0\0\0\0\0\0", 8); /* padding */ | 303 | /* padding */ |
313 | buffer_compress(&outgoing_packet, &compression_buffer); | 304 | buffer_append(&compression_buffer, "\0\0\0\0\0\0\0\0", 8); |
314 | buffer_clear(&outgoing_packet); | 305 | buffer_compress(&outgoing_packet, &compression_buffer); |
315 | buffer_append(&outgoing_packet, buffer_ptr(&compression_buffer), | 306 | buffer_clear(&outgoing_packet); |
316 | buffer_len(&compression_buffer)); | 307 | buffer_append(&outgoing_packet, buffer_ptr(&compression_buffer), |
317 | } | 308 | buffer_len(&compression_buffer)); |
318 | 309 | } | |
319 | /* Compute packet length without padding (add checksum, remove padding). */ | 310 | /* Compute packet length without padding (add checksum, remove padding). */ |
320 | len = buffer_len(&outgoing_packet) + 4 - 8; | 311 | len = buffer_len(&outgoing_packet) + 4 - 8; |
321 | 312 | ||
322 | /* Insert padding. */ | 313 | /* Insert padding. */ |
323 | padding = 8 - len % 8; | 314 | padding = 8 - len % 8; |
324 | if (cipher_type != SSH_CIPHER_NONE) | 315 | if (cipher_type != SSH_CIPHER_NONE) { |
325 | { | 316 | cp = buffer_ptr(&outgoing_packet); |
326 | cp = buffer_ptr(&outgoing_packet); | 317 | for (i = 0; i < padding; i++) { |
327 | for (i = 0; i < padding; i++) { | 318 | if (i % 4 == 0) |
328 | if (i % 4 == 0) | 319 | rand = arc4random(); |
329 | rand = arc4random(); | 320 | cp[7 - i] = rand & 0xff; |
330 | cp[7 - i] = rand & 0xff; | 321 | rand >>= 8; |
331 | rand >>= 8; | 322 | } |
332 | } | 323 | } |
333 | } | 324 | buffer_consume(&outgoing_packet, 8 - padding); |
334 | buffer_consume(&outgoing_packet, 8 - padding); | 325 | |
335 | 326 | /* Add check bytes. */ | |
336 | /* Add check bytes. */ | 327 | checksum = crc32((unsigned char *) buffer_ptr(&outgoing_packet), |
337 | checksum = crc32((unsigned char *)buffer_ptr(&outgoing_packet), | 328 | buffer_len(&outgoing_packet)); |
338 | buffer_len(&outgoing_packet)); | 329 | PUT_32BIT(buf, checksum); |
339 | PUT_32BIT(buf, checksum); | 330 | buffer_append(&outgoing_packet, buf, 4); |
340 | buffer_append(&outgoing_packet, buf, 4); | ||
341 | 331 | ||
342 | #ifdef PACKET_DEBUG | 332 | #ifdef PACKET_DEBUG |
343 | fprintf(stderr, "packet_send plain: "); | 333 | fprintf(stderr, "packet_send plain: "); |
344 | buffer_dump(&outgoing_packet); | 334 | buffer_dump(&outgoing_packet); |
345 | #endif | 335 | #endif |
346 | 336 | ||
347 | /* Append to output. */ | 337 | /* Append to output. */ |
348 | PUT_32BIT(buf, len); | 338 | PUT_32BIT(buf, len); |
349 | buffer_append(&output, buf, 4); | 339 | buffer_append(&output, buf, 4); |
350 | buffer_append_space(&output, &cp, buffer_len(&outgoing_packet)); | 340 | buffer_append_space(&output, &cp, buffer_len(&outgoing_packet)); |
351 | packet_encrypt(&send_context, cp, buffer_ptr(&outgoing_packet), | 341 | packet_encrypt(&send_context, cp, buffer_ptr(&outgoing_packet), |
352 | buffer_len(&outgoing_packet)); | 342 | buffer_len(&outgoing_packet)); |
353 | 343 | ||
354 | #ifdef PACKET_DEBUG | 344 | #ifdef PACKET_DEBUG |
355 | fprintf(stderr, "encrypted: "); buffer_dump(&output); | 345 | fprintf(stderr, "encrypted: "); |
346 | buffer_dump(&output); | ||
356 | #endif | 347 | #endif |
357 | 348 | ||
358 | buffer_clear(&outgoing_packet); | 349 | buffer_clear(&outgoing_packet); |
359 | 350 | ||
360 | /* Note that the packet is now only buffered in output. It won\'t be | 351 | /* Note that the packet is now only buffered in output. It won\'t |
361 | actually sent until packet_write_wait or packet_write_poll is called. */ | 352 | be actually sent until packet_write_wait or packet_write_poll |
353 | is called. */ | ||
362 | } | 354 | } |
363 | 355 | ||
364 | /* Waits until a packet has been received, and returns its type. Note that | 356 | /* Waits until a packet has been received, and returns its type. Note that |
@@ -368,42 +360,41 @@ packet_send() | |||
368 | int | 360 | int |
369 | packet_read(int *payload_len_ptr) | 361 | packet_read(int *payload_len_ptr) |
370 | { | 362 | { |
371 | int type, len; | 363 | int type, len; |
372 | fd_set set; | 364 | fd_set set; |
373 | char buf[8192]; | 365 | char buf[8192]; |
374 | 366 | ||
375 | /* Since we are blocking, ensure that all written packets have been sent. */ | 367 | /* Since we are blocking, ensure that all written packets have been sent. */ |
376 | packet_write_wait(); | 368 | packet_write_wait(); |
377 | 369 | ||
378 | /* Stay in the loop until we have received a complete packet. */ | 370 | /* Stay in the loop until we have received a complete packet. */ |
379 | for (;;) | 371 | for (;;) { |
380 | { | 372 | /* Try to read a packet from the buffer. */ |
381 | /* Try to read a packet from the buffer. */ | 373 | type = packet_read_poll(payload_len_ptr); |
382 | type = packet_read_poll(payload_len_ptr); | 374 | if (type == SSH_SMSG_SUCCESS |
383 | if (type == SSH_SMSG_SUCCESS | 375 | || type == SSH_SMSG_FAILURE |
384 | || type == SSH_SMSG_FAILURE | 376 | || type == SSH_CMSG_EOF |
385 | || type == SSH_CMSG_EOF | 377 | || type == SSH_CMSG_EXIT_CONFIRMATION) |
386 | || type == SSH_CMSG_EXIT_CONFIRMATION) | 378 | packet_integrity_check(*payload_len_ptr, 0, type); |
387 | packet_integrity_check(*payload_len_ptr, 0, type); | 379 | /* If we got a packet, return it. */ |
388 | /* If we got a packet, return it. */ | 380 | if (type != SSH_MSG_NONE) |
389 | if (type != SSH_MSG_NONE) | 381 | return type; |
390 | return type; | 382 | /* Otherwise, wait for some data to arrive, add it to the |
391 | /* Otherwise, wait for some data to arrive, add it to the buffer, | 383 | buffer, and try again. */ |
392 | and try again. */ | 384 | FD_ZERO(&set); |
393 | FD_ZERO(&set); | 385 | FD_SET(connection_in, &set); |
394 | FD_SET(connection_in, &set); | 386 | /* Wait for some data to arrive. */ |
395 | /* Wait for some data to arrive. */ | 387 | select(connection_in + 1, &set, NULL, NULL, NULL); |
396 | select(connection_in + 1, &set, NULL, NULL, NULL); | 388 | /* Read data from the socket. */ |
397 | /* Read data from the socket. */ | 389 | len = read(connection_in, buf, sizeof(buf)); |
398 | len = read(connection_in, buf, sizeof(buf)); | 390 | if (len == 0) |
399 | if (len == 0) | 391 | fatal("Connection closed by %.200s", get_remote_ipaddr()); |
400 | fatal("Connection closed by remote host."); | 392 | if (len < 0) |
401 | if (len < 0) | 393 | fatal("Read from socket failed: %.100s", strerror(errno)); |
402 | fatal("Read from socket failed: %.100s", strerror(errno)); | 394 | /* Append it to the buffer. */ |
403 | /* Append it to the buffer. */ | 395 | packet_process_incoming(buf, len); |
404 | packet_process_incoming(buf, len); | 396 | } |
405 | } | 397 | /* NOTREACHED */ |
406 | /*NOTREACHED*/ | ||
407 | } | 398 | } |
408 | 399 | ||
409 | /* Waits until a packet has been received, verifies that its type matches | 400 | /* Waits until a packet has been received, verifies that its type matches |
@@ -412,131 +403,126 @@ packet_read(int *payload_len_ptr) | |||
412 | void | 403 | void |
413 | packet_read_expect(int *payload_len_ptr, int expected_type) | 404 | packet_read_expect(int *payload_len_ptr, int expected_type) |
414 | { | 405 | { |
415 | int type; | 406 | int type; |
416 | 407 | ||
417 | type = packet_read(payload_len_ptr); | 408 | type = packet_read(payload_len_ptr); |
418 | if (type != expected_type) | 409 | if (type != expected_type) |
419 | packet_disconnect("Protocol error: expected packet type %d, got %d", | 410 | packet_disconnect("Protocol error: expected packet type %d, got %d", |
420 | expected_type, type); | 411 | expected_type, type); |
421 | } | 412 | } |
422 | 413 | ||
423 | /* Checks if a full packet is available in the data received so far via | 414 | /* Checks if a full packet is available in the data received so far via |
424 | packet_process_incoming. If so, reads the packet; otherwise returns | 415 | * packet_process_incoming. If so, reads the packet; otherwise returns |
425 | SSH_MSG_NONE. This does not wait for data from the connection. | 416 | * SSH_MSG_NONE. This does not wait for data from the connection. |
426 | 417 | * | |
427 | SSH_MSG_DISCONNECT is handled specially here. Also, | 418 | * SSH_MSG_DISCONNECT is handled specially here. Also, |
428 | SSH_MSG_IGNORE messages are skipped by this function and are never returned | 419 | * SSH_MSG_IGNORE messages are skipped by this function and are never returned |
429 | to higher levels. | 420 | * to higher levels. |
430 | 421 | * | |
431 | The returned payload_len does include space consumed by: | 422 | * The returned payload_len does include space consumed by: |
432 | Packet length | 423 | * Packet length |
433 | Padding | 424 | * Padding |
434 | Packet type | 425 | * Packet type |
435 | Check bytes | 426 | * Check bytes |
436 | 427 | */ | |
437 | |||
438 | */ | ||
439 | 428 | ||
440 | int | 429 | int |
441 | packet_read_poll(int *payload_len_ptr) | 430 | packet_read_poll(int *payload_len_ptr) |
442 | { | 431 | { |
443 | unsigned int len, padded_len; | 432 | unsigned int len, padded_len; |
444 | unsigned char *ucp; | 433 | unsigned char *ucp; |
445 | char buf[8], *cp; | 434 | char buf[8], *cp; |
446 | unsigned int checksum, stored_checksum; | 435 | unsigned int checksum, stored_checksum; |
447 | 436 | ||
448 | restart: | 437 | restart: |
449 | 438 | ||
450 | /* Check if input size is less than minimum packet size. */ | 439 | /* Check if input size is less than minimum packet size. */ |
451 | if (buffer_len(&input) < 4 + 8) | 440 | if (buffer_len(&input) < 4 + 8) |
452 | return SSH_MSG_NONE; | 441 | return SSH_MSG_NONE; |
453 | /* Get length of incoming packet. */ | 442 | /* Get length of incoming packet. */ |
454 | ucp = (unsigned char *)buffer_ptr(&input); | 443 | ucp = (unsigned char *) buffer_ptr(&input); |
455 | len = GET_32BIT(ucp); | 444 | len = GET_32BIT(ucp); |
456 | if (len < 1 + 2 + 2 || len > 256*1024) | 445 | if (len < 1 + 2 + 2 || len > 256 * 1024) |
457 | packet_disconnect("Bad packet length %d.", len); | 446 | packet_disconnect("Bad packet length %d.", len); |
458 | padded_len = (len + 8) & ~7; | 447 | padded_len = (len + 8) & ~7; |
459 | 448 | ||
460 | /* Check if the packet has been entirely received. */ | 449 | /* Check if the packet has been entirely received. */ |
461 | if (buffer_len(&input) < 4 + padded_len) | 450 | if (buffer_len(&input) < 4 + padded_len) |
462 | return SSH_MSG_NONE; | 451 | return SSH_MSG_NONE; |
463 | 452 | ||
464 | /* The entire packet is in buffer. */ | 453 | /* The entire packet is in buffer. */ |
465 | 454 | ||
466 | /* Consume packet length. */ | 455 | /* Consume packet length. */ |
467 | buffer_consume(&input, 4); | 456 | buffer_consume(&input, 4); |
468 | 457 | ||
469 | /* Copy data to incoming_packet. */ | 458 | /* Copy data to incoming_packet. */ |
470 | buffer_clear(&incoming_packet); | 459 | buffer_clear(&incoming_packet); |
471 | buffer_append_space(&incoming_packet, &cp, padded_len); | 460 | buffer_append_space(&incoming_packet, &cp, padded_len); |
472 | packet_decrypt(&receive_context, cp, buffer_ptr(&input), padded_len); | 461 | packet_decrypt(&receive_context, cp, buffer_ptr(&input), padded_len); |
473 | buffer_consume(&input, padded_len); | 462 | buffer_consume(&input, padded_len); |
474 | 463 | ||
475 | #ifdef PACKET_DEBUG | 464 | #ifdef PACKET_DEBUG |
476 | fprintf(stderr, "read_poll plain: "); buffer_dump(&incoming_packet); | 465 | fprintf(stderr, "read_poll plain: "); |
466 | buffer_dump(&incoming_packet); | ||
477 | #endif | 467 | #endif |
478 | 468 | ||
479 | /* Compute packet checksum. */ | 469 | /* Compute packet checksum. */ |
480 | checksum = crc32((unsigned char *)buffer_ptr(&incoming_packet), | 470 | checksum = crc32((unsigned char *) buffer_ptr(&incoming_packet), |
481 | buffer_len(&incoming_packet) - 4); | 471 | buffer_len(&incoming_packet) - 4); |
482 | 472 | ||
483 | /* Skip padding. */ | 473 | /* Skip padding. */ |
484 | buffer_consume(&incoming_packet, 8 - len % 8); | 474 | buffer_consume(&incoming_packet, 8 - len % 8); |
485 | 475 | ||
486 | /* Test check bytes. */ | 476 | /* Test check bytes. */ |
487 | 477 | ||
488 | if (len != buffer_len(&incoming_packet)) | 478 | if (len != buffer_len(&incoming_packet)) |
489 | packet_disconnect("packet_read_poll: len %d != buffer_len %d.", | 479 | packet_disconnect("packet_read_poll: len %d != buffer_len %d.", |
490 | len, buffer_len(&incoming_packet)); | 480 | len, buffer_len(&incoming_packet)); |
491 | 481 | ||
492 | ucp = (unsigned char *)buffer_ptr(&incoming_packet) + len - 4; | 482 | ucp = (unsigned char *) buffer_ptr(&incoming_packet) + len - 4; |
493 | stored_checksum = GET_32BIT(ucp); | 483 | stored_checksum = GET_32BIT(ucp); |
494 | if (checksum != stored_checksum) | 484 | if (checksum != stored_checksum) |
495 | packet_disconnect("Corrupted check bytes on input."); | 485 | packet_disconnect("Corrupted check bytes on input."); |
496 | buffer_consume_end(&incoming_packet, 4); | 486 | buffer_consume_end(&incoming_packet, 4); |
497 | 487 | ||
498 | /* If using packet compression, decompress the packet. */ | 488 | /* If using packet compression, decompress the packet. */ |
499 | if (packet_compression) | 489 | if (packet_compression) { |
500 | { | 490 | buffer_clear(&compression_buffer); |
501 | buffer_clear(&compression_buffer); | 491 | buffer_uncompress(&incoming_packet, &compression_buffer); |
502 | buffer_uncompress(&incoming_packet, &compression_buffer); | 492 | buffer_clear(&incoming_packet); |
503 | buffer_clear(&incoming_packet); | 493 | buffer_append(&incoming_packet, buffer_ptr(&compression_buffer), |
504 | buffer_append(&incoming_packet, buffer_ptr(&compression_buffer), | 494 | buffer_len(&compression_buffer)); |
505 | buffer_len(&compression_buffer)); | 495 | } |
506 | } | 496 | /* Get packet type. */ |
507 | 497 | buffer_get(&incoming_packet, &buf[0], 1); | |
508 | /* Get packet type. */ | 498 | |
509 | buffer_get(&incoming_packet, &buf[0], 1); | 499 | /* Return length of payload (without type field). */ |
510 | 500 | *payload_len_ptr = buffer_len(&incoming_packet); | |
511 | /* Return length of payload (without type field). */ | 501 | |
512 | *payload_len_ptr = buffer_len(&incoming_packet); | 502 | /* Handle disconnect message. */ |
513 | 503 | if ((unsigned char) buf[0] == SSH_MSG_DISCONNECT) | |
514 | /* Handle disconnect message. */ | 504 | fatal("Received disconnect: %.900s", packet_get_string(NULL)); |
515 | if ((unsigned char)buf[0] == SSH_MSG_DISCONNECT) | 505 | |
516 | fatal("%.900s", packet_get_string(NULL)); | 506 | /* Ignore ignore messages. */ |
517 | 507 | if ((unsigned char) buf[0] == SSH_MSG_IGNORE) | |
518 | /* Ignore ignore messages. */ | 508 | goto restart; |
519 | if ((unsigned char)buf[0] == SSH_MSG_IGNORE) | 509 | |
520 | goto restart; | 510 | /* Send debug messages as debugging output. */ |
521 | 511 | if ((unsigned char) buf[0] == SSH_MSG_DEBUG) { | |
522 | /* Send debug messages as debugging output. */ | 512 | debug("Remote: %.900s", packet_get_string(NULL)); |
523 | if ((unsigned char)buf[0] == SSH_MSG_DEBUG) | 513 | goto restart; |
524 | { | 514 | } |
525 | debug("Remote: %.900s", packet_get_string(NULL)); | 515 | /* Return type. */ |
526 | goto restart; | 516 | return (unsigned char) buf[0]; |
527 | } | 517 | } |
528 | 518 | ||
529 | /* Return type. */ | ||
530 | return (unsigned char)buf[0]; | ||
531 | } | ||
532 | |||
533 | /* Buffers the given amount of input characters. This is intended to be | 519 | /* Buffers the given amount of input characters. This is intended to be |
534 | used together with packet_read_poll. */ | 520 | used together with packet_read_poll. */ |
535 | 521 | ||
536 | void | 522 | void |
537 | packet_process_incoming(const char *buf, unsigned int len) | 523 | packet_process_incoming(const char *buf, unsigned int len) |
538 | { | 524 | { |
539 | buffer_append(&input, buf, len); | 525 | buffer_append(&input, buf, len); |
540 | } | 526 | } |
541 | 527 | ||
542 | /* Returns a character from the packet. */ | 528 | /* Returns a character from the packet. */ |
@@ -544,9 +530,9 @@ packet_process_incoming(const char *buf, unsigned int len) | |||
544 | unsigned int | 530 | unsigned int |
545 | packet_get_char() | 531 | packet_get_char() |
546 | { | 532 | { |
547 | char ch; | 533 | char ch; |
548 | buffer_get(&incoming_packet, &ch, 1); | 534 | buffer_get(&incoming_packet, &ch, 1); |
549 | return (unsigned char)ch; | 535 | return (unsigned char) ch; |
550 | } | 536 | } |
551 | 537 | ||
552 | /* Returns an integer from the packet data. */ | 538 | /* Returns an integer from the packet data. */ |
@@ -554,16 +540,16 @@ packet_get_char() | |||
554 | unsigned int | 540 | unsigned int |
555 | packet_get_int() | 541 | packet_get_int() |
556 | { | 542 | { |
557 | return buffer_get_int(&incoming_packet); | 543 | return buffer_get_int(&incoming_packet); |
558 | } | 544 | } |
559 | 545 | ||
560 | /* Returns an arbitrary precision integer from the packet data. The integer | 546 | /* Returns an arbitrary precision integer from the packet data. The integer |
561 | must have been initialized before this call. */ | 547 | must have been initialized before this call. */ |
562 | 548 | ||
563 | void | 549 | void |
564 | packet_get_bignum(BIGNUM *value, int *length_ptr) | 550 | packet_get_bignum(BIGNUM * value, int *length_ptr) |
565 | { | 551 | { |
566 | *length_ptr = buffer_get_bignum(&incoming_packet, value); | 552 | *length_ptr = buffer_get_bignum(&incoming_packet, value); |
567 | } | 553 | } |
568 | 554 | ||
569 | /* Returns a string from the packet data. The string is allocated using | 555 | /* Returns a string from the packet data. The string is allocated using |
@@ -572,9 +558,10 @@ packet_get_bignum(BIGNUM *value, int *length_ptr) | |||
572 | integer into which the length of the string is stored. */ | 558 | integer into which the length of the string is stored. */ |
573 | 559 | ||
574 | char | 560 | char |
575 | *packet_get_string(unsigned int *length_ptr) | 561 | * |
562 | packet_get_string(unsigned int *length_ptr) | ||
576 | { | 563 | { |
577 | return buffer_get_string(&incoming_packet, length_ptr); | 564 | return buffer_get_string(&incoming_packet, length_ptr); |
578 | } | 565 | } |
579 | 566 | ||
580 | /* Sends a diagnostic message from the server to the client. This message | 567 | /* Sends a diagnostic message from the server to the client. This message |
@@ -586,19 +573,19 @@ char | |||
586 | packet_write_wait. */ | 573 | packet_write_wait. */ |
587 | 574 | ||
588 | void | 575 | void |
589 | packet_send_debug(const char *fmt, ...) | 576 | packet_send_debug(const char *fmt,...) |
590 | { | 577 | { |
591 | char buf[1024]; | 578 | char buf[1024]; |
592 | va_list args; | 579 | va_list args; |
593 | 580 | ||
594 | va_start(args, fmt); | 581 | va_start(args, fmt); |
595 | vsnprintf(buf, sizeof(buf), fmt, args); | 582 | vsnprintf(buf, sizeof(buf), fmt, args); |
596 | va_end(args); | 583 | va_end(args); |
597 | 584 | ||
598 | packet_start(SSH_MSG_DEBUG); | 585 | packet_start(SSH_MSG_DEBUG); |
599 | packet_put_string(buf, strlen(buf)); | 586 | packet_put_string(buf, strlen(buf)); |
600 | packet_send(); | 587 | packet_send(); |
601 | packet_write_wait(); | 588 | packet_write_wait(); |
602 | } | 589 | } |
603 | 590 | ||
604 | /* Logs the error plus constructs and sends a disconnect | 591 | /* Logs the error plus constructs and sends a disconnect |
@@ -607,36 +594,35 @@ packet_send_debug(const char *fmt, ...) | |||
607 | formatted message must not exceed 1024 bytes. */ | 594 | formatted message must not exceed 1024 bytes. */ |
608 | 595 | ||
609 | void | 596 | void |
610 | packet_disconnect(const char *fmt, ...) | 597 | packet_disconnect(const char *fmt,...) |
611 | { | 598 | { |
612 | char buf[1024]; | 599 | char buf[1024]; |
613 | va_list args; | 600 | va_list args; |
614 | static int disconnecting = 0; | 601 | static int disconnecting = 0; |
615 | if (disconnecting) /* Guard against recursive invocations. */ | 602 | if (disconnecting) /* Guard against recursive invocations. */ |
616 | fatal("packet_disconnect called recursively."); | 603 | fatal("packet_disconnect called recursively."); |
617 | disconnecting = 1; | 604 | disconnecting = 1; |
618 | 605 | ||
619 | /* Format the message. Note that the caller must make sure the message | 606 | /* Format the message. Note that the caller must make sure the |
620 | is of limited size. */ | 607 | message is of limited size. */ |
621 | va_start(args, fmt); | 608 | va_start(args, fmt); |
622 | vsnprintf(buf, sizeof(buf), fmt, args); | 609 | vsnprintf(buf, sizeof(buf), fmt, args); |
623 | va_end(args); | 610 | va_end(args); |
624 | 611 | ||
625 | /* Send the disconnect message to the other side, and wait for it to get | 612 | /* Send the disconnect message to the other side, and wait for it to get sent. */ |
626 | sent. */ | 613 | packet_start(SSH_MSG_DISCONNECT); |
627 | packet_start(SSH_MSG_DISCONNECT); | 614 | packet_put_string(buf, strlen(buf)); |
628 | packet_put_string(buf, strlen(buf)); | 615 | packet_send(); |
629 | packet_send(); | 616 | packet_write_wait(); |
630 | packet_write_wait(); | 617 | |
631 | 618 | /* Stop listening for connections. */ | |
632 | /* Stop listening for connections. */ | 619 | channel_stop_listening(); |
633 | channel_stop_listening(); | 620 | |
634 | 621 | /* Close the connection. */ | |
635 | /* Close the connection. */ | 622 | packet_close(); |
636 | packet_close(); | 623 | |
637 | 624 | /* Display the error locally and exit. */ | |
638 | /* Display the error locally and exit. */ | 625 | fatal("Disconnecting: %.100s", buf); |
639 | fatal("Local: %.100s", buf); | ||
640 | } | 626 | } |
641 | 627 | ||
642 | /* Checks if there is any buffered output, and tries to write some of the | 628 | /* Checks if there is any buffered output, and tries to write some of the |
@@ -645,18 +631,17 @@ packet_disconnect(const char *fmt, ...) | |||
645 | void | 631 | void |
646 | packet_write_poll() | 632 | packet_write_poll() |
647 | { | 633 | { |
648 | int len = buffer_len(&output); | 634 | int len = buffer_len(&output); |
649 | if (len > 0) | 635 | if (len > 0) { |
650 | { | 636 | len = write(connection_out, buffer_ptr(&output), len); |
651 | len = write(connection_out, buffer_ptr(&output), len); | 637 | if (len <= 0) { |
652 | if (len <= 0) { | 638 | if (errno == EAGAIN) |
653 | if (errno == EAGAIN) | 639 | return; |
654 | return; | 640 | else |
655 | else | 641 | fatal("Write failed: %.100s", strerror(errno)); |
656 | fatal("Write failed: %.100s", strerror(errno)); | 642 | } |
657 | } | 643 | buffer_consume(&output, len); |
658 | buffer_consume(&output, len); | 644 | } |
659 | } | ||
660 | } | 645 | } |
661 | 646 | ||
662 | /* Calls packet_write_poll repeatedly until all pending output data has | 647 | /* Calls packet_write_poll repeatedly until all pending output data has |
@@ -665,15 +650,14 @@ packet_write_poll() | |||
665 | void | 650 | void |
666 | packet_write_wait() | 651 | packet_write_wait() |
667 | { | 652 | { |
668 | packet_write_poll(); | 653 | packet_write_poll(); |
669 | while (packet_have_data_to_write()) | 654 | while (packet_have_data_to_write()) { |
670 | { | 655 | fd_set set; |
671 | fd_set set; | 656 | FD_ZERO(&set); |
672 | FD_ZERO(&set); | 657 | FD_SET(connection_out, &set); |
673 | FD_SET(connection_out, &set); | 658 | select(connection_out + 1, NULL, &set, NULL, NULL); |
674 | select(connection_out + 1, NULL, &set, NULL, NULL); | 659 | packet_write_poll(); |
675 | packet_write_poll(); | 660 | } |
676 | } | ||
677 | } | 661 | } |
678 | 662 | ||
679 | /* Returns true if there is buffered data to write to the connection. */ | 663 | /* Returns true if there is buffered data to write to the connection. */ |
@@ -681,7 +665,7 @@ packet_write_wait() | |||
681 | int | 665 | int |
682 | packet_have_data_to_write() | 666 | packet_have_data_to_write() |
683 | { | 667 | { |
684 | return buffer_len(&output) != 0; | 668 | return buffer_len(&output) != 0; |
685 | } | 669 | } |
686 | 670 | ||
687 | /* Returns true if there is not too much data to write to the connection. */ | 671 | /* Returns true if there is not too much data to write to the connection. */ |
@@ -689,10 +673,10 @@ packet_have_data_to_write() | |||
689 | int | 673 | int |
690 | packet_not_very_much_data_to_write() | 674 | packet_not_very_much_data_to_write() |
691 | { | 675 | { |
692 | if (interactive_mode) | 676 | if (interactive_mode) |
693 | return buffer_len(&output) < 16384; | 677 | return buffer_len(&output) < 16384; |
694 | else | 678 | else |
695 | return buffer_len(&output) < 128*1024; | 679 | return buffer_len(&output) < 128 * 1024; |
696 | } | 680 | } |
697 | 681 | ||
698 | /* Informs that the current session is interactive. Sets IP flags for that. */ | 682 | /* Informs that the current session is interactive. Sets IP flags for that. */ |
@@ -700,45 +684,40 @@ packet_not_very_much_data_to_write() | |||
700 | void | 684 | void |
701 | packet_set_interactive(int interactive, int keepalives) | 685 | packet_set_interactive(int interactive, int keepalives) |
702 | { | 686 | { |
703 | int on = 1; | 687 | int on = 1; |
704 | 688 | ||
705 | /* Record that we are in interactive mode. */ | 689 | /* Record that we are in interactive mode. */ |
706 | interactive_mode = interactive; | 690 | interactive_mode = interactive; |
707 | 691 | ||
708 | /* Only set socket options if using a socket (as indicated by the descriptors | 692 | /* Only set socket options if using a socket (as indicated by the |
709 | being the same). */ | 693 | descriptors being the same). */ |
710 | if (connection_in != connection_out) | 694 | if (connection_in != connection_out) |
711 | return; | 695 | return; |
712 | 696 | ||
713 | if (keepalives) | 697 | if (keepalives) { |
714 | { | 698 | /* Set keepalives if requested. */ |
715 | /* Set keepalives if requested. */ | 699 | if (setsockopt(connection_in, SOL_SOCKET, SO_KEEPALIVE, (void *) &on, |
716 | if (setsockopt(connection_in, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, | 700 | sizeof(on)) < 0) |
717 | sizeof(on)) < 0) | 701 | error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); |
718 | error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); | 702 | } |
719 | } | 703 | if (interactive) { |
720 | 704 | /* Set IP options for an interactive connection. Use | |
721 | if (interactive) | 705 | IPTOS_LOWDELAY and TCP_NODELAY. */ |
722 | { | 706 | int lowdelay = IPTOS_LOWDELAY; |
723 | /* Set IP options for an interactive connection. Use IPTOS_LOWDELAY | 707 | if (setsockopt(connection_in, IPPROTO_IP, IP_TOS, (void *) &lowdelay, |
724 | and TCP_NODELAY. */ | 708 | sizeof(lowdelay)) < 0) |
725 | int lowdelay = IPTOS_LOWDELAY; | 709 | error("setsockopt IPTOS_LOWDELAY: %.100s", strerror(errno)); |
726 | if (setsockopt(connection_in, IPPROTO_IP, IP_TOS, (void *)&lowdelay, | 710 | if (setsockopt(connection_in, IPPROTO_TCP, TCP_NODELAY, (void *) &on, |
727 | sizeof(lowdelay)) < 0) | 711 | sizeof(on)) < 0) |
728 | error("setsockopt IPTOS_LOWDELAY: %.100s", strerror(errno)); | 712 | error("setsockopt TCP_NODELAY: %.100s", strerror(errno)); |
729 | if (setsockopt(connection_in, IPPROTO_TCP, TCP_NODELAY, (void *)&on, | 713 | } else { |
730 | sizeof(on)) < 0) | 714 | /* Set IP options for a non-interactive connection. Use |
731 | error("setsockopt TCP_NODELAY: %.100s", strerror(errno)); | 715 | IPTOS_THROUGHPUT. */ |
732 | } | 716 | int throughput = IPTOS_THROUGHPUT; |
733 | else | 717 | if (setsockopt(connection_in, IPPROTO_IP, IP_TOS, (void *) &throughput, |
734 | { | 718 | sizeof(throughput)) < 0) |
735 | /* Set IP options for a non-interactive connection. Use | 719 | error("setsockopt IPTOS_THROUGHPUT: %.100s", strerror(errno)); |
736 | IPTOS_THROUGHPUT. */ | 720 | } |
737 | int throughput = IPTOS_THROUGHPUT; | ||
738 | if (setsockopt(connection_in, IPPROTO_IP, IP_TOS, (void *)&throughput, | ||
739 | sizeof(throughput)) < 0) | ||
740 | error("setsockopt IPTOS_THROUGHPUT: %.100s", strerror(errno)); | ||
741 | } | ||
742 | } | 721 | } |
743 | 722 | ||
744 | /* Returns true if the current connection is interactive. */ | 723 | /* Returns true if the current connection is interactive. */ |
@@ -746,22 +725,22 @@ packet_set_interactive(int interactive, int keepalives) | |||
746 | int | 725 | int |
747 | packet_is_interactive() | 726 | packet_is_interactive() |
748 | { | 727 | { |
749 | return interactive_mode; | 728 | return interactive_mode; |
750 | } | 729 | } |
751 | 730 | ||
752 | int | 731 | int |
753 | packet_set_maxsize(int s) | 732 | packet_set_maxsize(int s) |
754 | { | 733 | { |
755 | static int called = 0; | 734 | static int called = 0; |
756 | if (called) { | 735 | if (called) { |
757 | log("packet_set_maxsize: called twice: old %d new %d", max_packet_size, s); | 736 | log("packet_set_maxsize: called twice: old %d new %d", max_packet_size, s); |
758 | return -1; | 737 | return -1; |
759 | } | 738 | } |
760 | if (s < 4*1024 || s > 1024*1024) { | 739 | if (s < 4 * 1024 || s > 1024 * 1024) { |
761 | log("packet_set_maxsize: bad size %d", s); | 740 | log("packet_set_maxsize: bad size %d", s); |
762 | return -1; | 741 | return -1; |
763 | } | 742 | } |
764 | log("packet_set_maxsize: setting to %d", s); | 743 | log("packet_set_maxsize: setting to %d", s); |
765 | max_packet_size = s; | 744 | max_packet_size = s; |
766 | return s; | 745 | return s; |
767 | } | 746 | } |
@@ -1,19 +1,19 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * | |
3 | packet.h | 3 | * packet.h |
4 | 4 | * | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
6 | 6 | * | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
8 | All rights reserved | 8 | * All rights reserved |
9 | 9 | * | |
10 | Created: Sat Mar 18 02:02:14 1995 ylo | 10 | * Created: Sat Mar 18 02:02:14 1995 ylo |
11 | 11 | * | |
12 | Interface for the packet protocol functions. | 12 | * Interface for the packet protocol functions. |
13 | 13 | * | |
14 | */ | 14 | */ |
15 | 15 | ||
16 | /* RCSID("$Id: packet.h,v 1.5 1999/11/21 02:23:53 damien Exp $"); */ | 16 | /* RCSID("$Id: packet.h,v 1.6 1999/11/24 13:26:22 damien Exp $"); */ |
17 | 17 | ||
18 | #ifndef PACKET_H | 18 | #ifndef PACKET_H |
19 | #define PACKET_H | 19 | #define PACKET_H |
@@ -31,83 +31,84 @@ Interface for the packet protocol functions. | |||
31 | packet_set_encryption_key is called. It is permissible that fd_in | 31 | packet_set_encryption_key is called. It is permissible that fd_in |
32 | and fd_out are the same descriptor; in that case it is assumed to | 32 | and fd_out are the same descriptor; in that case it is assumed to |
33 | be a socket. */ | 33 | be a socket. */ |
34 | void packet_set_connection(int fd_in, int fd_out); | 34 | void packet_set_connection(int fd_in, int fd_out); |
35 | 35 | ||
36 | /* Puts the connection file descriptors into non-blocking mode. */ | 36 | /* Puts the connection file descriptors into non-blocking mode. */ |
37 | void packet_set_nonblocking(void); | 37 | void packet_set_nonblocking(void); |
38 | 38 | ||
39 | /* Returns the file descriptor used for input. */ | 39 | /* Returns the file descriptor used for input. */ |
40 | int packet_get_connection_in(void); | 40 | int packet_get_connection_in(void); |
41 | 41 | ||
42 | /* Returns the file descriptor used for output. */ | 42 | /* Returns the file descriptor used for output. */ |
43 | int packet_get_connection_out(void); | 43 | int packet_get_connection_out(void); |
44 | 44 | ||
45 | /* Closes the connection (both descriptors) and clears and frees | 45 | /* Closes the connection (both descriptors) and clears and frees |
46 | internal data structures. */ | 46 | internal data structures. */ |
47 | void packet_close(void); | 47 | void packet_close(void); |
48 | 48 | ||
49 | /* Causes any further packets to be encrypted using the given key. The same | 49 | /* Causes any further packets to be encrypted using the given key. The same |
50 | key is used for both sending and reception. However, both directions | 50 | key is used for both sending and reception. However, both directions |
51 | are encrypted independently of each other. Cipher types are | 51 | are encrypted independently of each other. Cipher types are |
52 | defined in ssh.h. */ | 52 | defined in ssh.h. */ |
53 | void packet_set_encryption_key(const unsigned char *key, unsigned int keylen, | 53 | void |
54 | int cipher_type); | 54 | packet_set_encryption_key(const unsigned char *key, unsigned int keylen, |
55 | int cipher_type); | ||
55 | 56 | ||
56 | /* Sets remote side protocol flags for the current connection. This can | 57 | /* Sets remote side protocol flags for the current connection. This can |
57 | be called at any time. */ | 58 | be called at any time. */ |
58 | void packet_set_protocol_flags(unsigned int flags); | 59 | void packet_set_protocol_flags(unsigned int flags); |
59 | 60 | ||
60 | /* Returns the remote protocol flags set earlier by the above function. */ | 61 | /* Returns the remote protocol flags set earlier by the above function. */ |
61 | unsigned int packet_get_protocol_flags(void); | 62 | unsigned int packet_get_protocol_flags(void); |
62 | 63 | ||
63 | /* Enables compression in both directions starting from the next packet. */ | 64 | /* Enables compression in both directions starting from the next packet. */ |
64 | void packet_start_compression(int level); | 65 | void packet_start_compression(int level); |
65 | 66 | ||
66 | /* Informs that the current session is interactive. Sets IP flags for optimal | 67 | /* Informs that the current session is interactive. Sets IP flags for optimal |
67 | performance in interactive use. */ | 68 | performance in interactive use. */ |
68 | void packet_set_interactive(int interactive, int keepalives); | 69 | void packet_set_interactive(int interactive, int keepalives); |
69 | 70 | ||
70 | /* Returns true if the current connection is interactive. */ | 71 | /* Returns true if the current connection is interactive. */ |
71 | int packet_is_interactive(void); | 72 | int packet_is_interactive(void); |
72 | 73 | ||
73 | /* Starts constructing a packet to send. */ | 74 | /* Starts constructing a packet to send. */ |
74 | void packet_start(int type); | 75 | void packet_start(int type); |
75 | 76 | ||
76 | /* Appends a character to the packet data. */ | 77 | /* Appends a character to the packet data. */ |
77 | void packet_put_char(int ch); | 78 | void packet_put_char(int ch); |
78 | 79 | ||
79 | /* Appends an integer to the packet data. */ | 80 | /* Appends an integer to the packet data. */ |
80 | void packet_put_int(unsigned int value); | 81 | void packet_put_int(unsigned int value); |
81 | 82 | ||
82 | /* Appends an arbitrary precision integer to packet data. */ | 83 | /* Appends an arbitrary precision integer to packet data. */ |
83 | void packet_put_bignum(BIGNUM *value); | 84 | void packet_put_bignum(BIGNUM * value); |
84 | 85 | ||
85 | /* Appends a string to packet data. */ | 86 | /* Appends a string to packet data. */ |
86 | void packet_put_string(const char *buf, unsigned int len); | 87 | void packet_put_string(const char *buf, unsigned int len); |
87 | 88 | ||
88 | /* Finalizes and sends the packet. If the encryption key has been set, | 89 | /* Finalizes and sends the packet. If the encryption key has been set, |
89 | encrypts the packet before sending. */ | 90 | encrypts the packet before sending. */ |
90 | void packet_send(void); | 91 | void packet_send(void); |
91 | 92 | ||
92 | /* Waits until a packet has been received, and returns its type. */ | 93 | /* Waits until a packet has been received, and returns its type. */ |
93 | int packet_read(int *payload_len_ptr); | 94 | int packet_read(int *payload_len_ptr); |
94 | 95 | ||
95 | /* Waits until a packet has been received, verifies that its type matches | 96 | /* Waits until a packet has been received, verifies that its type matches |
96 | that given, and gives a fatal error and exits if there is a mismatch. */ | 97 | that given, and gives a fatal error and exits if there is a mismatch. */ |
97 | void packet_read_expect(int *payload_len_ptr, int type); | 98 | void packet_read_expect(int *payload_len_ptr, int type); |
98 | 99 | ||
99 | /* Checks if a full packet is available in the data received so far via | 100 | /* Checks if a full packet is available in the data received so far via |
100 | packet_process_incoming. If so, reads the packet; otherwise returns | 101 | packet_process_incoming. If so, reads the packet; otherwise returns |
101 | SSH_MSG_NONE. This does not wait for data from the connection. | 102 | SSH_MSG_NONE. This does not wait for data from the connection. |
102 | 103 | ||
103 | SSH_MSG_DISCONNECT is handled specially here. Also, | 104 | SSH_MSG_DISCONNECT is handled specially here. Also, |
104 | SSH_MSG_IGNORE messages are skipped by this function and are never returned | 105 | SSH_MSG_IGNORE messages are skipped by this function and are never returned |
105 | to higher levels. */ | 106 | to higher levels. */ |
106 | int packet_read_poll(int *packet_len_ptr); | 107 | int packet_read_poll(int *packet_len_ptr); |
107 | 108 | ||
108 | /* Buffers the given amount of input characters. This is intended to be | 109 | /* Buffers the given amount of input characters. This is intended to be |
109 | used together with packet_read_poll. */ | 110 | used together with packet_read_poll. */ |
110 | void packet_process_incoming(const char *buf, unsigned int len); | 111 | void packet_process_incoming(const char *buf, unsigned int len); |
111 | 112 | ||
112 | /* Returns a character (0-255) from the packet data. */ | 113 | /* Returns a character (0-255) from the packet data. */ |
113 | unsigned int packet_get_char(void); | 114 | unsigned int packet_get_char(void); |
@@ -117,19 +118,19 @@ unsigned int packet_get_int(void); | |||
117 | 118 | ||
118 | /* Returns an arbitrary precision integer from the packet data. The integer | 119 | /* Returns an arbitrary precision integer from the packet data. The integer |
119 | must have been initialized before this call. */ | 120 | must have been initialized before this call. */ |
120 | void packet_get_bignum(BIGNUM *value, int *length_ptr); | 121 | void packet_get_bignum(BIGNUM * value, int *length_ptr); |
121 | 122 | ||
122 | /* Returns a string from the packet data. The string is allocated using | 123 | /* Returns a string from the packet data. The string is allocated using |
123 | xmalloc; it is the responsibility of the calling program to free it when | 124 | xmalloc; it is the responsibility of the calling program to free it when |
124 | no longer needed. The length_ptr argument may be NULL, or point to an | 125 | no longer needed. The length_ptr argument may be NULL, or point to an |
125 | integer into which the length of the string is stored. */ | 126 | integer into which the length of the string is stored. */ |
126 | char *packet_get_string(unsigned int *length_ptr); | 127 | char *packet_get_string(unsigned int *length_ptr); |
127 | 128 | ||
128 | /* Logs the error in syslog using LOG_INFO, constructs and sends a disconnect | 129 | /* Logs the error in syslog using LOG_INFO, constructs and sends a disconnect |
129 | packet, closes the connection, and exits. This function never returns. | 130 | packet, closes the connection, and exits. This function never returns. |
130 | The error message should not contain a newline. The total length of the | 131 | The error message should not contain a newline. The total length of the |
131 | message must not exceed 1024 bytes. */ | 132 | message must not exceed 1024 bytes. */ |
132 | void packet_disconnect(const char *fmt, ...); | 133 | void packet_disconnect(const char *fmt,...); |
133 | 134 | ||
134 | /* Sends a diagnostic message to the other side. This message | 135 | /* Sends a diagnostic message to the other side. This message |
135 | can be sent at any time (but not while constructing another message). | 136 | can be sent at any time (but not while constructing another message). |
@@ -139,31 +140,31 @@ void packet_disconnect(const char *fmt, ...); | |||
139 | must not exceed 1024 bytes. This will automatically call | 140 | must not exceed 1024 bytes. This will automatically call |
140 | packet_write_wait. If the remote side protocol flags do not indicate | 141 | packet_write_wait. If the remote side protocol flags do not indicate |
141 | that it supports SSH_MSG_DEBUG, this will do nothing. */ | 142 | that it supports SSH_MSG_DEBUG, this will do nothing. */ |
142 | void packet_send_debug(const char *fmt, ...); | 143 | void packet_send_debug(const char *fmt,...); |
143 | 144 | ||
144 | /* Checks if there is any buffered output, and tries to write some of the | 145 | /* Checks if there is any buffered output, and tries to write some of the |
145 | output. */ | 146 | output. */ |
146 | void packet_write_poll(void); | 147 | void packet_write_poll(void); |
147 | 148 | ||
148 | /* Waits until all pending output data has been written. */ | 149 | /* Waits until all pending output data has been written. */ |
149 | void packet_write_wait(void); | 150 | void packet_write_wait(void); |
150 | 151 | ||
151 | /* Returns true if there is buffered data to write to the connection. */ | 152 | /* Returns true if there is buffered data to write to the connection. */ |
152 | int packet_have_data_to_write(void); | 153 | int packet_have_data_to_write(void); |
153 | 154 | ||
154 | /* Returns true if there is not too much data to write to the connection. */ | 155 | /* Returns true if there is not too much data to write to the connection. */ |
155 | int packet_not_very_much_data_to_write(void); | 156 | int packet_not_very_much_data_to_write(void); |
156 | 157 | ||
157 | /* maximum packet size, requested by client with SSH_CMSG_MAX_PACKET_SIZE */ | 158 | /* maximum packet size, requested by client with SSH_CMSG_MAX_PACKET_SIZE */ |
158 | extern int max_packet_size; | 159 | extern int max_packet_size; |
159 | int packet_set_maxsize(int s); | 160 | int packet_set_maxsize(int s); |
160 | #define packet_get_maxsize() max_packet_size | 161 | #define packet_get_maxsize() max_packet_size |
161 | 162 | ||
162 | /* Stores tty modes from the fd into current packet. */ | 163 | /* Stores tty modes from the fd into current packet. */ |
163 | void tty_make_modes(int fd); | 164 | void tty_make_modes(int fd); |
164 | 165 | ||
165 | /* Parses tty modes for the fd from the current packet. */ | 166 | /* Parses tty modes for the fd from the current packet. */ |
166 | void tty_parse_modes(int fd, int *n_bytes_ptr); | 167 | void tty_parse_modes(int fd, int *n_bytes_ptr); |
167 | 168 | ||
168 | #define packet_integrity_check(payload_len, expected_len, type) \ | 169 | #define packet_integrity_check(payload_len, expected_len, type) \ |
169 | do { \ | 170 | do { \ |
@@ -175,4 +176,4 @@ do { \ | |||
175 | } \ | 176 | } \ |
176 | } while (0) | 177 | } while (0) |
177 | 178 | ||
178 | #endif /* PACKET_H */ | 179 | #endif /* PACKET_H */ |
@@ -1,28 +1,28 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * | |
3 | pty.c | 3 | * pty.c |
4 | 4 | * | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
6 | 6 | * | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
8 | All rights reserved | 8 | * All rights reserved |
9 | 9 | * | |
10 | Created: Fri Mar 17 04:37:25 1995 ylo | 10 | * Created: Fri Mar 17 04:37:25 1995 ylo |
11 | 11 | * | |
12 | Allocating a pseudo-terminal, and making it the controlling tty. | 12 | * Allocating a pseudo-terminal, and making it the controlling tty. |
13 | 13 | * | |
14 | */ | 14 | */ |
15 | 15 | ||
16 | #include "includes.h" | 16 | #include "includes.h" |
17 | RCSID("$Id: pty.c,v 1.3 1999/11/15 04:40:55 damien Exp $"); | 17 | RCSID("$Id: pty.c,v 1.4 1999/11/24 13:26:22 damien Exp $"); |
18 | |||
19 | #include "pty.h" | ||
20 | #include "ssh.h" | ||
18 | 21 | ||
19 | #ifdef HAVE_PTY_H | 22 | #ifdef HAVE_PTY_H |
20 | #include <pty.h> | 23 | #include <pty.h> |
21 | #endif /* HAVE_PTY_H */ | 24 | #endif /* HAVE_PTY_H */ |
22 | 25 | ||
23 | #include "pty.h" | ||
24 | #include "ssh.h" | ||
25 | |||
26 | /* Pty allocated with _getpty gets broken if we do I_PUSH:es to it. */ | 26 | /* Pty allocated with _getpty gets broken if we do I_PUSH:es to it. */ |
27 | #if defined(HAVE__GETPTY) || defined(HAVE_OPENPTY) | 27 | #if defined(HAVE__GETPTY) || defined(HAVE_OPENPTY) |
28 | #undef HAVE_DEV_PTMX | 28 | #undef HAVE_DEV_PTMX |
@@ -34,235 +34,211 @@ RCSID("$Id: pty.c,v 1.3 1999/11/15 04:40:55 damien Exp $"); | |||
34 | 34 | ||
35 | /* Allocates and opens a pty. Returns 0 if no pty could be allocated, | 35 | /* Allocates and opens a pty. Returns 0 if no pty could be allocated, |
36 | or nonzero if a pty was successfully allocated. On success, open file | 36 | or nonzero if a pty was successfully allocated. On success, open file |
37 | descriptors for the pty and tty sides and the name of the tty side are | 37 | descriptors for the pty and tty sides and the name of the tty side are |
38 | returned (the buffer must be able to hold at least 64 characters). */ | 38 | returned (the buffer must be able to hold at least 64 characters). */ |
39 | 39 | ||
40 | int pty_allocate(int *ptyfd, int *ttyfd, char *namebuf) | 40 | int |
41 | pty_allocate(int *ptyfd, int *ttyfd, char *namebuf) | ||
41 | { | 42 | { |
42 | #ifdef HAVE_OPENPTY | 43 | #ifdef HAVE_OPENPTY |
44 | /* openpty(3) exists in OSF/1 and some other os'es */ | ||
45 | int i; | ||
43 | 46 | ||
44 | /* openpty(3) exists in OSF/1 and some other os'es */ | 47 | i = openpty(ptyfd, ttyfd, namebuf, NULL, NULL); |
45 | 48 | if (i < 0) { | |
46 | int i; | 49 | error("openpty: %.100s", strerror(errno)); |
47 | 50 | return 0; | |
48 | i = openpty(ptyfd, ttyfd, namebuf, NULL, NULL); | 51 | } |
49 | 52 | return 1; | |
50 | if (i < 0) | ||
51 | { | ||
52 | error("openpty: %.100s", strerror(errno)); | ||
53 | return 0; | ||
54 | } | ||
55 | |||
56 | return 1; | ||
57 | |||
58 | #else /* HAVE_OPENPTY */ | 53 | #else /* HAVE_OPENPTY */ |
59 | #ifdef HAVE__GETPTY | 54 | #ifdef HAVE__GETPTY |
60 | 55 | /* _getpty(3) exists in SGI Irix 4.x, 5.x & 6.x -- it generates | |
61 | /* _getpty(3) exists in SGI Irix 4.x, 5.x & 6.x -- it generates more | 56 | more pty's automagically when needed */ |
62 | pty's automagically when needed */ | 57 | char *slave; |
63 | 58 | ||
64 | char *slave; | 59 | slave = _getpty(ptyfd, O_RDWR, 0622, 0); |
65 | 60 | if (slave == NULL) { | |
66 | slave = _getpty(ptyfd, O_RDWR, 0622, 0); | 61 | error("_getpty: %.100s", strerror(errno)); |
67 | if (slave == NULL) | 62 | return 0; |
68 | { | 63 | } |
69 | error("_getpty: %.100s", strerror(errno)); | 64 | strcpy(namebuf, slave); |
70 | return 0; | 65 | /* Open the slave side. */ |
71 | } | 66 | *ttyfd = open(namebuf, O_RDWR | O_NOCTTY); |
72 | strcpy(namebuf, slave); | 67 | if (*ttyfd < 0) { |
73 | /* Open the slave side. */ | 68 | error("%.200s: %.100s", namebuf, strerror(errno)); |
74 | *ttyfd = open(namebuf, O_RDWR|O_NOCTTY); | 69 | close(*ptyfd); |
75 | if (*ttyfd < 0) | 70 | return 0; |
76 | { | 71 | } |
77 | error("%.200s: %.100s", namebuf, strerror(errno)); | 72 | return 1; |
78 | close(*ptyfd); | ||
79 | return 0; | ||
80 | } | ||
81 | return 1; | ||
82 | |||
83 | #else /* HAVE__GETPTY */ | 73 | #else /* HAVE__GETPTY */ |
84 | #ifdef HAVE_DEV_PTMX | 74 | #ifdef HAVE_DEV_PTMX |
85 | /* This code is used e.g. on Solaris 2.x. (Note that Solaris 2.3 also has | 75 | /* This code is used e.g. on Solaris 2.x. (Note that Solaris 2.3 |
86 | bsd-style ptys, but they simply do not work.) */ | 76 | also has bsd-style ptys, but they simply do not work.) */ |
87 | 77 | int ptm; | |
88 | int ptm; | 78 | char *pts; |
89 | char *pts; | 79 | |
90 | 80 | ptm = open("/dev/ptmx", O_RDWR | O_NOCTTY); | |
91 | ptm = open("/dev/ptmx", O_RDWR|O_NOCTTY); | 81 | if (ptm < 0) { |
92 | if (ptm < 0) | 82 | error("/dev/ptmx: %.100s", strerror(errno)); |
93 | { | 83 | return 0; |
94 | error("/dev/ptmx: %.100s", strerror(errno)); | 84 | } |
95 | return 0; | 85 | if (grantpt(ptm) < 0) { |
96 | } | 86 | error("grantpt: %.100s", strerror(errno)); |
97 | if (grantpt(ptm) < 0) | 87 | return 0; |
98 | { | 88 | } |
99 | error("grantpt: %.100s", strerror(errno)); | 89 | if (unlockpt(ptm) < 0) { |
100 | return 0; | 90 | error("unlockpt: %.100s", strerror(errno)); |
101 | } | 91 | return 0; |
102 | if (unlockpt(ptm) < 0) | 92 | } |
103 | { | 93 | pts = ptsname(ptm); |
104 | error("unlockpt: %.100s", strerror(errno)); | 94 | if (pts == NULL) |
105 | return 0; | 95 | error("Slave pty side name could not be obtained."); |
106 | } | 96 | strcpy(namebuf, pts); |
107 | pts = ptsname(ptm); | 97 | *ptyfd = ptm; |
108 | if (pts == NULL) | 98 | |
109 | error("Slave pty side name could not be obtained."); | 99 | /* Open the slave side. */ |
110 | strcpy(namebuf, pts); | 100 | *ttyfd = open(namebuf, O_RDWR | O_NOCTTY); |
111 | *ptyfd = ptm; | 101 | if (*ttyfd < 0) { |
112 | 102 | error("%.100s: %.100s", namebuf, strerror(errno)); | |
113 | /* Open the slave side. */ | 103 | close(*ptyfd); |
114 | *ttyfd = open(namebuf, O_RDWR|O_NOCTTY); | 104 | return 0; |
115 | if (*ttyfd < 0) | 105 | } |
116 | { | 106 | /* Push the appropriate streams modules, as described in Solaris |
117 | error("%.100s: %.100s", namebuf, strerror(errno)); | 107 | pts(7). */ |
118 | close(*ptyfd); | 108 | if (ioctl(*ttyfd, I_PUSH, "ptem") < 0) |
119 | return 0; | 109 | error("ioctl I_PUSH ptem: %.100s", strerror(errno)); |
120 | } | 110 | if (ioctl(*ttyfd, I_PUSH, "ldterm") < 0) |
121 | /* Push the appropriate streams modules, as described in Solaris pts(7). */ | 111 | error("ioctl I_PUSH ldterm: %.100s", strerror(errno)); |
122 | if (ioctl(*ttyfd, I_PUSH, "ptem") < 0) | 112 | if (ioctl(*ttyfd, I_PUSH, "ttcompat") < 0) |
123 | error("ioctl I_PUSH ptem: %.100s", strerror(errno)); | 113 | error("ioctl I_PUSH ttcompat: %.100s", strerror(errno)); |
124 | if (ioctl(*ttyfd, I_PUSH, "ldterm") < 0) | 114 | return 1; |
125 | error("ioctl I_PUSH ldterm: %.100s", strerror(errno)); | ||
126 | if (ioctl(*ttyfd, I_PUSH, "ttcompat") < 0) | ||
127 | error("ioctl I_PUSH ttcompat: %.100s", strerror(errno)); | ||
128 | return 1; | ||
129 | |||
130 | #else /* HAVE_DEV_PTMX */ | 115 | #else /* HAVE_DEV_PTMX */ |
131 | #ifdef HAVE_DEV_PTS_AND_PTC | 116 | #ifdef HAVE_DEV_PTS_AND_PTC |
117 | /* AIX-style pty code. */ | ||
118 | const char *name; | ||
132 | 119 | ||
133 | /* AIX-style pty code. */ | 120 | *ptyfd = open("/dev/ptc", O_RDWR | O_NOCTTY); |
134 | 121 | if (*ptyfd < 0) { | |
135 | const char *name; | 122 | error("Could not open /dev/ptc: %.100s", strerror(errno)); |
136 | 123 | return 0; | |
137 | *ptyfd = open("/dev/ptc", O_RDWR|O_NOCTTY); | 124 | } |
138 | if (*ptyfd < 0) | 125 | name = ttyname(*ptyfd); |
139 | { | 126 | if (!name) |
140 | error("Could not open /dev/ptc: %.100s", strerror(errno)); | 127 | fatal("Open of /dev/ptc returns device for which ttyname fails."); |
141 | return 0; | 128 | strcpy(namebuf, name); |
142 | } | 129 | *ttyfd = open(name, O_RDWR | O_NOCTTY); |
143 | name = ttyname(*ptyfd); | 130 | if (*ttyfd < 0) { |
144 | if (!name) | 131 | error("Could not open pty slave side %.100s: %.100s", |
145 | fatal("Open of /dev/ptc returns device for which ttyname fails."); | 132 | name, strerror(errno)); |
146 | strcpy(namebuf, name); | 133 | close(*ptyfd); |
147 | *ttyfd = open(name, O_RDWR|O_NOCTTY); | 134 | return 0; |
148 | if (*ttyfd < 0) | 135 | } |
149 | { | 136 | return 1; |
150 | error("Could not open pty slave side %.100s: %.100s", | ||
151 | name, strerror(errno)); | ||
152 | close(*ptyfd); | ||
153 | return 0; | ||
154 | } | ||
155 | return 1; | ||
156 | |||
157 | #else /* HAVE_DEV_PTS_AND_PTC */ | 137 | #else /* HAVE_DEV_PTS_AND_PTC */ |
158 | /* BSD-style pty code. */ | 138 | /* BSD-style pty code. */ |
159 | 139 | char buf[64]; | |
160 | char buf[64]; | 140 | int i; |
161 | int i; | 141 | const char *ptymajors = |
162 | const char *ptymajors = | 142 | "pqrstuvwxyzabcdefghijklmnoABCDEFGHIJKLMNOPQRSTUVWXYZ"; |
163 | "pqrstuvwxyzabcdefghijklmnoABCDEFGHIJKLMNOPQRSTUVWXYZ"; | 143 | const char *ptyminors = "0123456789abcdef"; |
164 | const char *ptyminors = "0123456789abcdef"; | 144 | int num_minors = strlen(ptyminors); |
165 | int num_minors = strlen(ptyminors); | 145 | int num_ptys = strlen(ptymajors) * num_minors; |
166 | int num_ptys = strlen(ptymajors) * num_minors; | 146 | |
167 | 147 | for (i = 0; i < num_ptys; i++) { | |
168 | for (i = 0; i < num_ptys; i++) | 148 | snprintf(buf, sizeof buf, "/dev/pty%c%c", ptymajors[i / num_minors], |
169 | { | 149 | ptyminors[i % num_minors]); |
170 | snprintf(buf, sizeof buf, "/dev/pty%c%c", ptymajors[i / num_minors], | 150 | *ptyfd = open(buf, O_RDWR | O_NOCTTY); |
171 | ptyminors[i % num_minors]); | 151 | if (*ptyfd < 0) |
172 | *ptyfd = open(buf, O_RDWR|O_NOCTTY); | 152 | continue; |
173 | if (*ptyfd < 0) | 153 | snprintf(namebuf, sizeof buf, "/dev/tty%c%c", ptymajors[i / num_minors], |
174 | continue; | 154 | ptyminors[i % num_minors]); |
175 | snprintf(namebuf, sizeof buf, "/dev/tty%c%c", ptymajors[i / num_minors], | 155 | |
176 | ptyminors[i % num_minors]); | 156 | /* Open the slave side. */ |
177 | 157 | *ttyfd = open(namebuf, O_RDWR | O_NOCTTY); | |
178 | /* Open the slave side. */ | 158 | if (*ttyfd < 0) { |
179 | *ttyfd = open(namebuf, O_RDWR|O_NOCTTY); | 159 | error("%.100s: %.100s", namebuf, strerror(errno)); |
180 | if (*ttyfd < 0) | 160 | close(*ptyfd); |
181 | { | 161 | return 0; |
182 | error("%.100s: %.100s", namebuf, strerror(errno)); | 162 | } |
183 | close(*ptyfd); | 163 | return 1; |
184 | return 0; | ||
185 | } | 164 | } |
186 | return 1; | 165 | return 0; |
187 | } | ||
188 | return 0; | ||
189 | #endif /* HAVE_DEV_PTS_AND_PTC */ | 166 | #endif /* HAVE_DEV_PTS_AND_PTC */ |
190 | #endif /* HAVE_DEV_PTMX */ | 167 | #endif /* HAVE_DEV_PTMX */ |
191 | #endif /* HAVE__GETPTY */ | 168 | #endif /* HAVE__GETPTY */ |
192 | #endif /* HAVE_OPENPTY */ | 169 | #endif /* HAVE_OPENPTY */ |
193 | } | 170 | } |
194 | 171 | ||
195 | /* Releases the tty. Its ownership is returned to root, and permissions to | 172 | /* Releases the tty. Its ownership is returned to root, and permissions to 0666. */ |
196 | 0666. */ | ||
197 | 173 | ||
198 | void pty_release(const char *ttyname) | 174 | void |
175 | pty_release(const char *ttyname) | ||
199 | { | 176 | { |
200 | if (chown(ttyname, (uid_t)0, (gid_t)0) < 0) | 177 | if (chown(ttyname, (uid_t) 0, (gid_t) 0) < 0) |
201 | debug("chown %.100s 0 0 failed: %.100s", ttyname, strerror(errno)); | 178 | debug("chown %.100s 0 0 failed: %.100s", ttyname, strerror(errno)); |
202 | if (chmod(ttyname, (mode_t)0666) < 0) | 179 | if (chmod(ttyname, (mode_t) 0666) < 0) |
203 | debug("chmod %.100s 0666 failed: %.100s", ttyname, strerror(errno)); | 180 | debug("chmod %.100s 0666 failed: %.100s", ttyname, strerror(errno)); |
204 | } | 181 | } |
205 | 182 | ||
206 | /* Makes the tty the processes controlling tty and sets it to sane modes. */ | 183 | /* Makes the tty the processes controlling tty and sets it to sane modes. */ |
207 | 184 | ||
208 | void pty_make_controlling_tty(int *ttyfd, const char *ttyname) | 185 | void |
186 | pty_make_controlling_tty(int *ttyfd, const char *ttyname) | ||
209 | { | 187 | { |
210 | int fd; | 188 | int fd; |
211 | 189 | ||
212 | /* First disconnect from the old controlling tty. */ | 190 | /* First disconnect from the old controlling tty. */ |
213 | #ifdef TIOCNOTTY | 191 | #ifdef TIOCNOTTY |
214 | fd = open("/dev/tty", O_RDWR|O_NOCTTY); | 192 | fd = open("/dev/tty", O_RDWR | O_NOCTTY); |
215 | if (fd >= 0) | 193 | if (fd >= 0) { |
216 | { | 194 | (void) ioctl(fd, TIOCNOTTY, NULL); |
217 | (void)ioctl(fd, TIOCNOTTY, NULL); | 195 | close(fd); |
218 | close(fd); | 196 | } |
219 | } | ||
220 | #endif /* TIOCNOTTY */ | 197 | #endif /* TIOCNOTTY */ |
221 | if (setsid() < 0) | 198 | if (setsid() < 0) |
222 | error("setsid: %.100s", strerror(errno)); | 199 | error("setsid: %.100s", strerror(errno)); |
223 | 200 | ||
224 | /* Verify that we are successfully disconnected from the controlling tty. */ | 201 | /* Verify that we are successfully disconnected from the |
225 | fd = open("/dev/tty", O_RDWR|O_NOCTTY); | 202 | controlling tty. */ |
226 | if (fd >= 0) | 203 | fd = open("/dev/tty", O_RDWR | O_NOCTTY); |
227 | { | 204 | if (fd >= 0) { |
228 | error("Failed to disconnect from controlling tty."); | 205 | error("Failed to disconnect from controlling tty."); |
229 | close(fd); | 206 | close(fd); |
230 | } | 207 | } |
231 | 208 | /* Make it our controlling tty. */ | |
232 | /* Make it our controlling tty. */ | ||
233 | #ifdef TIOCSCTTY | 209 | #ifdef TIOCSCTTY |
234 | debug("Setting controlling tty using TIOCSCTTY."); | 210 | debug("Setting controlling tty using TIOCSCTTY."); |
235 | /* We ignore errors from this, because HPSUX defines TIOCSCTTY, but returns | 211 | /* We ignore errors from this, because HPSUX defines TIOCSCTTY, |
236 | EINVAL with these arguments, and there is absolutely no documentation. */ | 212 | but returns EINVAL with these arguments, and there is |
237 | ioctl(*ttyfd, TIOCSCTTY, NULL); | 213 | absolutely no documentation. */ |
214 | ioctl(*ttyfd, TIOCSCTTY, NULL); | ||
238 | #endif /* TIOCSCTTY */ | 215 | #endif /* TIOCSCTTY */ |
239 | fd = open(ttyname, O_RDWR); | 216 | fd = open(ttyname, O_RDWR); |
240 | if (fd < 0) | 217 | if (fd < 0) |
241 | error("%.100s: %.100s", ttyname, strerror(errno)); | 218 | error("%.100s: %.100s", ttyname, strerror(errno)); |
242 | else | 219 | else |
243 | close(fd); | 220 | close(fd); |
244 | 221 | ||
245 | /* Verify that we now have a controlling tty. */ | 222 | /* Verify that we now have a controlling tty. */ |
246 | fd = open("/dev/tty", O_WRONLY); | 223 | fd = open("/dev/tty", O_WRONLY); |
247 | if (fd < 0) | 224 | if (fd < 0) |
248 | error("open /dev/tty failed - could not set controlling tty: %.100s", | 225 | error("open /dev/tty failed - could not set controlling tty: %.100s", |
249 | strerror(errno)); | 226 | strerror(errno)); |
250 | else | 227 | else { |
251 | { | 228 | close(fd); |
252 | close(fd); | 229 | } |
253 | } | ||
254 | } | 230 | } |
255 | 231 | ||
256 | /* Changes the window size associated with the pty. */ | 232 | /* Changes the window size associated with the pty. */ |
257 | 233 | ||
258 | void pty_change_window_size(int ptyfd, int row, int col, | 234 | void |
259 | int xpixel, int ypixel) | 235 | pty_change_window_size(int ptyfd, int row, int col, |
236 | int xpixel, int ypixel) | ||
260 | { | 237 | { |
261 | struct winsize w; | 238 | struct winsize w; |
262 | w.ws_row = row; | 239 | w.ws_row = row; |
263 | w.ws_col = col; | 240 | w.ws_col = col; |
264 | w.ws_xpixel = xpixel; | 241 | w.ws_xpixel = xpixel; |
265 | w.ws_ypixel = ypixel; | 242 | w.ws_ypixel = ypixel; |
266 | (void)ioctl(ptyfd, TIOCSWINSZ, &w); | 243 | (void) ioctl(ptyfd, TIOCSWINSZ, &w); |
267 | } | 244 | } |
268 | |||
@@ -1,40 +1,40 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * | |
3 | pty.h | 3 | * pty.h |
4 | 4 | * | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
6 | 6 | * | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
8 | All rights reserved | 8 | * All rights reserved |
9 | 9 | * | |
10 | Created: Fri Mar 17 05:03:28 1995 ylo | 10 | * Created: Fri Mar 17 05:03:28 1995 ylo |
11 | 11 | * | |
12 | Functions for allocating a pseudo-terminal and making it the controlling | 12 | * Functions for allocating a pseudo-terminal and making it the controlling |
13 | tty. | 13 | * tty. |
14 | 14 | */ | |
15 | */ | 15 | |
16 | 16 | /* RCSID("$Id: pty.h,v 1.2 1999/11/24 13:26:22 damien Exp $"); */ | |
17 | /* RCSID("$Id: pty.h,v 1.1 1999/10/27 03:42:44 damien Exp $"); */ | ||
18 | 17 | ||
19 | #ifndef PTY_H | 18 | #ifndef PTY_H |
20 | #define PTY_H | 19 | #define PTY_H |
21 | 20 | ||
22 | /* Allocates and opens a pty. Returns 0 if no pty could be allocated, | 21 | /* Allocates and opens a pty. Returns 0 if no pty could be allocated, |
23 | or nonzero if a pty was successfully allocated. On success, open file | 22 | or nonzero if a pty was successfully allocated. On success, open file |
24 | descriptors for the pty and tty sides and the name of the tty side are | 23 | descriptors for the pty and tty sides and the name of the tty side are |
25 | returned (the buffer must be able to hold at least 64 characters). */ | 24 | returned (the buffer must be able to hold at least 64 characters). */ |
26 | int pty_allocate(int *ptyfd, int *ttyfd, char *ttyname); | 25 | int pty_allocate(int *ptyfd, int *ttyfd, char *ttyname); |
27 | 26 | ||
28 | /* Releases the tty. Its ownership is returned to root, and permissions to | 27 | /* Releases the tty. Its ownership is returned to root, and permissions to |
29 | 0666. */ | 28 | 0666. */ |
30 | void pty_release(const char *ttyname); | 29 | void pty_release(const char *ttyname); |
31 | 30 | ||
32 | /* Makes the tty the processes controlling tty and sets it to sane modes. | 31 | /* Makes the tty the processes controlling tty and sets it to sane modes. |
33 | This may need to reopen the tty to get rid of possible eavesdroppers. */ | 32 | This may need to reopen the tty to get rid of possible eavesdroppers. */ |
34 | void pty_make_controlling_tty(int *ttyfd, const char *ttyname); | 33 | void pty_make_controlling_tty(int *ttyfd, const char *ttyname); |
35 | 34 | ||
36 | /* Changes the window size associated with the pty. */ | 35 | /* Changes the window size associated with the pty. */ |
37 | void pty_change_window_size(int ptyfd, int row, int col, | 36 | void |
38 | int xpixel, int ypixel); | 37 | pty_change_window_size(int ptyfd, int row, int col, |
38 | int xpixel, int ypixel); | ||
39 | 39 | ||
40 | #endif /* PTY_H */ | 40 | #endif /* PTY_H */ |
@@ -1,101 +1,105 @@ | |||
1 | /* | 1 | /* |
2 | radix.c | 2 | * radix.c |
3 | * | ||
4 | * base-64 encoding pinched from lynx2-7-2, who pinched it from rpem. | ||
5 | * Originally written by Mark Riordan 12 August 1990 and 17 Feb 1991 | ||
6 | * and placed in the public domain. | ||
7 | * | ||
8 | * Dug Song <dugsong@UMICH.EDU> | ||
9 | */ | ||
3 | 10 | ||
4 | base-64 encoding pinched from lynx2-7-2, who pinched it from rpem. | ||
5 | Originally written by Mark Riordan 12 August 1990 and 17 Feb 1991 | ||
6 | and placed in the public domain. | ||
7 | |||
8 | Dug Song <dugsong@UMICH.EDU> | ||
9 | */ | ||
10 | |||
11 | #include "includes.h" | 11 | #include "includes.h" |
12 | 12 | ||
13 | #ifdef AFS | 13 | #ifdef AFS |
14 | #include <krb.h> | 14 | #include <krb.h> |
15 | 15 | ||
16 | char six2pr[64] = { | 16 | char six2pr[64] = { |
17 | 'A','B','C','D','E','F','G','H','I','J','K','L','M', | 17 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', |
18 | 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z', | 18 | 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', |
19 | 'a','b','c','d','e','f','g','h','i','j','k','l','m', | 19 | 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', |
20 | 'n','o','p','q','r','s','t','u','v','w','x','y','z', | 20 | 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', |
21 | '0','1','2','3','4','5','6','7','8','9','+','/' | 21 | '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' |
22 | }; | 22 | }; |
23 | 23 | ||
24 | unsigned char pr2six[256]; | 24 | unsigned char pr2six[256]; |
25 | 25 | ||
26 | int uuencode(unsigned char *bufin, unsigned int nbytes, char *bufcoded) | 26 | int |
27 | uuencode(unsigned char *bufin, unsigned int nbytes, char *bufcoded) | ||
27 | { | 28 | { |
28 | /* ENC is the basic 1 character encoding function to make a char printing */ | 29 | /* ENC is the basic 1 character encoding function to make a char printing */ |
29 | #define ENC(c) six2pr[c] | 30 | #define ENC(c) six2pr[c] |
30 | 31 | ||
31 | register char *outptr = bufcoded; | 32 | register char *outptr = bufcoded; |
32 | unsigned int i; | 33 | unsigned int i; |
33 | 34 | ||
34 | for (i=0; i<nbytes; i += 3) { | 35 | for (i = 0; i < nbytes; i += 3) { |
35 | *(outptr++) = ENC(*bufin >> 2); /* c1 */ | 36 | *(outptr++) = ENC(*bufin >> 2); /* c1 */ |
36 | *(outptr++) = ENC(((*bufin << 4) & 060) | ((bufin[1] >> 4) & 017)); /*c2*/ | 37 | *(outptr++) = ENC(((*bufin << 4) & 060) | ((bufin[1] >> 4) & 017)); /* c2 */ |
37 | *(outptr++) = ENC(((bufin[1] << 2) & 074) | ((bufin[2] >> 6) & 03));/*c3*/ | 38 | *(outptr++) = ENC(((bufin[1] << 2) & 074) | ((bufin[2] >> 6) & 03)); /* c3 */ |
38 | *(outptr++) = ENC(bufin[2] & 077); /* c4 */ | 39 | *(outptr++) = ENC(bufin[2] & 077); /* c4 */ |
39 | bufin += 3; | 40 | bufin += 3; |
40 | } | 41 | } |
41 | if (i == nbytes+1) { | 42 | if (i == nbytes + 1) { |
42 | outptr[-1] = '='; | 43 | outptr[-1] = '='; |
43 | } else if (i == nbytes+2) { | 44 | } else if (i == nbytes + 2) { |
44 | outptr[-1] = '='; | 45 | outptr[-1] = '='; |
45 | outptr[-2] = '='; | 46 | outptr[-2] = '='; |
46 | } | 47 | } |
47 | *outptr = '\0'; | 48 | *outptr = '\0'; |
48 | return(outptr - bufcoded); | 49 | return (outptr - bufcoded); |
49 | } | 50 | } |
50 | 51 | ||
51 | int uudecode(const char *bufcoded, unsigned char *bufplain, int outbufsize) | 52 | int |
53 | uudecode(const char *bufcoded, unsigned char *bufplain, int outbufsize) | ||
52 | { | 54 | { |
53 | /* single character decode */ | 55 | /* single character decode */ |
54 | #define DEC(c) pr2six[(unsigned char)c] | 56 | #define DEC(c) pr2six[(unsigned char)c] |
55 | #define MAXVAL 63 | 57 | #define MAXVAL 63 |
56 | 58 | ||
57 | static int first = 1; | 59 | static int first = 1; |
58 | int nbytesdecoded, j; | 60 | int nbytesdecoded, j; |
59 | const char *bufin = bufcoded; | 61 | const char *bufin = bufcoded; |
60 | register unsigned char *bufout = bufplain; | 62 | register unsigned char *bufout = bufplain; |
61 | register int nprbytes; | 63 | register int nprbytes; |
62 | 64 | ||
63 | /* If this is the first call, initialize the mapping table. */ | 65 | /* If this is the first call, initialize the mapping table. */ |
64 | if (first) { | 66 | if (first) { |
65 | first = 0; | 67 | first = 0; |
66 | for(j=0; j<256; j++) pr2six[j] = MAXVAL+1; | 68 | for (j = 0; j < 256; j++) |
67 | for(j=0; j<64; j++) pr2six[(unsigned char)six2pr[j]] = (unsigned char)j; | 69 | pr2six[j] = MAXVAL + 1; |
68 | } | 70 | for (j = 0; j < 64; j++) |
69 | 71 | pr2six[(unsigned char) six2pr[j]] = (unsigned char) j; | |
70 | /* Strip leading whitespace. */ | 72 | } |
71 | while (*bufcoded==' ' || *bufcoded == '\t') bufcoded++; | 73 | /* Strip leading whitespace. */ |
72 | 74 | while (*bufcoded == ' ' || *bufcoded == '\t') | |
73 | /* Figure out how many characters are in the input buffer. | 75 | bufcoded++; |
74 | If this would decode into more bytes than would fit into | 76 | |
75 | the output buffer, adjust the number of input bytes downwards. */ | 77 | /* Figure out how many characters are in the input buffer. If this |
76 | bufin = bufcoded; | 78 | would decode into more bytes than would fit into the output |
77 | while (DEC(*(bufin++)) <= MAXVAL); | 79 | buffer, adjust the number of input bytes downwards. */ |
78 | nprbytes = bufin - bufcoded - 1; | 80 | bufin = bufcoded; |
79 | nbytesdecoded = ((nprbytes+3)/4) * 3; | 81 | while (DEC(*(bufin++)) <= MAXVAL); |
80 | if (nbytesdecoded > outbufsize) | 82 | nprbytes = bufin - bufcoded - 1; |
81 | nprbytes = (outbufsize*4)/3; | 83 | nbytesdecoded = ((nprbytes + 3) / 4) * 3; |
82 | 84 | if (nbytesdecoded > outbufsize) | |
83 | bufin = bufcoded; | 85 | nprbytes = (outbufsize * 4) / 3; |
84 | 86 | ||
85 | while (nprbytes > 0) { | 87 | bufin = bufcoded; |
86 | *(bufout++) = (unsigned char) (DEC(*bufin) << 2 | DEC(bufin[1]) >> 4); | 88 | |
87 | *(bufout++) = (unsigned char) (DEC(bufin[1]) << 4 | DEC(bufin[2]) >> 2); | 89 | while (nprbytes > 0) { |
88 | *(bufout++) = (unsigned char) (DEC(bufin[2]) << 6 | DEC(bufin[3])); | 90 | *(bufout++) = (unsigned char) (DEC(*bufin) << 2 | DEC(bufin[1]) >> 4); |
89 | bufin += 4; | 91 | *(bufout++) = (unsigned char) (DEC(bufin[1]) << 4 | DEC(bufin[2]) >> 2); |
90 | nprbytes -= 4; | 92 | *(bufout++) = (unsigned char) (DEC(bufin[2]) << 6 | DEC(bufin[3])); |
91 | } | 93 | bufin += 4; |
92 | if (nprbytes & 03) { | 94 | nprbytes -= 4; |
93 | if (DEC(bufin[-2]) > MAXVAL) | 95 | } |
94 | nbytesdecoded -= 2; | 96 | if (nprbytes & 03) { |
95 | else | 97 | if (DEC(bufin[-2]) > MAXVAL) |
96 | nbytesdecoded -= 1; | 98 | nbytesdecoded -= 2; |
97 | } | 99 | else |
98 | return(nbytesdecoded); | 100 | nbytesdecoded -= 1; |
101 | } | ||
102 | return (nbytesdecoded); | ||
99 | } | 103 | } |
100 | 104 | ||
101 | typedef unsigned char my_u_char; | 105 | typedef unsigned char my_u_char; |
@@ -156,103 +160,124 @@ typedef unsigned short my_u_short; | |||
156 | } | 160 | } |
157 | 161 | ||
158 | 162 | ||
159 | int creds_to_radix(CREDENTIALS *creds, unsigned char *buf) | 163 | int |
164 | creds_to_radix(CREDENTIALS *creds, unsigned char *buf) | ||
160 | { | 165 | { |
161 | char *p, *s; | 166 | char *p, *s; |
162 | int len; | 167 | int len; |
163 | char temp[2048]; | 168 | char temp[2048]; |
164 | 169 | ||
165 | p = temp; | 170 | p = temp; |
166 | *p++ = 1; /* version */ | 171 | *p++ = 1; /* version */ |
167 | s = creds->service; while (*s) *p++ = *s++; *p++ = *s; | 172 | s = creds->service; |
168 | s = creds->instance; while (*s) *p++ = *s++; *p++ = *s; | 173 | while (*s) |
169 | s = creds->realm; while (*s) *p++ = *s++; *p++ = *s; | 174 | *p++ = *s++; |
170 | 175 | *p++ = *s; | |
171 | s = creds->pname; while (*s) *p++ = *s++; *p++ = *s; | 176 | s = creds->instance; |
172 | s = creds->pinst; while (*s) *p++ = *s++; *p++ = *s; | 177 | while (*s) |
173 | /* Null string to repeat the realm. */ | 178 | *p++ = *s++; |
174 | *p++ = '\0'; | 179 | *p++ = *s; |
175 | 180 | s = creds->realm; | |
176 | PUTLONG(creds->issue_date,p); | 181 | while (*s) |
177 | { | 182 | *p++ = *s++; |
178 | unsigned int endTime ; | 183 | *p++ = *s; |
179 | endTime = (unsigned int)krb_life_to_time(creds->issue_date, | 184 | |
180 | creds->lifetime); | 185 | s = creds->pname; |
181 | PUTLONG(endTime,p); | 186 | while (*s) |
182 | } | 187 | *p++ = *s++; |
183 | 188 | *p++ = *s; | |
184 | memcpy(p,&creds->session, sizeof(creds->session)); | 189 | s = creds->pinst; |
185 | p += sizeof(creds->session); | 190 | while (*s) |
186 | 191 | *p++ = *s++; | |
187 | PUTSHORT(creds->kvno,p); | 192 | *p++ = *s; |
188 | PUTLONG(creds->ticket_st.length,p); | 193 | /* Null string to repeat the realm. */ |
189 | 194 | *p++ = '\0'; | |
190 | memcpy(p,creds->ticket_st.dat, creds->ticket_st.length); | 195 | |
191 | p += creds->ticket_st.length; | 196 | PUTLONG(creds->issue_date, p); |
192 | len = p - temp; | 197 | { |
193 | 198 | unsigned int endTime; | |
194 | return(uuencode(temp, len, buf)); | 199 | endTime = (unsigned int) krb_life_to_time(creds->issue_date, |
200 | creds->lifetime); | ||
201 | PUTLONG(endTime, p); | ||
202 | } | ||
203 | |||
204 | memcpy(p, &creds->session, sizeof(creds->session)); | ||
205 | p += sizeof(creds->session); | ||
206 | |||
207 | PUTSHORT(creds->kvno, p); | ||
208 | PUTLONG(creds->ticket_st.length, p); | ||
209 | |||
210 | memcpy(p, creds->ticket_st.dat, creds->ticket_st.length); | ||
211 | p += creds->ticket_st.length; | ||
212 | len = p - temp; | ||
213 | |||
214 | return (uuencode(temp, len, buf)); | ||
195 | } | 215 | } |
196 | 216 | ||
197 | int radix_to_creds(const char *buf, CREDENTIALS *creds) | 217 | int |
218 | radix_to_creds(const char *buf, CREDENTIALS *creds) | ||
198 | { | 219 | { |
199 | 220 | ||
200 | char *p; | 221 | char *p; |
201 | int len, tl; | 222 | int len, tl; |
202 | char version; | 223 | char version; |
203 | char temp[2048]; | 224 | char temp[2048]; |
204 | 225 | ||
205 | if (!(len = uudecode(buf, temp, sizeof(temp)))) | 226 | if (!(len = uudecode(buf, temp, sizeof(temp)))) |
206 | return 0; | 227 | return 0; |
207 | 228 | ||
208 | p = temp; | 229 | p = temp; |
209 | 230 | ||
210 | /* check version and length! */ | 231 | /* check version and length! */ |
211 | if (len < 1) return 0; | 232 | if (len < 1) |
212 | version = *p; p++; len--; | 233 | return 0; |
213 | 234 | version = *p; | |
214 | GETSTRING(creds->service, p, len); | 235 | p++; |
215 | GETSTRING(creds->instance, p, len); | 236 | len--; |
216 | GETSTRING(creds->realm, p, len); | 237 | |
217 | 238 | GETSTRING(creds->service, p, len); | |
218 | GETSTRING(creds->pname, p, len); | 239 | GETSTRING(creds->instance, p, len); |
219 | GETSTRING(creds->pinst, p, len); | 240 | GETSTRING(creds->realm, p, len); |
220 | /* Ignore possibly different realm. */ | 241 | |
221 | while (*p && len) p++, len--; | 242 | GETSTRING(creds->pname, p, len); |
222 | if (len == 0) return 0; | 243 | GETSTRING(creds->pinst, p, len); |
223 | p++, len--; | 244 | /* Ignore possibly different realm. */ |
224 | 245 | while (*p && len) | |
225 | /* Enough space for remaining fixed-length parts? */ | 246 | p++, len--; |
226 | if (len < (4 + 4 + sizeof(creds->session) + 2 + 4)) | 247 | if (len == 0) |
227 | return 0; | 248 | return 0; |
228 | 249 | p++, len--; | |
229 | GETLONG(creds->issue_date,p); | ||
230 | len -= 4; | ||
231 | { | ||
232 | unsigned int endTime; | ||
233 | GETLONG(endTime,p); | ||
234 | len -= 4; | ||
235 | creds->lifetime = krb_time_to_life(creds->issue_date, endTime); | ||
236 | } | ||
237 | |||
238 | memcpy(&creds->session, p, sizeof(creds->session)); | ||
239 | p += sizeof(creds->session); | ||
240 | len -= sizeof(creds->session); | ||
241 | |||
242 | GETSHORT(creds->kvno,p); | ||
243 | len -= 2; | ||
244 | GETLONG(creds->ticket_st.length,p); | ||
245 | len -= 4; | ||
246 | |||
247 | tl = creds->ticket_st.length; | ||
248 | if (tl < 0 || tl > len || tl > sizeof(creds->ticket_st.dat)) | ||
249 | return 0; | ||
250 | |||
251 | memcpy(creds->ticket_st.dat, p, tl); | ||
252 | p += tl; | ||
253 | len -= tl; | ||
254 | |||
255 | return 1; | ||
256 | } | ||
257 | 250 | ||
251 | /* Enough space for remaining fixed-length parts? */ | ||
252 | if (len < (4 + 4 + sizeof(creds->session) + 2 + 4)) | ||
253 | return 0; | ||
254 | |||
255 | GETLONG(creds->issue_date, p); | ||
256 | len -= 4; | ||
257 | { | ||
258 | unsigned int endTime; | ||
259 | GETLONG(endTime, p); | ||
260 | len -= 4; | ||
261 | creds->lifetime = krb_time_to_life(creds->issue_date, endTime); | ||
262 | } | ||
263 | |||
264 | memcpy(&creds->session, p, sizeof(creds->session)); | ||
265 | p += sizeof(creds->session); | ||
266 | len -= sizeof(creds->session); | ||
267 | |||
268 | GETSHORT(creds->kvno, p); | ||
269 | len -= 2; | ||
270 | GETLONG(creds->ticket_st.length, p); | ||
271 | len -= 4; | ||
272 | |||
273 | tl = creds->ticket_st.length; | ||
274 | if (tl < 0 || tl > len || tl > sizeof(creds->ticket_st.dat)) | ||
275 | return 0; | ||
276 | |||
277 | memcpy(creds->ticket_st.dat, p, tl); | ||
278 | p += tl; | ||
279 | len -= tl; | ||
280 | |||
281 | return 1; | ||
282 | } | ||
258 | #endif /* AFS */ | 283 | #endif /* AFS */ |
diff --git a/readconf.c b/readconf.c index d8694b82d..063bd467d 100644 --- a/readconf.c +++ b/readconf.c | |||
@@ -1,20 +1,20 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * | |
3 | readconf.c | 3 | * readconf.c |
4 | 4 | * | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
6 | 6 | * | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
8 | All rights reserved | 8 | * All rights reserved |
9 | 9 | * | |
10 | Created: Sat Apr 22 00:03:10 1995 ylo | 10 | * Created: Sat Apr 22 00:03:10 1995 ylo |
11 | 11 | * | |
12 | Functions for reading the configuration files. | 12 | * Functions for reading the configuration files. |
13 | 13 | * | |
14 | */ | 14 | */ |
15 | 15 | ||
16 | #include "includes.h" | 16 | #include "includes.h" |
17 | RCSID("$Id: readconf.c,v 1.4 1999/11/21 02:23:53 damien Exp $"); | 17 | RCSID("$Id: readconf.c,v 1.5 1999/11/24 13:26:22 damien Exp $"); |
18 | 18 | ||
19 | #include "ssh.h" | 19 | #include "ssh.h" |
20 | #include "cipher.h" | 20 | #include "cipher.h" |
@@ -86,73 +86,72 @@ RCSID("$Id: readconf.c,v 1.4 1999/11/21 02:23:53 damien Exp $"); | |||
86 | 86 | ||
87 | /* Keyword tokens. */ | 87 | /* Keyword tokens. */ |
88 | 88 | ||
89 | typedef enum | 89 | typedef enum { |
90 | { | 90 | oBadOption, |
91 | oBadOption, | 91 | oForwardAgent, oForwardX11, oGatewayPorts, oRhostsAuthentication, |
92 | oForwardAgent, oForwardX11, oGatewayPorts, oRhostsAuthentication, | 92 | oPasswordAuthentication, oRSAAuthentication, oFallBackToRsh, oUseRsh, |
93 | oPasswordAuthentication, oRSAAuthentication, oFallBackToRsh, oUseRsh, | 93 | oSkeyAuthentication, |
94 | #ifdef KRB4 | 94 | #ifdef KRB4 |
95 | oKerberosAuthentication, | 95 | oKerberosAuthentication, |
96 | #endif /* KRB4 */ | 96 | #endif /* KRB4 */ |
97 | #ifdef AFS | 97 | #ifdef AFS |
98 | oKerberosTgtPassing, oAFSTokenPassing, | 98 | oKerberosTgtPassing, oAFSTokenPassing, |
99 | #endif | 99 | #endif |
100 | oIdentityFile, oHostName, oPort, oCipher, oRemoteForward, oLocalForward, | 100 | oIdentityFile, oHostName, oPort, oCipher, oRemoteForward, oLocalForward, |
101 | oUser, oHost, oEscapeChar, oRhostsRSAAuthentication, oProxyCommand, | 101 | oUser, oHost, oEscapeChar, oRhostsRSAAuthentication, oProxyCommand, |
102 | oGlobalKnownHostsFile, oUserKnownHostsFile, oConnectionAttempts, | 102 | oGlobalKnownHostsFile, oUserKnownHostsFile, oConnectionAttempts, |
103 | oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression, | 103 | oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression, |
104 | oCompressionLevel, oKeepAlives, oNumberOfPasswordPrompts, oTISAuthentication, | 104 | oCompressionLevel, oKeepAlives, oNumberOfPasswordPrompts, oTISAuthentication, |
105 | oUsePrivilegedPort, oLogLevel | 105 | oUsePrivilegedPort, oLogLevel |
106 | } OpCodes; | 106 | } OpCodes; |
107 | 107 | ||
108 | /* Textual representations of the tokens. */ | 108 | /* Textual representations of the tokens. */ |
109 | 109 | ||
110 | static struct | 110 | static struct { |
111 | { | 111 | const char *name; |
112 | const char *name; | 112 | OpCodes opcode; |
113 | OpCodes opcode; | 113 | } keywords[] = { |
114 | } keywords[] = | 114 | { "forwardagent", oForwardAgent }, |
115 | { | 115 | { "forwardx11", oForwardX11 }, |
116 | { "forwardagent", oForwardAgent }, | 116 | { "gatewayports", oGatewayPorts }, |
117 | { "forwardx11", oForwardX11 }, | 117 | { "useprivilegedport", oUsePrivilegedPort }, |
118 | { "gatewayports", oGatewayPorts }, | 118 | { "rhostsauthentication", oRhostsAuthentication }, |
119 | { "useprivilegedport", oUsePrivilegedPort }, | 119 | { "passwordauthentication", oPasswordAuthentication }, |
120 | { "rhostsauthentication", oRhostsAuthentication }, | 120 | { "rsaauthentication", oRSAAuthentication }, |
121 | { "passwordauthentication", oPasswordAuthentication }, | 121 | { "skeyauthentication", oSkeyAuthentication }, |
122 | { "rsaauthentication", oRSAAuthentication }, | ||
123 | #ifdef KRB4 | 122 | #ifdef KRB4 |
124 | { "kerberosauthentication", oKerberosAuthentication }, | 123 | { "kerberosauthentication", oKerberosAuthentication }, |
125 | #endif /* KRB4 */ | 124 | #endif /* KRB4 */ |
126 | #ifdef AFS | 125 | #ifdef AFS |
127 | { "kerberostgtpassing", oKerberosTgtPassing }, | 126 | { "kerberostgtpassing", oKerberosTgtPassing }, |
128 | { "afstokenpassing", oAFSTokenPassing }, | 127 | { "afstokenpassing", oAFSTokenPassing }, |
129 | #endif | 128 | #endif |
130 | { "fallbacktorsh", oFallBackToRsh }, | 129 | { "fallbacktorsh", oFallBackToRsh }, |
131 | { "usersh", oUseRsh }, | 130 | { "usersh", oUseRsh }, |
132 | { "identityfile", oIdentityFile }, | 131 | { "identityfile", oIdentityFile }, |
133 | { "hostname", oHostName }, | 132 | { "hostname", oHostName }, |
134 | { "proxycommand", oProxyCommand }, | 133 | { "proxycommand", oProxyCommand }, |
135 | { "port", oPort }, | 134 | { "port", oPort }, |
136 | { "cipher", oCipher }, | 135 | { "cipher", oCipher }, |
137 | { "remoteforward", oRemoteForward }, | 136 | { "remoteforward", oRemoteForward }, |
138 | { "localforward", oLocalForward }, | 137 | { "localforward", oLocalForward }, |
139 | { "user", oUser }, | 138 | { "user", oUser }, |
140 | { "host", oHost }, | 139 | { "host", oHost }, |
141 | { "escapechar", oEscapeChar }, | 140 | { "escapechar", oEscapeChar }, |
142 | { "rhostsrsaauthentication", oRhostsRSAAuthentication }, | 141 | { "rhostsrsaauthentication", oRhostsRSAAuthentication }, |
143 | { "globalknownhostsfile", oGlobalKnownHostsFile }, | 142 | { "globalknownhostsfile", oGlobalKnownHostsFile }, |
144 | { "userknownhostsfile", oUserKnownHostsFile }, | 143 | { "userknownhostsfile", oUserKnownHostsFile }, |
145 | { "connectionattempts", oConnectionAttempts }, | 144 | { "connectionattempts", oConnectionAttempts }, |
146 | { "batchmode", oBatchMode }, | 145 | { "batchmode", oBatchMode }, |
147 | { "checkhostip", oCheckHostIP }, | 146 | { "checkhostip", oCheckHostIP }, |
148 | { "stricthostkeychecking", oStrictHostKeyChecking }, | 147 | { "stricthostkeychecking", oStrictHostKeyChecking }, |
149 | { "compression", oCompression }, | 148 | { "compression", oCompression }, |
150 | { "compressionlevel", oCompressionLevel }, | 149 | { "compressionlevel", oCompressionLevel }, |
151 | { "keepalive", oKeepAlives }, | 150 | { "keepalive", oKeepAlives }, |
152 | { "numberofpasswordprompts", oNumberOfPasswordPrompts }, | 151 | { "numberofpasswordprompts", oNumberOfPasswordPrompts }, |
153 | { "tisauthentication", oTISAuthentication }, | 152 | { "tisauthentication", oTISAuthentication }, |
154 | { "loglevel", oLogLevel }, | 153 | { "loglevel", oLogLevel }, |
155 | { NULL, 0 } | 154 | { NULL, 0 } |
156 | }; | 155 | }; |
157 | 156 | ||
158 | /* Characters considered whitespace in strtok calls. */ | 157 | /* Characters considered whitespace in strtok calls. */ |
@@ -162,53 +161,56 @@ static struct | |||
162 | /* Adds a local TCP/IP port forward to options. Never returns if there | 161 | /* Adds a local TCP/IP port forward to options. Never returns if there |
163 | is an error. */ | 162 | is an error. */ |
164 | 163 | ||
165 | void add_local_forward(Options *options, int port, const char *host, | 164 | void |
166 | int host_port) | 165 | add_local_forward(Options *options, int port, const char *host, |
166 | int host_port) | ||
167 | { | 167 | { |
168 | Forward *fwd; | 168 | Forward *fwd; |
169 | extern uid_t original_real_uid; | 169 | extern uid_t original_real_uid; |
170 | if ((port & 0xffff) != port) | 170 | if ((port & 0xffff) != port) |
171 | fatal("Requested forwarding of nonexistent port %d.", port); | 171 | fatal("Requested forwarding of nonexistent port %d.", port); |
172 | if (port < IPPORT_RESERVED && original_real_uid != 0) | 172 | if (port < IPPORT_RESERVED && original_real_uid != 0) |
173 | fatal("Privileged ports can only be forwarded by root.\n"); | 173 | fatal("Privileged ports can only be forwarded by root.\n"); |
174 | if (options->num_local_forwards >= SSH_MAX_FORWARDS_PER_DIRECTION) | 174 | if (options->num_local_forwards >= SSH_MAX_FORWARDS_PER_DIRECTION) |
175 | fatal("Too many local forwards (max %d).", SSH_MAX_FORWARDS_PER_DIRECTION); | 175 | fatal("Too many local forwards (max %d).", SSH_MAX_FORWARDS_PER_DIRECTION); |
176 | fwd = &options->local_forwards[options->num_local_forwards++]; | 176 | fwd = &options->local_forwards[options->num_local_forwards++]; |
177 | fwd->port = port; | 177 | fwd->port = port; |
178 | fwd->host = xstrdup(host); | 178 | fwd->host = xstrdup(host); |
179 | fwd->host_port = host_port; | 179 | fwd->host_port = host_port; |
180 | } | 180 | } |
181 | 181 | ||
182 | /* Adds a remote TCP/IP port forward to options. Never returns if there | 182 | /* Adds a remote TCP/IP port forward to options. Never returns if there |
183 | is an error. */ | 183 | is an error. */ |
184 | 184 | ||
185 | void add_remote_forward(Options *options, int port, const char *host, | 185 | void |
186 | int host_port) | 186 | add_remote_forward(Options *options, int port, const char *host, |
187 | int host_port) | ||
187 | { | 188 | { |
188 | Forward *fwd; | 189 | Forward *fwd; |
189 | if (options->num_remote_forwards >= SSH_MAX_FORWARDS_PER_DIRECTION) | 190 | if (options->num_remote_forwards >= SSH_MAX_FORWARDS_PER_DIRECTION) |
190 | fatal("Too many remote forwards (max %d).", | 191 | fatal("Too many remote forwards (max %d).", |
191 | SSH_MAX_FORWARDS_PER_DIRECTION); | 192 | SSH_MAX_FORWARDS_PER_DIRECTION); |
192 | fwd = &options->remote_forwards[options->num_remote_forwards++]; | 193 | fwd = &options->remote_forwards[options->num_remote_forwards++]; |
193 | fwd->port = port; | 194 | fwd->port = port; |
194 | fwd->host = xstrdup(host); | 195 | fwd->host = xstrdup(host); |
195 | fwd->host_port = host_port; | 196 | fwd->host_port = host_port; |
196 | } | 197 | } |
197 | 198 | ||
198 | /* Returns the number of the token pointed to by cp of length len. | 199 | /* Returns the number of the token pointed to by cp of length len. |
199 | Never returns if the token is not known. */ | 200 | Never returns if the token is not known. */ |
200 | 201 | ||
201 | static OpCodes parse_token(const char *cp, const char *filename, int linenum) | 202 | static OpCodes |
203 | parse_token(const char *cp, const char *filename, int linenum) | ||
202 | { | 204 | { |
203 | unsigned int i; | 205 | unsigned int i; |
204 | 206 | ||
205 | for (i = 0; keywords[i].name; i++) | 207 | for (i = 0; keywords[i].name; i++) |
206 | if (strcmp(cp, keywords[i].name) == 0) | 208 | if (strcmp(cp, keywords[i].name) == 0) |
207 | return keywords[i].opcode; | 209 | return keywords[i].opcode; |
208 | 210 | ||
209 | fprintf(stderr, "%s: line %d: Bad configuration option: %s\n", | 211 | fprintf(stderr, "%s: line %d: Bad configuration option: %s\n", |
210 | filename, linenum, cp); | 212 | filename, linenum, cp); |
211 | return oBadOption; | 213 | return oBadOption; |
212 | } | 214 | } |
213 | 215 | ||
214 | /* Processes a single option line as used in the configuration files. | 216 | /* Processes a single option line as used in the configuration files. |
@@ -216,336 +218,329 @@ static OpCodes parse_token(const char *cp, const char *filename, int linenum) | |||
216 | 218 | ||
217 | int | 219 | int |
218 | process_config_line(Options *options, const char *host, | 220 | process_config_line(Options *options, const char *host, |
219 | char *line, const char *filename, int linenum, | 221 | char *line, const char *filename, int linenum, |
220 | int *activep) | 222 | int *activep) |
221 | { | 223 | { |
222 | char buf[256], *cp, *string, **charptr; | 224 | char buf[256], *cp, *string, **charptr; |
223 | int opcode, *intptr, value, fwd_port, fwd_host_port; | 225 | int opcode, *intptr, value, fwd_port, fwd_host_port; |
224 | 226 | ||
225 | /* Skip leading whitespace. */ | 227 | /* Skip leading whitespace. */ |
226 | cp = line + strspn(line, WHITESPACE); | 228 | cp = line + strspn(line, WHITESPACE); |
227 | if (!*cp || *cp == '\n' || *cp == '#') | 229 | if (!*cp || *cp == '\n' || *cp == '#') |
228 | return 0; | 230 | return 0; |
229 | 231 | ||
230 | /* Get the keyword. (Each line is supposed to begin with a keyword). */ | 232 | /* Get the keyword. (Each line is supposed to begin with a |
231 | cp = strtok(cp, WHITESPACE); | 233 | keyword). */ |
232 | { | 234 | cp = strtok(cp, WHITESPACE); |
233 | char *t = cp; | 235 | { |
234 | for (; *t != 0; t++) | 236 | char *t = cp; |
235 | if ('A' <= *t && *t <= 'Z') | 237 | for (; *t != 0; t++) |
236 | *t = *t - 'A' + 'a'; /* tolower */ | 238 | if ('A' <= *t && *t <= 'Z') |
237 | 239 | *t = *t - 'A' + 'a'; /* tolower */ | |
238 | } | 240 | |
239 | opcode = parse_token(cp, filename, linenum); | 241 | } |
240 | 242 | opcode = parse_token(cp, filename, linenum); | |
241 | switch (opcode) | 243 | |
242 | { | 244 | switch (opcode) { |
243 | case oBadOption: | 245 | case oBadOption: |
244 | return -1; /* don't panic, but count bad options */ | 246 | return -1; /* don't panic, but count bad options */ |
245 | /*NOTREACHED*/ | 247 | /* NOTREACHED */ |
246 | case oForwardAgent: | 248 | case oForwardAgent: |
247 | intptr = &options->forward_agent; | 249 | intptr = &options->forward_agent; |
248 | parse_flag: | 250 | parse_flag: |
249 | cp = strtok(NULL, WHITESPACE); | 251 | cp = strtok(NULL, WHITESPACE); |
250 | if (!cp) | 252 | if (!cp) |
251 | fatal("%.200s line %d: Missing yes/no argument.", filename, linenum); | 253 | fatal("%.200s line %d: Missing yes/no argument.", filename, linenum); |
252 | value = 0; /* To avoid compiler warning... */ | 254 | value = 0; /* To avoid compiler warning... */ |
253 | if (strcmp(cp, "yes") == 0 || strcmp(cp, "true") == 0) | 255 | if (strcmp(cp, "yes") == 0 || strcmp(cp, "true") == 0) |
254 | value = 1; | 256 | value = 1; |
255 | else if (strcmp(cp, "no") == 0 || strcmp(cp, "false") == 0) | 257 | else if (strcmp(cp, "no") == 0 || strcmp(cp, "false") == 0) |
256 | value = 0; | 258 | value = 0; |
257 | else | 259 | else |
258 | fatal("%.200s line %d: Bad yes/no argument.", filename, linenum); | 260 | fatal("%.200s line %d: Bad yes/no argument.", filename, linenum); |
259 | if (*activep && *intptr == -1) | 261 | if (*activep && *intptr == -1) |
260 | *intptr = value; | 262 | *intptr = value; |
261 | break; | 263 | break; |
262 | 264 | ||
263 | case oForwardX11: | 265 | case oForwardX11: |
264 | intptr = &options->forward_x11; | 266 | intptr = &options->forward_x11; |
265 | goto parse_flag; | 267 | goto parse_flag; |
266 | 268 | ||
267 | case oGatewayPorts: | 269 | case oGatewayPorts: |
268 | intptr = &options->gateway_ports; | 270 | intptr = &options->gateway_ports; |
269 | goto parse_flag; | 271 | goto parse_flag; |
270 | 272 | ||
271 | case oUsePrivilegedPort: | 273 | case oUsePrivilegedPort: |
272 | intptr = &options->use_privileged_port; | 274 | intptr = &options->use_privileged_port; |
273 | goto parse_flag; | 275 | goto parse_flag; |
274 | 276 | ||
275 | case oRhostsAuthentication: | 277 | case oRhostsAuthentication: |
276 | intptr = &options->rhosts_authentication; | 278 | intptr = &options->rhosts_authentication; |
277 | goto parse_flag; | 279 | goto parse_flag; |
278 | 280 | ||
279 | case oPasswordAuthentication: | 281 | case oPasswordAuthentication: |
280 | intptr = &options->password_authentication; | 282 | intptr = &options->password_authentication; |
281 | goto parse_flag; | 283 | goto parse_flag; |
282 | 284 | ||
283 | case oRSAAuthentication: | 285 | case oRSAAuthentication: |
284 | intptr = &options->rsa_authentication; | 286 | intptr = &options->rsa_authentication; |
285 | goto parse_flag; | 287 | goto parse_flag; |
286 | 288 | ||
287 | case oRhostsRSAAuthentication: | 289 | case oRhostsRSAAuthentication: |
288 | intptr = &options->rhosts_rsa_authentication; | 290 | intptr = &options->rhosts_rsa_authentication; |
289 | goto parse_flag; | 291 | goto parse_flag; |
292 | |||
293 | case oTISAuthentication: | ||
294 | /* fallthrough, there is no difference on the client side */ | ||
295 | case oSkeyAuthentication: | ||
296 | intptr = &options->skey_authentication; | ||
297 | goto parse_flag; | ||
290 | 298 | ||
291 | #ifdef KRB4 | 299 | #ifdef KRB4 |
292 | case oKerberosAuthentication: | 300 | case oKerberosAuthentication: |
293 | intptr = &options->kerberos_authentication; | 301 | intptr = &options->kerberos_authentication; |
294 | goto parse_flag; | 302 | goto parse_flag; |
295 | #endif /* KRB4 */ | 303 | #endif /* KRB4 */ |
296 | 304 | ||
297 | #ifdef AFS | 305 | #ifdef AFS |
298 | case oKerberosTgtPassing: | 306 | case oKerberosTgtPassing: |
299 | intptr = &options->kerberos_tgt_passing; | 307 | intptr = &options->kerberos_tgt_passing; |
300 | goto parse_flag; | 308 | goto parse_flag; |
301 | 309 | ||
302 | case oAFSTokenPassing: | 310 | case oAFSTokenPassing: |
303 | intptr = &options->afs_token_passing; | 311 | intptr = &options->afs_token_passing; |
304 | goto parse_flag; | 312 | goto parse_flag; |
305 | #endif | 313 | #endif |
306 | 314 | ||
307 | case oFallBackToRsh: | 315 | case oFallBackToRsh: |
308 | intptr = &options->fallback_to_rsh; | 316 | intptr = &options->fallback_to_rsh; |
309 | goto parse_flag; | 317 | goto parse_flag; |
310 | 318 | ||
311 | case oUseRsh: | 319 | case oUseRsh: |
312 | intptr = &options->use_rsh; | 320 | intptr = &options->use_rsh; |
313 | goto parse_flag; | 321 | goto parse_flag; |
314 | 322 | ||
315 | case oBatchMode: | 323 | case oBatchMode: |
316 | intptr = &options->batch_mode; | 324 | intptr = &options->batch_mode; |
317 | goto parse_flag; | 325 | goto parse_flag; |
318 | 326 | ||
319 | case oCheckHostIP: | 327 | case oCheckHostIP: |
320 | intptr = &options->check_host_ip; | 328 | intptr = &options->check_host_ip; |
321 | goto parse_flag; | 329 | goto parse_flag; |
322 | 330 | ||
323 | case oStrictHostKeyChecking: | 331 | case oStrictHostKeyChecking: |
324 | intptr = &options->strict_host_key_checking; | 332 | intptr = &options->strict_host_key_checking; |
325 | cp = strtok(NULL, WHITESPACE); | 333 | cp = strtok(NULL, WHITESPACE); |
326 | if (!cp) | 334 | if (!cp) |
327 | fatal("%.200s line %d: Missing yes/no argument.", | 335 | fatal("%.200s line %d: Missing yes/no argument.", |
328 | filename, linenum); | 336 | filename, linenum); |
329 | value = 0; /* To avoid compiler warning... */ | 337 | value = 0; /* To avoid compiler warning... */ |
330 | if (strcmp(cp, "yes") == 0 || strcmp(cp, "true") == 0) | 338 | if (strcmp(cp, "yes") == 0 || strcmp(cp, "true") == 0) |
331 | value = 1; | 339 | value = 1; |
332 | else if (strcmp(cp, "no") == 0 || strcmp(cp, "false") == 0) | 340 | else if (strcmp(cp, "no") == 0 || strcmp(cp, "false") == 0) |
333 | value = 0; | 341 | value = 0; |
334 | else if (strcmp(cp, "ask") == 0) | 342 | else if (strcmp(cp, "ask") == 0) |
335 | value = 2; | 343 | value = 2; |
336 | else | 344 | else |
337 | fatal("%.200s line %d: Bad yes/no/ask argument.", filename, linenum); | 345 | fatal("%.200s line %d: Bad yes/no/ask argument.", filename, linenum); |
338 | if (*activep && *intptr == -1) | 346 | if (*activep && *intptr == -1) |
339 | *intptr = value; | 347 | *intptr = value; |
340 | break; | 348 | break; |
341 | 349 | ||
342 | case oCompression: | 350 | case oCompression: |
343 | intptr = &options->compression; | 351 | intptr = &options->compression; |
344 | goto parse_flag; | 352 | goto parse_flag; |
345 | 353 | ||
346 | case oKeepAlives: | 354 | case oKeepAlives: |
347 | intptr = &options->keepalives; | 355 | intptr = &options->keepalives; |
348 | goto parse_flag; | 356 | goto parse_flag; |
349 | 357 | ||
350 | case oNumberOfPasswordPrompts: | 358 | case oNumberOfPasswordPrompts: |
351 | intptr = &options->number_of_password_prompts; | 359 | intptr = &options->number_of_password_prompts; |
352 | goto parse_int; | 360 | goto parse_int; |
353 | 361 | ||
354 | case oTISAuthentication: | 362 | case oCompressionLevel: |
355 | cp = strtok(NULL, WHITESPACE); | 363 | intptr = &options->compression_level; |
356 | if (cp != 0 && (strcmp(cp, "yes") == 0 || strcmp(cp, "true") == 0)) | 364 | goto parse_int; |
357 | fprintf(stderr, | 365 | |
358 | "%.99s line %d: Warning, TIS is not supported.\n", | 366 | case oIdentityFile: |
359 | filename, | 367 | cp = strtok(NULL, WHITESPACE); |
360 | linenum); | 368 | if (!cp) |
361 | break; | 369 | fatal("%.200s line %d: Missing argument.", filename, linenum); |
362 | 370 | if (*activep) { | |
363 | case oCompressionLevel: | 371 | if (options->num_identity_files >= SSH_MAX_IDENTITY_FILES) |
364 | intptr = &options->compression_level; | 372 | fatal("%.200s line %d: Too many identity files specified (max %d).", |
365 | goto parse_int; | 373 | filename, linenum, SSH_MAX_IDENTITY_FILES); |
366 | 374 | options->identity_files[options->num_identity_files++] = xstrdup(cp); | |
367 | case oIdentityFile: | 375 | } |
368 | cp = strtok(NULL, WHITESPACE); | 376 | break; |
369 | if (!cp) | 377 | |
370 | fatal("%.200s line %d: Missing argument.", filename, linenum); | 378 | case oUser: |
371 | if (*activep) | 379 | charptr = &options->user; |
372 | { | 380 | parse_string: |
373 | if (options->num_identity_files >= SSH_MAX_IDENTITY_FILES) | 381 | cp = strtok(NULL, WHITESPACE); |
374 | fatal("%.200s line %d: Too many identity files specified (max %d).", | 382 | if (!cp) |
375 | filename, linenum, SSH_MAX_IDENTITY_FILES); | 383 | fatal("%.200s line %d: Missing argument.", filename, linenum); |
376 | options->identity_files[options->num_identity_files++] = xstrdup(cp); | 384 | if (*activep && *charptr == NULL) |
377 | } | 385 | *charptr = xstrdup(cp); |
378 | break; | 386 | break; |
379 | 387 | ||
380 | case oUser: | 388 | case oGlobalKnownHostsFile: |
381 | charptr = &options->user; | 389 | charptr = &options->system_hostfile; |
382 | parse_string: | 390 | goto parse_string; |
383 | cp = strtok(NULL, WHITESPACE); | 391 | |
384 | if (!cp) | 392 | case oUserKnownHostsFile: |
385 | fatal("%.200s line %d: Missing argument.", filename, linenum); | 393 | charptr = &options->user_hostfile; |
386 | if (*activep && *charptr == NULL) | 394 | goto parse_string; |
387 | *charptr = xstrdup(cp); | 395 | |
388 | break; | 396 | case oHostName: |
389 | 397 | charptr = &options->hostname; | |
390 | case oGlobalKnownHostsFile: | 398 | goto parse_string; |
391 | charptr = &options->system_hostfile; | 399 | |
392 | goto parse_string; | 400 | case oProxyCommand: |
393 | 401 | charptr = &options->proxy_command; | |
394 | case oUserKnownHostsFile: | 402 | string = xstrdup(""); |
395 | charptr = &options->user_hostfile; | 403 | while ((cp = strtok(NULL, WHITESPACE)) != NULL) { |
396 | goto parse_string; | 404 | string = xrealloc(string, strlen(string) + strlen(cp) + 2); |
397 | 405 | strcat(string, " "); | |
398 | case oHostName: | 406 | strcat(string, cp); |
399 | charptr = &options->hostname; | 407 | } |
400 | goto parse_string; | 408 | if (*activep && *charptr == NULL) |
401 | 409 | *charptr = string; | |
402 | case oProxyCommand: | 410 | else |
403 | charptr = &options->proxy_command; | 411 | xfree(string); |
404 | string = xstrdup(""); | 412 | return 0; |
405 | while ((cp = strtok(NULL, WHITESPACE)) != NULL) | 413 | |
406 | { | 414 | case oPort: |
407 | string = xrealloc(string, strlen(string) + strlen(cp) + 2); | 415 | intptr = &options->port; |
408 | strcat(string, " "); | 416 | parse_int: |
409 | strcat(string, cp); | 417 | cp = strtok(NULL, WHITESPACE); |
410 | } | 418 | if (!cp) |
411 | if (*activep && *charptr == NULL) | 419 | fatal("%.200s line %d: Missing argument.", filename, linenum); |
412 | *charptr = string; | 420 | if (cp[0] < '0' || cp[0] > '9') |
413 | else | 421 | fatal("%.200s line %d: Bad number.", filename, linenum); |
414 | xfree(string); | ||
415 | return 0; | ||
416 | |||
417 | case oPort: | ||
418 | intptr = &options->port; | ||
419 | parse_int: | ||
420 | cp = strtok(NULL, WHITESPACE); | ||
421 | if (!cp) | ||
422 | fatal("%.200s line %d: Missing argument.", filename, linenum); | ||
423 | if (cp[0] < '0' || cp[0] > '9') | ||
424 | fatal("%.200s line %d: Bad number.", filename, linenum); | ||
425 | #if 0 | 422 | #if 0 |
426 | value = atoi(cp); | 423 | value = atoi(cp); |
427 | #else | 424 | #else |
428 | { | 425 | { |
429 | char *ptr; | 426 | char *ptr; |
430 | value = strtol(cp, &ptr, 0); /* Octal, decimal, or hex format? */ | 427 | value = strtol(cp, &ptr, 0); /* Octal, decimal, or |
431 | if (cp == ptr) | 428 | hex format? */ |
432 | fatal("%.200s line %d: Bad number.", filename, linenum); | 429 | if (cp == ptr) |
433 | } | 430 | fatal("%.200s line %d: Bad number.", filename, linenum); |
431 | } | ||
434 | #endif | 432 | #endif |
435 | if (*activep && *intptr == -1) | 433 | if (*activep && *intptr == -1) |
436 | *intptr = value; | 434 | *intptr = value; |
437 | break; | 435 | break; |
438 | 436 | ||
439 | case oConnectionAttempts: | 437 | case oConnectionAttempts: |
440 | intptr = &options->connection_attempts; | 438 | intptr = &options->connection_attempts; |
441 | goto parse_int; | 439 | goto parse_int; |
442 | 440 | ||
443 | case oCipher: | 441 | case oCipher: |
444 | intptr = &options->cipher; | 442 | intptr = &options->cipher; |
445 | cp = strtok(NULL, WHITESPACE); | 443 | cp = strtok(NULL, WHITESPACE); |
446 | value = cipher_number(cp); | 444 | value = cipher_number(cp); |
447 | if (value == -1) | 445 | if (value == -1) |
448 | fatal("%.200s line %d: Bad cipher '%s'.", | 446 | fatal("%.200s line %d: Bad cipher '%s'.", |
449 | filename, linenum, cp ? cp : "<NONE>"); | 447 | filename, linenum, cp ? cp : "<NONE>"); |
450 | if (*activep && *intptr == -1) | 448 | if (*activep && *intptr == -1) |
451 | *intptr = value; | 449 | *intptr = value; |
452 | break; | 450 | break; |
453 | 451 | ||
454 | case oLogLevel: | 452 | case oLogLevel: |
455 | intptr = (int *)&options->log_level; | 453 | intptr = (int *) &options->log_level; |
456 | cp = strtok(NULL, WHITESPACE); | 454 | cp = strtok(NULL, WHITESPACE); |
457 | value = log_level_number(cp); | 455 | value = log_level_number(cp); |
458 | if (value == (LogLevel)-1) | 456 | if (value == (LogLevel) - 1) |
459 | fatal("%.200s line %d: unsupported log level '%s'\n", | 457 | fatal("%.200s line %d: unsupported log level '%s'\n", |
460 | filename, linenum, cp ? cp : "<NONE>"); | 458 | filename, linenum, cp ? cp : "<NONE>"); |
461 | if (*activep && (LogLevel)*intptr == -1) | 459 | if (*activep && (LogLevel) * intptr == -1) |
462 | *intptr = (LogLevel)value; | 460 | *intptr = (LogLevel) value; |
463 | break; | 461 | break; |
464 | 462 | ||
465 | case oRemoteForward: | 463 | case oRemoteForward: |
466 | cp = strtok(NULL, WHITESPACE); | 464 | cp = strtok(NULL, WHITESPACE); |
467 | if (!cp) | 465 | if (!cp) |
468 | fatal("%.200s line %d: Missing argument.", filename, linenum); | 466 | fatal("%.200s line %d: Missing argument.", filename, linenum); |
469 | if (cp[0] < '0' || cp[0] > '9') | 467 | if (cp[0] < '0' || cp[0] > '9') |
470 | fatal("%.200s line %d: Badly formatted port number.", | 468 | fatal("%.200s line %d: Badly formatted port number.", |
471 | filename, linenum); | 469 | filename, linenum); |
472 | fwd_port = atoi(cp); | 470 | fwd_port = atoi(cp); |
473 | cp = strtok(NULL, WHITESPACE); | 471 | cp = strtok(NULL, WHITESPACE); |
474 | if (!cp) | 472 | if (!cp) |
475 | fatal("%.200s line %d: Missing second argument.", | 473 | fatal("%.200s line %d: Missing second argument.", |
476 | filename, linenum); | 474 | filename, linenum); |
477 | if (sscanf(cp, "%255[^:]:%d", buf, &fwd_host_port) != 2) | 475 | if (sscanf(cp, "%255[^:]:%d", buf, &fwd_host_port) != 2) |
478 | fatal("%.200s line %d: Badly formatted host:port.", | 476 | fatal("%.200s line %d: Badly formatted host:port.", |
479 | filename, linenum); | 477 | filename, linenum); |
480 | if (*activep) | 478 | if (*activep) |
481 | add_remote_forward(options, fwd_port, buf, fwd_host_port); | 479 | add_remote_forward(options, fwd_port, buf, fwd_host_port); |
482 | break; | 480 | break; |
483 | 481 | ||
484 | case oLocalForward: | 482 | case oLocalForward: |
485 | cp = strtok(NULL, WHITESPACE); | 483 | cp = strtok(NULL, WHITESPACE); |
486 | if (!cp) | 484 | if (!cp) |
487 | fatal("%.200s line %d: Missing argument.", filename, linenum); | 485 | fatal("%.200s line %d: Missing argument.", filename, linenum); |
488 | if (cp[0] < '0' || cp[0] > '9') | 486 | if (cp[0] < '0' || cp[0] > '9') |
489 | fatal("%.200s line %d: Badly formatted port number.", | 487 | fatal("%.200s line %d: Badly formatted port number.", |
490 | filename, linenum); | 488 | filename, linenum); |
491 | fwd_port = atoi(cp); | 489 | fwd_port = atoi(cp); |
492 | cp = strtok(NULL, WHITESPACE); | 490 | cp = strtok(NULL, WHITESPACE); |
493 | if (!cp) | 491 | if (!cp) |
494 | fatal("%.200s line %d: Missing second argument.", | 492 | fatal("%.200s line %d: Missing second argument.", |
495 | filename, linenum); | 493 | filename, linenum); |
496 | if (sscanf(cp, "%255[^:]:%d", buf, &fwd_host_port) != 2) | 494 | if (sscanf(cp, "%255[^:]:%d", buf, &fwd_host_port) != 2) |
497 | fatal("%.200s line %d: Badly formatted host:port.", | 495 | fatal("%.200s line %d: Badly formatted host:port.", |
498 | filename, linenum); | 496 | filename, linenum); |
499 | if (*activep) | 497 | if (*activep) |
500 | add_local_forward(options, fwd_port, buf, fwd_host_port); | 498 | add_local_forward(options, fwd_port, buf, fwd_host_port); |
501 | break; | 499 | break; |
502 | 500 | ||
503 | case oHost: | 501 | case oHost: |
504 | *activep = 0; | 502 | *activep = 0; |
505 | while ((cp = strtok(NULL, WHITESPACE)) != NULL) | 503 | while ((cp = strtok(NULL, WHITESPACE)) != NULL) |
506 | if (match_pattern(host, cp)) | 504 | if (match_pattern(host, cp)) { |
507 | { | 505 | debug("Applying options for %.100s", cp); |
508 | debug("Applying options for %.100s", cp); | 506 | *activep = 1; |
509 | *activep = 1; | 507 | break; |
510 | break; | 508 | } |
511 | } | 509 | /* Avoid garbage check below, as strtok already returned |
512 | /* Avoid garbage check below, as strtok already returned NULL. */ | 510 | NULL. */ |
513 | return 0; | 511 | return 0; |
514 | 512 | ||
515 | case oEscapeChar: | 513 | case oEscapeChar: |
516 | intptr = &options->escape_char; | 514 | intptr = &options->escape_char; |
517 | cp = strtok(NULL, WHITESPACE); | 515 | cp = strtok(NULL, WHITESPACE); |
518 | if (!cp) | 516 | if (!cp) |
519 | fatal("%.200s line %d: Missing argument.", filename, linenum); | 517 | fatal("%.200s line %d: Missing argument.", filename, linenum); |
520 | if (cp[0] == '^' && cp[2] == 0 && | 518 | if (cp[0] == '^' && cp[2] == 0 && |
521 | (unsigned char)cp[1] >= 64 && (unsigned char)cp[1] < 128) | 519 | (unsigned char) cp[1] >= 64 && (unsigned char) cp[1] < 128) |
522 | value = (unsigned char)cp[1] & 31; | 520 | value = (unsigned char) cp[1] & 31; |
523 | else | 521 | else if (strlen(cp) == 1) |
524 | if (strlen(cp) == 1) | 522 | value = (unsigned char) cp[0]; |
525 | value = (unsigned char)cp[0]; | 523 | else if (strcmp(cp, "none") == 0) |
526 | else | 524 | value = -2; |
527 | if (strcmp(cp, "none") == 0) | 525 | else { |
528 | value = -2; | 526 | fatal("%.200s line %d: Bad escape character.", |
529 | else | 527 | filename, linenum); |
530 | { | 528 | /* NOTREACHED */ |
531 | fatal("%.200s line %d: Bad escape character.", | 529 | value = 0; /* Avoid compiler warning. */ |
532 | filename, linenum); | 530 | } |
533 | /*NOTREACHED*/ | 531 | if (*activep && *intptr == -1) |
534 | value = 0; /* Avoid compiler warning. */ | 532 | *intptr = value; |
535 | } | 533 | break; |
536 | if (*activep && *intptr == -1) | 534 | |
537 | *intptr = value; | 535 | default: |
538 | break; | 536 | fatal("process_config_line: Unimplemented opcode %d", opcode); |
539 | 537 | } | |
540 | default: | 538 | |
541 | fatal("process_config_line: Unimplemented opcode %d", opcode); | 539 | /* Check that there is no garbage at end of line. */ |
542 | } | 540 | if (strtok(NULL, WHITESPACE) != NULL) |
543 | 541 | fatal("%.200s line %d: garbage at end of line.", | |
544 | /* Check that there is no garbage at end of line. */ | 542 | filename, linenum); |
545 | if (strtok(NULL, WHITESPACE) != NULL) | 543 | return 0; |
546 | fatal("%.200s line %d: garbage at end of line.", | ||
547 | filename, linenum); | ||
548 | return 0; | ||
549 | } | 544 | } |
550 | 545 | ||
551 | 546 | ||
@@ -553,35 +548,35 @@ process_config_line(Options *options, const char *host, | |||
553 | already be initialized before this call. This never returns if there | 548 | already be initialized before this call. This never returns if there |
554 | is an error. If the file does not exist, this returns immediately. */ | 549 | is an error. If the file does not exist, this returns immediately. */ |
555 | 550 | ||
556 | void read_config_file(const char *filename, const char *host, Options *options) | 551 | void |
552 | read_config_file(const char *filename, const char *host, Options *options) | ||
557 | { | 553 | { |
558 | FILE *f; | 554 | FILE *f; |
559 | char line[1024]; | 555 | char line[1024]; |
560 | int active, linenum; | 556 | int active, linenum; |
561 | int bad_options = 0; | 557 | int bad_options = 0; |
562 | 558 | ||
563 | /* Open the file. */ | 559 | /* Open the file. */ |
564 | f = fopen(filename, "r"); | 560 | f = fopen(filename, "r"); |
565 | if (!f) | 561 | if (!f) |
566 | return; | 562 | return; |
567 | 563 | ||
568 | debug("Reading configuration data %.200s", filename); | 564 | debug("Reading configuration data %.200s", filename); |
569 | 565 | ||
570 | /* Mark that we are now processing the options. This flag is turned on/off | 566 | /* Mark that we are now processing the options. This flag is |
571 | by Host specifications. */ | 567 | turned on/off by Host specifications. */ |
572 | active = 1; | 568 | active = 1; |
573 | linenum = 0; | 569 | linenum = 0; |
574 | while (fgets(line, sizeof(line), f)) | 570 | while (fgets(line, sizeof(line), f)) { |
575 | { | 571 | /* Update line number counter. */ |
576 | /* Update line number counter. */ | 572 | linenum++; |
577 | linenum++; | 573 | if (process_config_line(options, host, line, filename, linenum, &active) != 0) |
578 | if (process_config_line(options, host, line, filename, linenum, &active) != 0) | 574 | bad_options++; |
579 | bad_options++; | 575 | } |
580 | } | 576 | fclose(f); |
581 | fclose(f); | 577 | if (bad_options > 0) |
582 | if (bad_options > 0) | 578 | fatal("%s: terminating, %d bad configuration options\n", |
583 | fatal("%s: terminating, %d bad configuration options\n", | 579 | filename, bad_options); |
584 | filename, bad_options); | ||
585 | } | 580 | } |
586 | 581 | ||
587 | /* Initializes options to special values that indicate that they have not | 582 | /* Initializes options to special values that indicate that they have not |
@@ -589,120 +584,124 @@ void read_config_file(const char *filename, const char *host, Options *options) | |||
589 | Options are processed in the following order: command line, user config | 584 | Options are processed in the following order: command line, user config |
590 | file, system config file. Last, fill_default_options is called. */ | 585 | file, system config file. Last, fill_default_options is called. */ |
591 | 586 | ||
592 | void initialize_options(Options *options) | 587 | void |
588 | initialize_options(Options * options) | ||
593 | { | 589 | { |
594 | memset(options, 'X', sizeof(*options)); | 590 | memset(options, 'X', sizeof(*options)); |
595 | options->forward_agent = -1; | 591 | options->forward_agent = -1; |
596 | options->forward_x11 = -1; | 592 | options->forward_x11 = -1; |
597 | options->gateway_ports = -1; | 593 | options->gateway_ports = -1; |
598 | options->use_privileged_port = -1; | 594 | options->use_privileged_port = -1; |
599 | options->rhosts_authentication = -1; | 595 | options->rhosts_authentication = -1; |
600 | options->rsa_authentication = -1; | 596 | options->rsa_authentication = -1; |
597 | options->skey_authentication = -1; | ||
601 | #ifdef KRB4 | 598 | #ifdef KRB4 |
602 | options->kerberos_authentication = -1; | 599 | options->kerberos_authentication = -1; |
603 | #endif | 600 | #endif |
604 | #ifdef AFS | 601 | #ifdef AFS |
605 | options->kerberos_tgt_passing = -1; | 602 | options->kerberos_tgt_passing = -1; |
606 | options->afs_token_passing = -1; | 603 | options->afs_token_passing = -1; |
607 | #endif | 604 | #endif |
608 | options->password_authentication = -1; | 605 | options->password_authentication = -1; |
609 | options->rhosts_rsa_authentication = -1; | 606 | options->rhosts_rsa_authentication = -1; |
610 | options->fallback_to_rsh = -1; | 607 | options->fallback_to_rsh = -1; |
611 | options->use_rsh = -1; | 608 | options->use_rsh = -1; |
612 | options->batch_mode = -1; | 609 | options->batch_mode = -1; |
613 | options->check_host_ip = -1; | 610 | options->check_host_ip = -1; |
614 | options->strict_host_key_checking = -1; | 611 | options->strict_host_key_checking = -1; |
615 | options->compression = -1; | 612 | options->compression = -1; |
616 | options->keepalives = -1; | 613 | options->keepalives = -1; |
617 | options->compression_level = -1; | 614 | options->compression_level = -1; |
618 | options->port = -1; | 615 | options->port = -1; |
619 | options->connection_attempts = -1; | 616 | options->connection_attempts = -1; |
620 | options->number_of_password_prompts = -1; | 617 | options->number_of_password_prompts = -1; |
621 | options->cipher = -1; | 618 | options->cipher = -1; |
622 | options->num_identity_files = 0; | 619 | options->num_identity_files = 0; |
623 | options->hostname = NULL; | 620 | options->hostname = NULL; |
624 | options->proxy_command = NULL; | 621 | options->proxy_command = NULL; |
625 | options->user = NULL; | 622 | options->user = NULL; |
626 | options->escape_char = -1; | 623 | options->escape_char = -1; |
627 | options->system_hostfile = NULL; | 624 | options->system_hostfile = NULL; |
628 | options->user_hostfile = NULL; | 625 | options->user_hostfile = NULL; |
629 | options->num_local_forwards = 0; | 626 | options->num_local_forwards = 0; |
630 | options->num_remote_forwards = 0; | 627 | options->num_remote_forwards = 0; |
631 | options->log_level = (LogLevel)-1; | 628 | options->log_level = (LogLevel) - 1; |
632 | } | 629 | } |
633 | 630 | ||
634 | /* Called after processing other sources of option data, this fills those | 631 | /* Called after processing other sources of option data, this fills those |
635 | options for which no value has been specified with their default values. */ | 632 | options for which no value has been specified with their default values. */ |
636 | 633 | ||
637 | void fill_default_options(Options *options) | 634 | void |
635 | fill_default_options(Options * options) | ||
638 | { | 636 | { |
639 | if (options->forward_agent == -1) | 637 | if (options->forward_agent == -1) |
640 | options->forward_agent = 1; | 638 | options->forward_agent = 1; |
641 | if (options->forward_x11 == -1) | 639 | if (options->forward_x11 == -1) |
642 | options->forward_x11 = 1; | 640 | options->forward_x11 = 1; |
643 | if (options->gateway_ports == -1) | 641 | if (options->gateway_ports == -1) |
644 | options->gateway_ports = 0; | 642 | options->gateway_ports = 0; |
645 | if (options->use_privileged_port == -1) | 643 | if (options->use_privileged_port == -1) |
646 | options->use_privileged_port = 1; | 644 | options->use_privileged_port = 1; |
647 | if (options->rhosts_authentication == -1) | 645 | if (options->rhosts_authentication == -1) |
648 | options->rhosts_authentication = 1; | 646 | options->rhosts_authentication = 1; |
649 | if (options->rsa_authentication == -1) | 647 | if (options->rsa_authentication == -1) |
650 | options->rsa_authentication = 1; | 648 | options->rsa_authentication = 1; |
649 | if (options->skey_authentication == -1) | ||
650 | options->skey_authentication = 0; | ||
651 | #ifdef KRB4 | 651 | #ifdef KRB4 |
652 | if (options->kerberos_authentication == -1) | 652 | if (options->kerberos_authentication == -1) |
653 | options->kerberos_authentication = 1; | 653 | options->kerberos_authentication = 1; |
654 | #endif /* KRB4 */ | 654 | #endif /* KRB4 */ |
655 | #ifdef AFS | 655 | #ifdef AFS |
656 | if (options->kerberos_tgt_passing == -1) | 656 | if (options->kerberos_tgt_passing == -1) |
657 | options->kerberos_tgt_passing = 1; | 657 | options->kerberos_tgt_passing = 1; |
658 | if (options->afs_token_passing == -1) | 658 | if (options->afs_token_passing == -1) |
659 | options->afs_token_passing = 1; | 659 | options->afs_token_passing = 1; |
660 | #endif /* AFS */ | 660 | #endif /* AFS */ |
661 | if (options->password_authentication == -1) | 661 | if (options->password_authentication == -1) |
662 | options->password_authentication = 1; | 662 | options->password_authentication = 1; |
663 | if (options->rhosts_rsa_authentication == -1) | 663 | if (options->rhosts_rsa_authentication == -1) |
664 | options->rhosts_rsa_authentication = 1; | 664 | options->rhosts_rsa_authentication = 1; |
665 | if (options->fallback_to_rsh == -1) | 665 | if (options->fallback_to_rsh == -1) |
666 | options->fallback_to_rsh = 1; | 666 | options->fallback_to_rsh = 1; |
667 | if (options->use_rsh == -1) | 667 | if (options->use_rsh == -1) |
668 | options->use_rsh = 0; | 668 | options->use_rsh = 0; |
669 | if (options->batch_mode == -1) | 669 | if (options->batch_mode == -1) |
670 | options->batch_mode = 0; | 670 | options->batch_mode = 0; |
671 | if (options->check_host_ip == -1) | 671 | if (options->check_host_ip == -1) |
672 | options->check_host_ip = 1; | 672 | options->check_host_ip = 1; |
673 | if (options->strict_host_key_checking == -1) | 673 | if (options->strict_host_key_checking == -1) |
674 | options->strict_host_key_checking = 2; /* 2 is default */ | 674 | options->strict_host_key_checking = 2; /* 2 is default */ |
675 | if (options->compression == -1) | 675 | if (options->compression == -1) |
676 | options->compression = 0; | 676 | options->compression = 0; |
677 | if (options->keepalives == -1) | 677 | if (options->keepalives == -1) |
678 | options->keepalives = 1; | 678 | options->keepalives = 1; |
679 | if (options->compression_level == -1) | 679 | if (options->compression_level == -1) |
680 | options->compression_level = 6; | 680 | options->compression_level = 6; |
681 | if (options->port == -1) | 681 | if (options->port == -1) |
682 | options->port = 0; /* Filled in ssh_connect. */ | 682 | options->port = 0; /* Filled in ssh_connect. */ |
683 | if (options->connection_attempts == -1) | 683 | if (options->connection_attempts == -1) |
684 | options->connection_attempts = 4; | 684 | options->connection_attempts = 4; |
685 | if (options->number_of_password_prompts == -1) | 685 | if (options->number_of_password_prompts == -1) |
686 | options->number_of_password_prompts = 3; | 686 | options->number_of_password_prompts = 3; |
687 | if (options->cipher == -1) | 687 | /* Selected in ssh_login(). */ |
688 | options->cipher = SSH_CIPHER_NOT_SET; /* Selected in ssh_login(). */ | 688 | if (options->cipher == -1) |
689 | if (options->num_identity_files == 0) | 689 | options->cipher = SSH_CIPHER_NOT_SET; |
690 | { | 690 | if (options->num_identity_files == 0) { |
691 | options->identity_files[0] = | 691 | options->identity_files[0] = |
692 | xmalloc(2 + strlen(SSH_CLIENT_IDENTITY) + 1); | 692 | xmalloc(2 + strlen(SSH_CLIENT_IDENTITY) + 1); |
693 | sprintf(options->identity_files[0], "~/%.100s", SSH_CLIENT_IDENTITY); | 693 | sprintf(options->identity_files[0], "~/%.100s", SSH_CLIENT_IDENTITY); |
694 | options->num_identity_files = 1; | 694 | options->num_identity_files = 1; |
695 | } | 695 | } |
696 | if (options->escape_char == -1) | 696 | if (options->escape_char == -1) |
697 | options->escape_char = '~'; | 697 | options->escape_char = '~'; |
698 | if (options->system_hostfile == NULL) | 698 | if (options->system_hostfile == NULL) |
699 | options->system_hostfile = SSH_SYSTEM_HOSTFILE; | 699 | options->system_hostfile = SSH_SYSTEM_HOSTFILE; |
700 | if (options->user_hostfile == NULL) | 700 | if (options->user_hostfile == NULL) |
701 | options->user_hostfile = SSH_USER_HOSTFILE; | 701 | options->user_hostfile = SSH_USER_HOSTFILE; |
702 | if (options->log_level == (LogLevel)-1) | 702 | if (options->log_level == (LogLevel) - 1) |
703 | options->log_level = SYSLOG_LEVEL_INFO; | 703 | options->log_level = SYSLOG_LEVEL_INFO; |
704 | /* options->proxy_command should not be set by default */ | 704 | /* options->proxy_command should not be set by default */ |
705 | /* options->user will be set in the main program if appropriate */ | 705 | /* options->user will be set in the main program if appropriate */ |
706 | /* options->hostname will be set in the main program if appropriate */ | 706 | /* options->hostname will be set in the main program if appropriate */ |
707 | } | 707 | } |
708 | |||
diff --git a/readconf.h b/readconf.h index d2d387df1..aeec53a75 100644 --- a/readconf.h +++ b/readconf.h | |||
@@ -1,118 +1,126 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * | |
3 | readconf.h | 3 | * readconf.h |
4 | 4 | * | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
6 | 6 | * | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
8 | All rights reserved | 8 | * All rights reserved |
9 | 9 | * | |
10 | Created: Sat Apr 22 00:25:29 1995 ylo | 10 | * Created: Sat Apr 22 00:25:29 1995 ylo |
11 | 11 | * | |
12 | Functions for reading the configuration file. | 12 | * Functions for reading the configuration file. |
13 | 13 | * | |
14 | */ | 14 | */ |
15 | 15 | ||
16 | /* RCSID("$Id: readconf.h,v 1.3 1999/11/15 04:25:10 damien Exp $"); */ | 16 | /* RCSID("$Id: readconf.h,v 1.4 1999/11/24 13:26:22 damien Exp $"); */ |
17 | 17 | ||
18 | #ifndef READCONF_H | 18 | #ifndef READCONF_H |
19 | #define READCONF_H | 19 | #define READCONF_H |
20 | 20 | ||
21 | /* Data structure for representing a forwarding request. */ | 21 | /* Data structure for representing a forwarding request. */ |
22 | 22 | ||
23 | typedef struct | 23 | typedef struct { |
24 | { | 24 | int port; /* Port to forward. */ |
25 | int port; /* Port to forward. */ | 25 | char *host; /* Host to connect. */ |
26 | char *host; /* Host to connect. */ | 26 | int host_port; /* Port to connect on host. */ |
27 | int host_port; /* Port to connect on host. */ | 27 | } Forward; |
28 | } Forward; | ||
29 | |||
30 | /* Data structure for representing option data. */ | 28 | /* Data structure for representing option data. */ |
31 | 29 | ||
32 | typedef struct | 30 | typedef struct { |
33 | { | 31 | int forward_agent; /* Forward authentication agent. */ |
34 | int forward_agent; /* Forward authentication agent. */ | 32 | int forward_x11; /* Forward X11 display. */ |
35 | int forward_x11; /* Forward X11 display. */ | 33 | int gateway_ports; /* Allow remote connects to forwarded ports. */ |
36 | int gateway_ports; /* Allow remote connects to forwarded ports. */ | 34 | int use_privileged_port; /* Don't use privileged port if false. */ |
37 | int use_privileged_port; /* Don't use privileged port if false. */ | 35 | int rhosts_authentication; /* Try rhosts authentication. */ |
38 | int rhosts_authentication; /* Try rhosts authentication. */ | 36 | int rhosts_rsa_authentication; /* Try rhosts with RSA |
39 | int rhosts_rsa_authentication;/* Try rhosts with RSA authentication. */ | 37 | * authentication. */ |
40 | int rsa_authentication; /* Try RSA authentication. */ | 38 | int rsa_authentication; /* Try RSA authentication. */ |
39 | int skey_authentication; /* Try S/Key or TIS authentication. */ | ||
41 | #ifdef KRB4 | 40 | #ifdef KRB4 |
42 | int kerberos_authentication; /* Try Kerberos authentication. */ | 41 | int kerberos_authentication; /* Try Kerberos |
42 | * authentication. */ | ||
43 | #endif | 43 | #endif |
44 | #ifdef AFS | 44 | #ifdef AFS |
45 | int kerberos_tgt_passing; /* Try Kerberos tgt passing. */ | 45 | int kerberos_tgt_passing; /* Try Kerberos tgt passing. */ |
46 | int afs_token_passing; /* Try AFS token passing. */ | 46 | int afs_token_passing; /* Try AFS token passing. */ |
47 | #endif | 47 | #endif |
48 | int password_authentication; /* Try password authentication. */ | 48 | int password_authentication; /* Try password |
49 | int fallback_to_rsh; /* Use rsh if cannot connect with ssh. */ | 49 | * authentication. */ |
50 | int use_rsh; /* Always use rsh (don\'t try ssh). */ | 50 | int fallback_to_rsh;/* Use rsh if cannot connect with ssh. */ |
51 | int batch_mode; /* Batch mode: do not ask for passwords. */ | 51 | int use_rsh; /* Always use rsh (don\'t try ssh). */ |
52 | int check_host_ip; /* Also keep track of keys for IP address */ | 52 | int batch_mode; /* Batch mode: do not ask for passwords. */ |
53 | int strict_host_key_checking; /* Strict host key checking. */ | 53 | int check_host_ip; /* Also keep track of keys for IP address */ |
54 | int compression; /* Compress packets in both directions. */ | 54 | int strict_host_key_checking; /* Strict host key checking. */ |
55 | int compression_level; /* Compression level 1 (fast) to 9 (best). */ | 55 | int compression; /* Compress packets in both directions. */ |
56 | int keepalives; /* Set SO_KEEPALIVE. */ | 56 | int compression_level; /* Compression level 1 (fast) to 9 |
57 | LogLevel log_level; /* Level for logging. */ | 57 | * (best). */ |
58 | 58 | int keepalives; /* Set SO_KEEPALIVE. */ | |
59 | int port; /* Port to connect. */ | 59 | LogLevel log_level; /* Level for logging. */ |
60 | int connection_attempts; /* Max attempts (seconds) before giving up */ | 60 | |
61 | int number_of_password_prompts; /* Max number of password prompts. */ | 61 | int port; /* Port to connect. */ |
62 | int cipher; /* Cipher to use. */ | 62 | int connection_attempts; /* Max attempts (seconds) before |
63 | char *hostname; /* Real host to connect. */ | 63 | * giving up */ |
64 | char *proxy_command; /* Proxy command for connecting the host. */ | 64 | int number_of_password_prompts; /* Max number of password |
65 | char *user; /* User to log in as. */ | 65 | * prompts. */ |
66 | int escape_char; /* Escape character; -2 = none */ | 66 | int cipher; /* Cipher to use. */ |
67 | 67 | char *hostname; /* Real host to connect. */ | |
68 | char *system_hostfile; /* Path for /etc/ssh_known_hosts. */ | 68 | char *proxy_command; /* Proxy command for connecting the host. */ |
69 | char *user_hostfile; /* Path for $HOME/.ssh/known_hosts. */ | 69 | char *user; /* User to log in as. */ |
70 | 70 | int escape_char; /* Escape character; -2 = none */ | |
71 | int num_identity_files; /* Number of files for RSA identities. */ | 71 | |
72 | char *identity_files[SSH_MAX_IDENTITY_FILES]; | 72 | char *system_hostfile;/* Path for /etc/ssh_known_hosts. */ |
73 | 73 | char *user_hostfile; /* Path for $HOME/.ssh/known_hosts. */ | |
74 | /* Local TCP/IP forward requests. */ | 74 | |
75 | int num_local_forwards; | 75 | int num_identity_files; /* Number of files for RSA identities. */ |
76 | Forward local_forwards[SSH_MAX_FORWARDS_PER_DIRECTION]; | 76 | char *identity_files[SSH_MAX_IDENTITY_FILES]; |
77 | 77 | ||
78 | /* Remote TCP/IP forward requests. */ | 78 | /* Local TCP/IP forward requests. */ |
79 | int num_remote_forwards; | 79 | int num_local_forwards; |
80 | Forward remote_forwards[SSH_MAX_FORWARDS_PER_DIRECTION]; | 80 | Forward local_forwards[SSH_MAX_FORWARDS_PER_DIRECTION]; |
81 | } Options; | 81 | |
82 | /* Remote TCP/IP forward requests. */ | ||
83 | int num_remote_forwards; | ||
84 | Forward remote_forwards[SSH_MAX_FORWARDS_PER_DIRECTION]; | ||
85 | } Options; | ||
82 | 86 | ||
83 | 87 | ||
84 | /* Initializes options to special values that indicate that they have not | 88 | /* Initializes options to special values that indicate that they have not |
85 | yet been set. Read_config_file will only set options with this value. | 89 | yet been set. Read_config_file will only set options with this value. |
86 | Options are processed in the following order: command line, user config | 90 | Options are processed in the following order: command line, user config |
87 | file, system config file. Last, fill_default_options is called. */ | 91 | file, system config file. Last, fill_default_options is called. */ |
88 | void initialize_options(Options *options); | 92 | void initialize_options(Options * options); |
89 | 93 | ||
90 | /* Called after processing other sources of option data, this fills those | 94 | /* Called after processing other sources of option data, this fills those |
91 | options for which no value has been specified with their default values. */ | 95 | options for which no value has been specified with their default values. */ |
92 | void fill_default_options(Options *options); | 96 | void fill_default_options(Options * options); |
93 | 97 | ||
94 | /* Processes a single option line as used in the configuration files. | 98 | /* Processes a single option line as used in the configuration files. |
95 | This only sets those values that have not already been set. | 99 | This only sets those values that have not already been set. |
96 | Returns 0 for legal options */ | 100 | Returns 0 for legal options */ |
97 | int process_config_line(Options *options, const char *host, | 101 | int |
98 | char *line, const char *filename, int linenum, | 102 | process_config_line(Options * options, const char *host, |
99 | int *activep); | 103 | char *line, const char *filename, int linenum, |
104 | int *activep); | ||
100 | 105 | ||
101 | /* Reads the config file and modifies the options accordingly. Options should | 106 | /* Reads the config file and modifies the options accordingly. Options should |
102 | already be initialized before this call. This never returns if there | 107 | already be initialized before this call. This never returns if there |
103 | is an error. If the file does not exist, this returns immediately. */ | 108 | is an error. If the file does not exist, this returns immediately. */ |
104 | void read_config_file(const char *filename, const char *host, | 109 | void |
105 | Options *options); | 110 | read_config_file(const char *filename, const char *host, |
111 | Options * options); | ||
106 | 112 | ||
107 | /* Adds a local TCP/IP port forward to options. Never returns if there | 113 | /* Adds a local TCP/IP port forward to options. Never returns if there |
108 | is an error. */ | 114 | is an error. */ |
109 | void add_local_forward(Options *options, int port, const char *host, | 115 | void |
110 | int host_port); | 116 | add_local_forward(Options * options, int port, const char *host, |
117 | int host_port); | ||
111 | 118 | ||
112 | /* Adds a remote TCP/IP port forward to options. Never returns if there | 119 | /* Adds a remote TCP/IP port forward to options. Never returns if there |
113 | is an error. */ | 120 | is an error. */ |
114 | void add_remote_forward(Options *options, int port, const char *host, | 121 | void |
115 | int host_port); | 122 | add_remote_forward(Options * options, int port, const char *host, |
123 | int host_port); | ||
116 | 124 | ||
117 | 125 | ||
118 | #endif /* READCONF_H */ | 126 | #endif /* READCONF_H */ |
diff --git a/readpass.c b/readpass.c index 3031825e5..66ce33c97 100644 --- a/readpass.c +++ b/readpass.c | |||
@@ -1,20 +1,20 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * | |
3 | readpass.c | 3 | * readpass.c |
4 | 4 | * | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
6 | 6 | * | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
8 | All rights reserved | 8 | * All rights reserved |
9 | 9 | * | |
10 | Created: Mon Jul 10 22:08:59 1995 ylo | 10 | * Created: Mon Jul 10 22:08:59 1995 ylo |
11 | 11 | * | |
12 | Functions for reading passphrases and passwords. | 12 | * Functions for reading passphrases and passwords. |
13 | 13 | * | |
14 | */ | 14 | */ |
15 | 15 | ||
16 | #include "includes.h" | 16 | #include "includes.h" |
17 | RCSID("$Id: readpass.c,v 1.1 1999/10/27 03:42:44 damien Exp $"); | 17 | RCSID("$Id: readpass.c,v 1.2 1999/11/24 13:26:22 damien Exp $"); |
18 | 18 | ||
19 | #include "xmalloc.h" | 19 | #include "xmalloc.h" |
20 | #include "ssh.h" | 20 | #include "ssh.h" |
@@ -23,92 +23,91 @@ RCSID("$Id: readpass.c,v 1.1 1999/10/27 03:42:44 damien Exp $"); | |||
23 | static struct termios saved_tio; | 23 | static struct termios saved_tio; |
24 | 24 | ||
25 | /* Old interrupt signal handler for read_passphrase. */ | 25 | /* Old interrupt signal handler for read_passphrase. */ |
26 | static void (*old_handler)(int sig) = NULL; | 26 | static void (*old_handler) (int sig) = NULL; |
27 | 27 | ||
28 | /* Interrupt signal handler for read_passphrase. */ | 28 | /* Interrupt signal handler for read_passphrase. */ |
29 | 29 | ||
30 | void intr_handler(int sig) | 30 | void |
31 | intr_handler(int sig) | ||
31 | { | 32 | { |
32 | /* Restore terminal modes. */ | 33 | /* Restore terminal modes. */ |
33 | tcsetattr(fileno(stdin), TCSANOW, &saved_tio); | 34 | tcsetattr(fileno(stdin), TCSANOW, &saved_tio); |
34 | /* Restore the old signal handler. */ | 35 | /* Restore the old signal handler. */ |
35 | signal(sig, old_handler); | 36 | signal(sig, old_handler); |
36 | /* Resend the signal, with the old handler. */ | 37 | /* Resend the signal, with the old handler. */ |
37 | kill(getpid(), sig); | 38 | kill(getpid(), sig); |
38 | } | 39 | } |
39 | 40 | ||
40 | /* Reads a passphrase from /dev/tty with echo turned off. Returns the | 41 | /* Reads a passphrase from /dev/tty with echo turned off. Returns the |
41 | passphrase (allocated with xmalloc). Exits if EOF is encountered. | 42 | passphrase (allocated with xmalloc). Exits if EOF is encountered. |
42 | The passphrase if read from stdin if from_stdin is true (as is the | 43 | The passphrase if read from stdin if from_stdin is true (as is the |
43 | case with ssh-keygen). */ | 44 | case with ssh-keygen). */ |
44 | 45 | ||
45 | char *read_passphrase(const char *prompt, int from_stdin) | 46 | char * |
47 | read_passphrase(const char *prompt, int from_stdin) | ||
46 | { | 48 | { |
47 | char buf[1024], *cp; | 49 | char buf[1024], *cp; |
48 | struct termios tio; | 50 | struct termios tio; |
49 | FILE *f; | 51 | FILE *f; |
50 | 52 | ||
51 | if (from_stdin) | 53 | if (from_stdin) |
52 | f = stdin; | 54 | f = stdin; |
53 | else | 55 | else { |
54 | { | 56 | /* Read the passphrase from /dev/tty to make it possible |
55 | /* Read the passphrase from /dev/tty to make it possible to ask it even | 57 | to ask it even when stdin has been redirected. */ |
56 | when stdin has been redirected. */ | 58 | f = fopen("/dev/tty", "r"); |
57 | f = fopen("/dev/tty", "r"); | 59 | if (!f) { |
58 | if (!f) | 60 | /* No controlling terminal and no DISPLAY. Nowhere to read. */ |
59 | { | 61 | fprintf(stderr, "You have no controlling tty and no DISPLAY. Cannot read passphrase.\n"); |
60 | /* No controlling terminal and no DISPLAY. Nowhere to read. */ | 62 | exit(1); |
61 | fprintf(stderr, "You have no controlling tty and no DISPLAY. Cannot read passphrase.\n"); | 63 | } |
62 | exit(1); | ||
63 | } | 64 | } |
64 | } | ||
65 | |||
66 | /* Display the prompt (on stderr because stdout might be redirected). */ | ||
67 | fflush(stdout); | ||
68 | fprintf(stderr, "%s", prompt); | ||
69 | fflush(stderr); | ||
70 | |||
71 | /* Get terminal modes. */ | ||
72 | tcgetattr(fileno(f), &tio); | ||
73 | saved_tio = tio; | ||
74 | /* Save signal handler and set the new handler. */ | ||
75 | old_handler = signal(SIGINT, intr_handler); | ||
76 | 65 | ||
77 | /* Set new terminal modes disabling all echo. */ | 66 | /* Display the prompt (on stderr because stdout might be redirected). */ |
78 | tio.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); | 67 | fflush(stdout); |
79 | tcsetattr(fileno(f), TCSANOW, &tio); | 68 | fprintf(stderr, "%s", prompt); |
80 | 69 | fflush(stderr); | |
81 | /* Read the passphrase from the terminal. */ | 70 | |
82 | if (fgets(buf, sizeof(buf), f) == NULL) | 71 | /* Get terminal modes. */ |
83 | { | 72 | tcgetattr(fileno(f), &tio); |
84 | /* Got EOF. Just exit. */ | 73 | saved_tio = tio; |
85 | /* Restore terminal modes. */ | 74 | /* Save signal handler and set the new handler. */ |
86 | tcsetattr(fileno(f), TCSANOW, &saved_tio); | 75 | old_handler = signal(SIGINT, intr_handler); |
87 | /* Restore the signal handler. */ | 76 | |
88 | signal(SIGINT, old_handler); | 77 | /* Set new terminal modes disabling all echo. */ |
89 | /* Print a newline (the prompt probably didn\'t have one). */ | 78 | tio.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); |
90 | fprintf(stderr, "\n"); | 79 | tcsetattr(fileno(f), TCSANOW, &tio); |
91 | /* Close the file. */ | 80 | |
92 | if (f != stdin) | 81 | /* Read the passphrase from the terminal. */ |
93 | fclose(f); | 82 | if (fgets(buf, sizeof(buf), f) == NULL) { |
94 | exit(1); | 83 | /* Got EOF. Just exit. */ |
95 | } | 84 | /* Restore terminal modes. */ |
96 | /* Restore terminal modes. */ | 85 | tcsetattr(fileno(f), TCSANOW, &saved_tio); |
97 | tcsetattr(fileno(f), TCSANOW, &saved_tio); | 86 | /* Restore the signal handler. */ |
98 | /* Restore the signal handler. */ | 87 | signal(SIGINT, old_handler); |
99 | (void)signal(SIGINT, old_handler); | 88 | /* Print a newline (the prompt probably didn\'t have one). */ |
100 | /* Remove newline from the passphrase. */ | 89 | fprintf(stderr, "\n"); |
101 | if (strchr(buf, '\n')) | 90 | /* Close the file. */ |
102 | *strchr(buf, '\n') = 0; | 91 | if (f != stdin) |
103 | /* Allocate a copy of the passphrase. */ | 92 | fclose(f); |
104 | cp = xstrdup(buf); | 93 | exit(1); |
105 | /* Clear the buffer so we don\'t leave copies of the passphrase laying | 94 | } |
106 | around. */ | 95 | /* Restore terminal modes. */ |
107 | memset(buf, 0, sizeof(buf)); | 96 | tcsetattr(fileno(f), TCSANOW, &saved_tio); |
108 | /* Print a newline since the prompt probably didn\'t have one. */ | 97 | /* Restore the signal handler. */ |
109 | fprintf(stderr, "\n"); | 98 | (void) signal(SIGINT, old_handler); |
110 | /* Close the file. */ | 99 | /* Remove newline from the passphrase. */ |
111 | if (f != stdin) | 100 | if (strchr(buf, '\n')) |
112 | fclose(f); | 101 | *strchr(buf, '\n') = 0; |
113 | return cp; | 102 | /* Allocate a copy of the passphrase. */ |
103 | cp = xstrdup(buf); | ||
104 | /* Clear the buffer so we don\'t leave copies of the passphrase | ||
105 | laying around. */ | ||
106 | memset(buf, 0, sizeof(buf)); | ||
107 | /* Print a newline since the prompt probably didn\'t have one. */ | ||
108 | fprintf(stderr, "\n"); | ||
109 | /* Close the file. */ | ||
110 | if (f != stdin) | ||
111 | fclose(f); | ||
112 | return cp; | ||
114 | } | 113 | } |
@@ -1,41 +1,41 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * | |
3 | rsa.c | 3 | * rsa.c |
4 | 4 | * | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
6 | 6 | * | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
8 | All rights reserved | 8 | * All rights reserved |
9 | 9 | * | |
10 | Created: Fri Mar 3 22:07:06 1995 ylo | 10 | * Created: Fri Mar 3 22:07:06 1995 ylo |
11 | 11 | * | |
12 | Description of the RSA algorithm can be found e.g. from the following sources: | 12 | * Description of the RSA algorithm can be found e.g. from the following sources: |
13 | 13 | * | |
14 | Bruce Schneier: Applied Cryptography. John Wiley & Sons, 1994. | 14 | * Bruce Schneier: Applied Cryptography. John Wiley & Sons, 1994. |
15 | 15 | * | |
16 | Jennifer Seberry and Josed Pieprzyk: Cryptography: An Introduction to | 16 | * Jennifer Seberry and Josed Pieprzyk: Cryptography: An Introduction to |
17 | Computer Security. Prentice-Hall, 1989. | 17 | * Computer Security. Prentice-Hall, 1989. |
18 | 18 | * | |
19 | Man Young Rhee: Cryptography and Secure Data Communications. McGraw-Hill, | 19 | * Man Young Rhee: Cryptography and Secure Data Communications. McGraw-Hill, |
20 | 1994. | 20 | * 1994. |
21 | 21 | * | |
22 | R. Rivest, A. Shamir, and L. M. Adleman: Cryptographic Communications | 22 | * R. Rivest, A. Shamir, and L. M. Adleman: Cryptographic Communications |
23 | System and Method. US Patent 4,405,829, 1983. | 23 | * System and Method. US Patent 4,405,829, 1983. |
24 | 24 | * | |
25 | Hans Riesel: Prime Numbers and Computer Methods for Factorization. | 25 | * Hans Riesel: Prime Numbers and Computer Methods for Factorization. |
26 | Birkhauser, 1994. | 26 | * Birkhauser, 1994. |
27 | 27 | * | |
28 | The RSA Frequently Asked Questions document by RSA Data Security, Inc., 1995. | 28 | * The RSA Frequently Asked Questions document by RSA Data Security, Inc., 1995. |
29 | 29 | * | |
30 | RSA in 3 lines of perl by Adam Back <aba@atlax.ex.ac.uk>, 1995, as included | 30 | * RSA in 3 lines of perl by Adam Back <aba@atlax.ex.ac.uk>, 1995, as included |
31 | below: | 31 | * below: |
32 | 32 | * | |
33 | gone - had to be deleted - what a pity | 33 | * [gone - had to be deleted - what a pity] |
34 | 34 | * | |
35 | */ | 35 | */ |
36 | 36 | ||
37 | #include "includes.h" | 37 | #include "includes.h" |
38 | RCSID("$Id: rsa.c,v 1.3 1999/11/08 23:35:52 damien Exp $"); | 38 | RCSID("$Id: rsa.c,v 1.4 1999/11/24 13:26:22 damien Exp $"); |
39 | 39 | ||
40 | #include "rsa.h" | 40 | #include "rsa.h" |
41 | #include "ssh.h" | 41 | #include "ssh.h" |
@@ -46,13 +46,13 @@ int rsa_verbose = 1; | |||
46 | int | 46 | int |
47 | rsa_alive() | 47 | rsa_alive() |
48 | { | 48 | { |
49 | RSA *key; | 49 | RSA *key; |
50 | 50 | ||
51 | key = RSA_generate_key(32, 3, NULL, NULL); | 51 | key = RSA_generate_key(32, 3, NULL, NULL); |
52 | if (key == NULL) | 52 | if (key == NULL) |
53 | return (0); | 53 | return (0); |
54 | RSA_free(key); | 54 | RSA_free(key); |
55 | return (1); | 55 | return (1); |
56 | } | 56 | } |
57 | 57 | ||
58 | /* Generates RSA public and private keys. This initializes the data | 58 | /* Generates RSA public and private keys. This initializes the data |
@@ -62,101 +62,100 @@ rsa_alive() | |||
62 | void | 62 | void |
63 | rsa_generate_key(RSA *prv, RSA *pub, unsigned int bits) | 63 | rsa_generate_key(RSA *prv, RSA *pub, unsigned int bits) |
64 | { | 64 | { |
65 | RSA *key; | 65 | RSA *key; |
66 | 66 | ||
67 | if (rsa_verbose) { | 67 | if (rsa_verbose) { |
68 | printf("Generating RSA keys: "); | 68 | printf("Generating RSA keys: "); |
69 | fflush(stdout); | 69 | fflush(stdout); |
70 | } | 70 | } |
71 | 71 | key = RSA_generate_key(bits, 35, NULL, NULL); | |
72 | key = RSA_generate_key(bits, 35, NULL, NULL); | 72 | if (key == NULL) |
73 | if (key == NULL) | 73 | fatal("rsa_generate_key: key generation failed."); |
74 | fatal("rsa_generate_key: key generation failed."); | 74 | |
75 | 75 | /* Copy public key parameters */ | |
76 | /* Copy public key parameters */ | 76 | pub->n = BN_new(); |
77 | pub->n = BN_new(); | 77 | BN_copy(pub->n, key->n); |
78 | BN_copy(pub->n, key->n); | 78 | pub->e = BN_new(); |
79 | pub->e = BN_new(); | 79 | BN_copy(pub->e, key->e); |
80 | BN_copy(pub->e, key->e); | 80 | |
81 | 81 | /* Copy private key parameters */ | |
82 | /* Copy private key parameters */ | 82 | prv->n = BN_new(); |
83 | prv->n = BN_new(); | 83 | BN_copy(prv->n, key->n); |
84 | BN_copy(prv->n, key->n); | 84 | prv->e = BN_new(); |
85 | prv->e = BN_new(); | 85 | BN_copy(prv->e, key->e); |
86 | BN_copy(prv->e, key->e); | 86 | prv->d = BN_new(); |
87 | prv->d = BN_new(); | 87 | BN_copy(prv->d, key->d); |
88 | BN_copy(prv->d, key->d); | 88 | prv->p = BN_new(); |
89 | prv->p = BN_new(); | 89 | BN_copy(prv->p, key->p); |
90 | BN_copy(prv->p, key->p); | 90 | prv->q = BN_new(); |
91 | prv->q = BN_new(); | 91 | BN_copy(prv->q, key->q); |
92 | BN_copy(prv->q, key->q); | 92 | |
93 | 93 | prv->dmp1 = BN_new(); | |
94 | prv->dmp1 = BN_new(); | 94 | BN_copy(prv->dmp1, key->dmp1); |
95 | BN_copy(prv->dmp1, key->dmp1); | 95 | |
96 | 96 | prv->dmq1 = BN_new(); | |
97 | prv->dmq1 = BN_new(); | 97 | BN_copy(prv->dmq1, key->dmq1); |
98 | BN_copy(prv->dmq1, key->dmq1); | 98 | |
99 | 99 | prv->iqmp = BN_new(); | |
100 | prv->iqmp = BN_new(); | 100 | BN_copy(prv->iqmp, key->iqmp); |
101 | BN_copy(prv->iqmp, key->iqmp); | 101 | |
102 | 102 | RSA_free(key); | |
103 | RSA_free(key); | 103 | |
104 | 104 | if (rsa_verbose) | |
105 | if (rsa_verbose) | 105 | printf("Key generation complete.\n"); |
106 | printf("Key generation complete.\n"); | ||
107 | } | 106 | } |
108 | 107 | ||
109 | void | 108 | void |
110 | rsa_public_encrypt(BIGNUM *out, BIGNUM *in, RSA* key) | 109 | rsa_public_encrypt(BIGNUM *out, BIGNUM *in, RSA *key) |
111 | { | 110 | { |
112 | char *inbuf, *outbuf; | 111 | char *inbuf, *outbuf; |
113 | int len, ilen, olen; | 112 | int len, ilen, olen; |
114 | 113 | ||
115 | if (BN_num_bits(key->e) < 2 || !BN_is_odd(key->e)) | 114 | if (BN_num_bits(key->e) < 2 || !BN_is_odd(key->e)) |
116 | fatal("rsa_public_encrypt() exponent too small or not odd"); | 115 | fatal("rsa_public_encrypt() exponent too small or not odd"); |
117 | 116 | ||
118 | olen = BN_num_bytes(key->n); | 117 | olen = BN_num_bytes(key->n); |
119 | outbuf = xmalloc(olen); | 118 | outbuf = xmalloc(olen); |
120 | 119 | ||
121 | ilen = BN_num_bytes(in); | 120 | ilen = BN_num_bytes(in); |
122 | inbuf = xmalloc(ilen); | 121 | inbuf = xmalloc(ilen); |
123 | BN_bn2bin(in, inbuf); | 122 | BN_bn2bin(in, inbuf); |
124 | 123 | ||
125 | if ((len = RSA_public_encrypt(ilen, inbuf, outbuf, key, | 124 | if ((len = RSA_public_encrypt(ilen, inbuf, outbuf, key, |
126 | RSA_PKCS1_PADDING)) <= 0) | 125 | RSA_PKCS1_PADDING)) <= 0) |
127 | fatal("rsa_public_encrypt() failed"); | 126 | fatal("rsa_public_encrypt() failed"); |
128 | 127 | ||
129 | BN_bin2bn(outbuf, len, out); | 128 | BN_bin2bn(outbuf, len, out); |
130 | 129 | ||
131 | memset(outbuf, 0, olen); | 130 | memset(outbuf, 0, olen); |
132 | memset(inbuf, 0, ilen); | 131 | memset(inbuf, 0, ilen); |
133 | xfree(outbuf); | 132 | xfree(outbuf); |
134 | xfree(inbuf); | 133 | xfree(inbuf); |
135 | } | 134 | } |
136 | 135 | ||
137 | void | 136 | void |
138 | rsa_private_decrypt(BIGNUM *out, BIGNUM *in, RSA *key) | 137 | rsa_private_decrypt(BIGNUM *out, BIGNUM *in, RSA *key) |
139 | { | 138 | { |
140 | char *inbuf, *outbuf; | 139 | char *inbuf, *outbuf; |
141 | int len, ilen, olen; | 140 | int len, ilen, olen; |
142 | 141 | ||
143 | olen = BN_num_bytes(key->n); | 142 | olen = BN_num_bytes(key->n); |
144 | outbuf = xmalloc(olen); | 143 | outbuf = xmalloc(olen); |
145 | 144 | ||
146 | ilen = BN_num_bytes(in); | 145 | ilen = BN_num_bytes(in); |
147 | inbuf = xmalloc(ilen); | 146 | inbuf = xmalloc(ilen); |
148 | BN_bn2bin(in, inbuf); | 147 | BN_bn2bin(in, inbuf); |
149 | 148 | ||
150 | if ((len = RSA_private_decrypt(ilen, inbuf, outbuf, key, | 149 | if ((len = RSA_private_decrypt(ilen, inbuf, outbuf, key, |
151 | RSA_SSLV23_PADDING)) <= 0) | 150 | RSA_SSLV23_PADDING)) <= 0) |
152 | fatal("rsa_private_decrypt() failed"); | 151 | fatal("rsa_private_decrypt() failed"); |
153 | 152 | ||
154 | BN_bin2bn(outbuf, len, out); | 153 | BN_bin2bn(outbuf, len, out); |
155 | 154 | ||
156 | memset(outbuf, 0, olen); | 155 | memset(outbuf, 0, olen); |
157 | memset(inbuf, 0, ilen); | 156 | memset(inbuf, 0, ilen); |
158 | xfree(outbuf); | 157 | xfree(outbuf); |
159 | xfree(inbuf); | 158 | xfree(inbuf); |
160 | } | 159 | } |
161 | 160 | ||
162 | /* Set whether to output verbose messages during key generation. */ | 161 | /* Set whether to output verbose messages during key generation. */ |
@@ -164,5 +163,5 @@ rsa_private_decrypt(BIGNUM *out, BIGNUM *in, RSA *key) | |||
164 | void | 163 | void |
165 | rsa_set_verbose(int verbose) | 164 | rsa_set_verbose(int verbose) |
166 | { | 165 | { |
167 | rsa_verbose = verbose; | 166 | rsa_verbose = verbose; |
168 | } | 167 | } |
@@ -1,24 +1,25 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * | |
3 | rsa.h | 3 | * rsa.h |
4 | 4 | * | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
6 | 6 | * | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
8 | All rights reserved | 8 | * All rights reserved |
9 | 9 | * | |
10 | Created: Fri Mar 3 22:01:06 1995 ylo | 10 | * Created: Fri Mar 3 22:01:06 1995 ylo |
11 | 11 | * | |
12 | RSA key generation, encryption and decryption. | 12 | * RSA key generation, encryption and decryption. |
13 | 13 | * | |
14 | */ | 14 | */ |
15 | 15 | ||
16 | /* RCSID("$Id: rsa.h,v 1.3 1999/11/10 23:40:23 damien Exp $"); */ | 16 | /* RCSID("$Id: rsa.h,v 1.4 1999/11/24 13:26:22 damien Exp $"); */ |
17 | #include "config.h" | ||
18 | 17 | ||
19 | #ifndef RSA_H | 18 | #ifndef RSA_H |
20 | #define RSA_H | 19 | #define RSA_H |
21 | 20 | ||
21 | #include "config.h" | ||
22 | |||
22 | #ifdef HAVE_OPENSSL | 23 | #ifdef HAVE_OPENSSL |
23 | #include <openssl/bn.h> | 24 | #include <openssl/bn.h> |
24 | #include <openssl/rsa.h> | 25 | #include <openssl/rsa.h> |
@@ -30,15 +31,15 @@ RSA key generation, encryption and decryption. | |||
30 | #endif | 31 | #endif |
31 | 32 | ||
32 | /* Calls SSL RSA_generate_key, only copies to prv and pub */ | 33 | /* Calls SSL RSA_generate_key, only copies to prv and pub */ |
33 | void rsa_generate_key(RSA *prv, RSA *pub, unsigned int bits); | 34 | void rsa_generate_key(RSA * prv, RSA * pub, unsigned int bits); |
34 | 35 | ||
35 | /* Indicates whether the rsa module is permitted to show messages on | 36 | /* Indicates whether the rsa module is permitted to show messages on |
36 | the terminal. */ | 37 | the terminal. */ |
37 | void rsa_set_verbose(int verbose); | 38 | void rsa_set_verbose __P((int verbose)); |
38 | 39 | ||
39 | int rsa_alive(void); | 40 | int rsa_alive __P((void)); |
40 | 41 | ||
41 | void rsa_public_encrypt(BIGNUM *out, BIGNUM *in, RSA *prv); | 42 | void rsa_public_encrypt __P((BIGNUM * out, BIGNUM * in, RSA * prv)); |
42 | void rsa_private_decrypt(BIGNUM *out, BIGNUM *in, RSA *prv); | 43 | void rsa_private_decrypt __P((BIGNUM * out, BIGNUM * in, RSA * prv)); |
43 | 44 | ||
44 | #endif /* RSA_H */ | 45 | #endif /* RSA_H */ |
@@ -1,13 +1,13 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * | |
3 | scp - secure remote copy. This is basically patched BSD rcp which uses ssh | 3 | * scp - secure remote copy. This is basically patched BSD rcp which uses ssh |
4 | to do the data transfer (instead of using rcmd). | 4 | * to do the data transfer (instead of using rcmd). |
5 | 5 | * | |
6 | NOTE: This version should NOT be suid root. (This uses ssh to do the transfer | 6 | * NOTE: This version should NOT be suid root. (This uses ssh to do the transfer |
7 | and ssh has the necessary privileges.) | 7 | * and ssh has the necessary privileges.) |
8 | 8 | * | |
9 | 1995 Timo Rinne <tri@iki.fi>, Tatu Ylonen <ylo@cs.hut.fi> | 9 | * 1995 Timo Rinne <tri@iki.fi>, Tatu Ylonen <ylo@cs.hut.fi> |
10 | 10 | * | |
11 | */ | 11 | */ |
12 | 12 | ||
13 | /* | 13 | /* |
@@ -42,11 +42,10 @@ and ssh has the necessary privileges.) | |||
42 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | 42 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
43 | * SUCH DAMAGE. | 43 | * SUCH DAMAGE. |
44 | * | 44 | * |
45 | * $Id: scp.c,v 1.8 1999/11/19 01:34:14 damien Exp $ | ||
46 | */ | 45 | */ |
47 | 46 | ||
48 | #include "includes.h" | 47 | #include "includes.h" |
49 | RCSID("$Id: scp.c,v 1.8 1999/11/19 01:34:14 damien Exp $"); | 48 | RCSID("$Id: scp.c,v 1.9 1999/11/24 13:26:22 damien Exp $"); |
50 | 49 | ||
51 | #include "ssh.h" | 50 | #include "ssh.h" |
52 | #include "xmalloc.h" | 51 | #include "xmalloc.h" |
@@ -76,7 +75,7 @@ off_t totalbytes = 0; | |||
76 | char *curfile; | 75 | char *curfile; |
77 | 76 | ||
78 | /* This is set to non-zero to enable verbose mode. */ | 77 | /* This is set to non-zero to enable verbose mode. */ |
79 | int verbose = 0; | 78 | int verbose_mode = 0; |
80 | 79 | ||
81 | /* This is set to non-zero if compression is desired. */ | 80 | /* This is set to non-zero if compression is desired. */ |
82 | int compress = 0; | 81 | int compress = 0; |
@@ -91,7 +90,7 @@ int batchmode = 0; | |||
91 | /* This is set to the cipher type string if given on the command line. */ | 90 | /* This is set to the cipher type string if given on the command line. */ |
92 | char *cipher = NULL; | 91 | char *cipher = NULL; |
93 | 92 | ||
94 | /* This is set to the RSA authentication identity file name if given on | 93 | /* This is set to the RSA authentication identity file name if given on |
95 | the command line. */ | 94 | the command line. */ |
96 | char *identity = NULL; | 95 | char *identity = NULL; |
97 | 96 | ||
@@ -102,98 +101,95 @@ char *port = NULL; | |||
102 | host. This returns < 0 if execution fails, and >= 0 otherwise. | 101 | host. This returns < 0 if execution fails, and >= 0 otherwise. |
103 | This assigns the input and output file descriptors on success. */ | 102 | This assigns the input and output file descriptors on success. */ |
104 | 103 | ||
105 | int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout) | 104 | int |
105 | do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout) | ||
106 | { | 106 | { |
107 | int pin[2], pout[2], reserved[2]; | 107 | int pin[2], pout[2], reserved[2]; |
108 | 108 | ||
109 | if (verbose) | 109 | if (verbose_mode) |
110 | fprintf(stderr, "Executing: host %s, user %s, command %s\n", | 110 | fprintf(stderr, "Executing: host %s, user %s, command %s\n", |
111 | host, remuser ? remuser : "(unspecified)", cmd); | 111 | host, remuser ? remuser : "(unspecified)", cmd); |
112 | 112 | ||
113 | /* Reserve two descriptors so that the real pipes won't get descriptors | 113 | /* Reserve two descriptors so that the real pipes won't get |
114 | 0 and 1 because that will screw up dup2 below. */ | 114 | descriptors 0 and 1 because that will screw up dup2 below. */ |
115 | pipe(reserved); | 115 | pipe(reserved); |
116 | 116 | ||
117 | /* Create a socket pair for communicating with ssh. */ | 117 | /* Create a socket pair for communicating with ssh. */ |
118 | if (pipe(pin) < 0) | 118 | if (pipe(pin) < 0) |
119 | fatal("pipe: %s", strerror(errno)); | 119 | fatal("pipe: %s", strerror(errno)); |
120 | if (pipe(pout) < 0) | 120 | if (pipe(pout) < 0) |
121 | fatal("pipe: %s", strerror(errno)); | 121 | fatal("pipe: %s", strerror(errno)); |
122 | 122 | ||
123 | /* Free the reserved descriptors. */ | 123 | /* Free the reserved descriptors. */ |
124 | close(reserved[0]); | 124 | close(reserved[0]); |
125 | close(reserved[1]); | 125 | close(reserved[1]); |
126 | 126 | ||
127 | /* For a child to execute the command on the remote host using ssh. */ | 127 | /* For a child to execute the command on the remote host using ssh. */ |
128 | if (fork() == 0) | 128 | if (fork() == 0) { |
129 | { | 129 | char *args[100]; |
130 | char *args[100]; | 130 | unsigned int i; |
131 | unsigned int i; | 131 | |
132 | 132 | /* Child. */ | |
133 | /* Child. */ | 133 | close(pin[1]); |
134 | close(pin[1]); | 134 | close(pout[0]); |
135 | close(pout[0]); | 135 | dup2(pin[0], 0); |
136 | dup2(pin[0], 0); | 136 | dup2(pout[1], 1); |
137 | dup2(pout[1], 1); | 137 | close(pin[0]); |
138 | close(pin[0]); | 138 | close(pout[1]); |
139 | close(pout[1]); | 139 | |
140 | 140 | i = 0; | |
141 | i = 0; | 141 | args[i++] = SSH_PROGRAM; |
142 | args[i++] = SSH_PROGRAM; | 142 | args[i++] = "-x"; |
143 | args[i++] = "-x"; | 143 | args[i++] = "-oFallBackToRsh no"; |
144 | args[i++] = "-oFallBackToRsh no"; | 144 | if (verbose_mode) |
145 | if (verbose) | 145 | args[i++] = "-v"; |
146 | args[i++] = "-v"; | 146 | if (compress) |
147 | if (compress) | 147 | args[i++] = "-C"; |
148 | args[i++] = "-C"; | 148 | if (batchmode) |
149 | if (batchmode) | 149 | args[i++] = "-oBatchMode yes"; |
150 | args[i++] = "-oBatchMode yes"; | 150 | if (cipher != NULL) { |
151 | if (cipher != NULL) | 151 | args[i++] = "-c"; |
152 | { | 152 | args[i++] = cipher; |
153 | args[i++] = "-c"; | 153 | } |
154 | args[i++] = cipher; | 154 | if (identity != NULL) { |
155 | } | 155 | args[i++] = "-i"; |
156 | if (identity != NULL) | 156 | args[i++] = identity; |
157 | { | 157 | } |
158 | args[i++] = "-i"; | 158 | if (port != NULL) { |
159 | args[i++] = identity; | 159 | args[i++] = "-p"; |
160 | } | 160 | args[i++] = port; |
161 | if (port != NULL) | 161 | } |
162 | { | 162 | if (remuser != NULL) { |
163 | args[i++] = "-p"; | 163 | args[i++] = "-l"; |
164 | args[i++] = port; | 164 | args[i++] = remuser; |
165 | } | 165 | } |
166 | if (remuser != NULL) | 166 | args[i++] = host; |
167 | { | 167 | args[i++] = cmd; |
168 | args[i++] = "-l"; | 168 | args[i++] = NULL; |
169 | args[i++] = remuser; | 169 | |
170 | execvp(SSH_PROGRAM, args); | ||
171 | perror(SSH_PROGRAM); | ||
172 | exit(1); | ||
170 | } | 173 | } |
171 | args[i++] = host; | 174 | /* Parent. Close the other side, and return the local side. */ |
172 | args[i++] = cmd; | 175 | close(pin[0]); |
173 | args[i++] = NULL; | 176 | *fdout = pin[1]; |
174 | 177 | close(pout[1]); | |
175 | execvp(SSH_PROGRAM, args); | 178 | *fdin = pout[0]; |
176 | perror(SSH_PROGRAM); | 179 | return 0; |
177 | exit(1); | ||
178 | } | ||
179 | /* Parent. Close the other side, and return the local side. */ | ||
180 | close(pin[0]); | ||
181 | *fdout = pin[1]; | ||
182 | close(pout[1]); | ||
183 | *fdin = pout[0]; | ||
184 | return 0; | ||
185 | } | 180 | } |
186 | 181 | ||
187 | void fatal(const char *fmt, ...) | 182 | void |
183 | fatal(const char *fmt,...) | ||
188 | { | 184 | { |
189 | va_list ap; | 185 | va_list ap; |
190 | char buf[1024]; | 186 | char buf[1024]; |
191 | 187 | ||
192 | va_start(ap, fmt); | 188 | va_start(ap, fmt); |
193 | vsnprintf(buf, sizeof(buf), fmt, ap); | 189 | vsnprintf(buf, sizeof(buf), fmt, ap); |
194 | va_end(ap); | 190 | va_end(ap); |
195 | fprintf(stderr, "%s\n", buf); | 191 | fprintf(stderr, "%s\n", buf); |
196 | exit(255); | 192 | exit(255); |
197 | } | 193 | } |
198 | 194 | ||
199 | /* This stuff used to be in BSD rcp extern.h. */ | 195 | /* This stuff used to be in BSD rcp extern.h. */ |
@@ -205,31 +201,31 @@ typedef struct { | |||
205 | 201 | ||
206 | extern int iamremote; | 202 | extern int iamremote; |
207 | 203 | ||
208 | BUF *allocbuf(BUF *, int, int); | 204 | BUF *allocbuf(BUF *, int, int); |
209 | char *colon(char *); | 205 | char *colon(char *); |
210 | void lostconn(int); | 206 | void lostconn(int); |
211 | void nospace(void); | 207 | void nospace(void); |
212 | int okname(char *); | 208 | int okname(char *); |
213 | void run_err(const char *, ...); | 209 | void run_err(const char *,...); |
214 | void verifydir(char *); | 210 | void verifydir(char *); |
215 | 211 | ||
216 | /* Stuff from BSD rcp.c continues. */ | 212 | /* Stuff from BSD rcp.c continues. */ |
217 | 213 | ||
218 | struct passwd *pwd; | 214 | struct passwd *pwd; |
219 | uid_t userid; | 215 | uid_t userid; |
220 | int errs, remin, remout; | 216 | int errs, remin, remout; |
221 | int pflag, iamremote, iamrecursive, targetshouldbedirectory; | 217 | int pflag, iamremote, iamrecursive, targetshouldbedirectory; |
222 | 218 | ||
223 | #define CMDNEEDS 64 | 219 | #define CMDNEEDS 64 |
224 | char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ | 220 | char cmd[CMDNEEDS]; /* must hold "rcp -r -p -d\0" */ |
225 | 221 | ||
226 | int response(void); | 222 | int response(void); |
227 | void rsource(char *, struct stat *); | 223 | void rsource(char *, struct stat *); |
228 | void sink(int, char *[]); | 224 | void sink(int, char *[]); |
229 | void source(int, char *[]); | 225 | void source(int, char *[]); |
230 | void tolocal(int, char *[]); | 226 | void tolocal(int, char *[]); |
231 | void toremote(char *, int, char *[]); | 227 | void toremote(char *, int, char *[]); |
232 | void usage(void); | 228 | void usage(void); |
233 | 229 | ||
234 | int | 230 | int |
235 | main(argc, argv) | 231 | main(argc, argv) |
@@ -242,47 +238,48 @@ main(argc, argv) | |||
242 | extern int optind; | 238 | extern int optind; |
243 | 239 | ||
244 | fflag = tflag = 0; | 240 | fflag = tflag = 0; |
245 | while ((ch = getopt(argc, argv, "dfprtvBCc:i:P:q")) != EOF) | 241 | while ((ch = getopt(argc, argv, "dfprtvBCc:i:P:q")) != EOF) |
246 | switch(ch) { /* User-visible flags. */ | 242 | switch (ch) { |
243 | /* User-visible flags. */ | ||
247 | case 'p': | 244 | case 'p': |
248 | pflag = 1; | 245 | pflag = 1; |
249 | break; | 246 | break; |
250 | case 'P': | 247 | case 'P': |
251 | port = optarg; | 248 | port = optarg; |
252 | break; | 249 | break; |
253 | case 'r': | 250 | case 'r': |
254 | iamrecursive = 1; | 251 | iamrecursive = 1; |
255 | break; | 252 | break; |
256 | /* Server options. */ | 253 | /* Server options. */ |
257 | case 'd': | 254 | case 'd': |
258 | targetshouldbedirectory = 1; | 255 | targetshouldbedirectory = 1; |
259 | break; | 256 | break; |
260 | case 'f': /* "from" */ | 257 | case 'f': /* "from" */ |
261 | iamremote = 1; | 258 | iamremote = 1; |
262 | fflag = 1; | 259 | fflag = 1; |
263 | break; | 260 | break; |
264 | case 't': /* "to" */ | 261 | case 't': /* "to" */ |
265 | iamremote = 1; | 262 | iamremote = 1; |
266 | tflag = 1; | 263 | tflag = 1; |
267 | break; | 264 | break; |
268 | case 'c': | 265 | case 'c': |
269 | cipher = optarg; | 266 | cipher = optarg; |
270 | break; | 267 | break; |
271 | case 'i': | 268 | case 'i': |
272 | identity = optarg; | 269 | identity = optarg; |
273 | break; | 270 | break; |
274 | case 'v': | 271 | case 'v': |
275 | verbose = 1; | 272 | verbose_mode = 1; |
276 | break; | 273 | break; |
277 | case 'B': | 274 | case 'B': |
278 | batchmode = 1; | 275 | batchmode = 1; |
279 | break; | 276 | break; |
280 | case 'C': | 277 | case 'C': |
281 | compress = 1; | 278 | compress = 1; |
282 | break; | 279 | break; |
283 | case 'q': | 280 | case 'q': |
284 | showprogress = 0; | 281 | showprogress = 0; |
285 | break; | 282 | break; |
286 | case '?': | 283 | case '?': |
287 | default: | 284 | default: |
288 | usage(); | 285 | usage(); |
@@ -291,25 +288,25 @@ main(argc, argv) | |||
291 | argv += optind; | 288 | argv += optind; |
292 | 289 | ||
293 | if ((pwd = getpwuid(userid = getuid())) == NULL) | 290 | if ((pwd = getpwuid(userid = getuid())) == NULL) |
294 | fatal("unknown user %d", (int)userid); | 291 | fatal("unknown user %d", (int) userid); |
295 | 292 | ||
296 | if (! isatty(STDERR_FILENO)) | 293 | if (!isatty(STDERR_FILENO)) |
297 | showprogress = 0; | 294 | showprogress = 0; |
298 | 295 | ||
299 | remin = STDIN_FILENO; | 296 | remin = STDIN_FILENO; |
300 | remout = STDOUT_FILENO; | 297 | remout = STDOUT_FILENO; |
301 | 298 | ||
302 | if (fflag) { /* Follow "protocol", send data. */ | 299 | if (fflag) { |
303 | (void)response(); | 300 | /* Follow "protocol", send data. */ |
301 | (void) response(); | ||
304 | source(argc, argv); | 302 | source(argc, argv); |
305 | exit(errs != 0); | 303 | exit(errs != 0); |
306 | } | 304 | } |
307 | 305 | if (tflag) { | |
308 | if (tflag) { /* Receive data. */ | 306 | /* Receive data. */ |
309 | sink(argc, argv); | 307 | sink(argc, argv); |
310 | exit(errs != 0); | 308 | exit(errs != 0); |
311 | } | 309 | } |
312 | |||
313 | if (argc < 2) | 310 | if (argc < 2) |
314 | usage(); | 311 | usage(); |
315 | if (argc > 2) | 312 | if (argc > 2) |
@@ -317,16 +314,16 @@ main(argc, argv) | |||
317 | 314 | ||
318 | remin = remout = -1; | 315 | remin = remout = -1; |
319 | /* Command to be executed on remote system using "ssh". */ | 316 | /* Command to be executed on remote system using "ssh". */ |
320 | (void)sprintf(cmd, "scp%s%s%s%s", verbose ? " -v" : "", | 317 | (void) sprintf(cmd, "scp%s%s%s%s", verbose_mode ? " -v" : "", |
321 | iamrecursive ? " -r" : "", pflag ? " -p" : "", | 318 | iamrecursive ? " -r" : "", pflag ? " -p" : "", |
322 | targetshouldbedirectory ? " -d" : ""); | 319 | targetshouldbedirectory ? " -d" : ""); |
323 | 320 | ||
324 | (void)signal(SIGPIPE, lostconn); | 321 | (void) signal(SIGPIPE, lostconn); |
325 | 322 | ||
326 | if ((targ = colon(argv[argc - 1]))) /* Dest is remote host. */ | 323 | if ((targ = colon(argv[argc - 1]))) /* Dest is remote host. */ |
327 | toremote(targ, argc, argv); | 324 | toremote(targ, argc, argv); |
328 | else { | 325 | else { |
329 | tolocal(argc, argv); /* Dest is local host. */ | 326 | tolocal(argc, argv); /* Dest is local host. */ |
330 | if (targetshouldbedirectory) | 327 | if (targetshouldbedirectory) |
331 | verifydir(argv[argc - 1]); | 328 | verifydir(argv[argc - 1]); |
332 | } | 329 | } |
@@ -360,15 +357,15 @@ toremote(targ, argc, argv) | |||
360 | 357 | ||
361 | for (i = 0; i < argc - 1; i++) { | 358 | for (i = 0; i < argc - 1; i++) { |
362 | src = colon(argv[i]); | 359 | src = colon(argv[i]); |
363 | if (src) { /* remote to remote */ | 360 | if (src) { /* remote to remote */ |
364 | *src++ = 0; | 361 | *src++ = 0; |
365 | if (*src == 0) | 362 | if (*src == 0) |
366 | src = "."; | 363 | src = "."; |
367 | host = strchr(argv[i], '@'); | 364 | host = strchr(argv[i], '@'); |
368 | len = strlen(SSH_PROGRAM) + strlen(argv[i]) + | 365 | len = strlen(SSH_PROGRAM) + strlen(argv[i]) + |
369 | strlen(src) + (tuser ? strlen(tuser) : 0) + | 366 | strlen(src) + (tuser ? strlen(tuser) : 0) + |
370 | strlen(thost) + strlen(targ) + CMDNEEDS + 32; | 367 | strlen(thost) + strlen(targ) + CMDNEEDS + 32; |
371 | bp = xmalloc(len); | 368 | bp = xmalloc(len); |
372 | if (host) { | 369 | if (host) { |
373 | *host++ = 0; | 370 | *host++ = 0; |
374 | suser = argv[i]; | 371 | suser = argv[i]; |
@@ -376,37 +373,37 @@ toremote(targ, argc, argv) | |||
376 | suser = pwd->pw_name; | 373 | suser = pwd->pw_name; |
377 | else if (!okname(suser)) | 374 | else if (!okname(suser)) |
378 | continue; | 375 | continue; |
379 | (void)sprintf(bp, | 376 | (void) sprintf(bp, |
380 | "%s%s -x -o'FallBackToRsh no' -n -l %s %s %s %s '%s%s%s:%s'", | 377 | "%s%s -x -o'FallBackToRsh no' -n -l %s %s %s %s '%s%s%s:%s'", |
381 | SSH_PROGRAM, verbose ? " -v" : "", | 378 | SSH_PROGRAM, verbose_mode ? " -v" : "", |
382 | suser, host, cmd, src, | 379 | suser, host, cmd, src, |
383 | tuser ? tuser : "", tuser ? "@" : "", | 380 | tuser ? tuser : "", tuser ? "@" : "", |
384 | thost, targ); | 381 | thost, targ); |
385 | } else | 382 | } else |
386 | (void)sprintf(bp, | 383 | (void) sprintf(bp, |
387 | "exec %s%s -x -o'FallBackToRsh no' -n %s %s %s '%s%s%s:%s'", | 384 | "exec %s%s -x -o'FallBackToRsh no' -n %s %s %s '%s%s%s:%s'", |
388 | SSH_PROGRAM, verbose ? " -v" : "", | 385 | SSH_PROGRAM, verbose_mode ? " -v" : "", |
389 | argv[i], cmd, src, | 386 | argv[i], cmd, src, |
390 | tuser ? tuser : "", tuser ? "@" : "", | 387 | tuser ? tuser : "", tuser ? "@" : "", |
391 | thost, targ); | 388 | thost, targ); |
392 | if (verbose) | 389 | if (verbose_mode) |
393 | fprintf(stderr, "Executing: %s\n", bp); | 390 | fprintf(stderr, "Executing: %s\n", bp); |
394 | (void)system(bp); | 391 | (void) system(bp); |
395 | (void)xfree(bp); | 392 | (void) xfree(bp); |
396 | } else { /* local to remote */ | 393 | } else { /* local to remote */ |
397 | if (remin == -1) { | 394 | if (remin == -1) { |
398 | len = strlen(targ) + CMDNEEDS + 20; | 395 | len = strlen(targ) + CMDNEEDS + 20; |
399 | bp = xmalloc(len); | 396 | bp = xmalloc(len); |
400 | (void)sprintf(bp, "%s -t %s", cmd, targ); | 397 | (void) sprintf(bp, "%s -t %s", cmd, targ); |
401 | host = thost; | 398 | host = thost; |
402 | if (do_cmd(host, tuser, | 399 | if (do_cmd(host, tuser, |
403 | bp, &remin, &remout) < 0) | 400 | bp, &remin, &remout) < 0) |
404 | exit(1); | 401 | exit(1); |
405 | if (response() < 0) | 402 | if (response() < 0) |
406 | exit(1); | 403 | exit(1); |
407 | (void)xfree(bp); | 404 | (void) xfree(bp); |
408 | } | 405 | } |
409 | source(1, argv+i); | 406 | source(1, argv + i); |
410 | } | 407 | } |
411 | } | 408 | } |
412 | } | 409 | } |
@@ -420,18 +417,18 @@ tolocal(argc, argv) | |||
420 | char *bp, *host, *src, *suser; | 417 | char *bp, *host, *src, *suser; |
421 | 418 | ||
422 | for (i = 0; i < argc - 1; i++) { | 419 | for (i = 0; i < argc - 1; i++) { |
423 | if (!(src = colon(argv[i]))) { /* Local to local. */ | 420 | if (!(src = colon(argv[i]))) { /* Local to local. */ |
424 | len = strlen(_PATH_CP) + strlen(argv[i]) + | 421 | len = strlen(_PATH_CP) + strlen(argv[i]) + |
425 | strlen(argv[argc - 1]) + 20; | 422 | strlen(argv[argc - 1]) + 20; |
426 | bp = xmalloc(len); | 423 | bp = xmalloc(len); |
427 | (void)sprintf(bp, "exec %s%s%s %s %s", _PATH_CP, | 424 | (void) sprintf(bp, "exec %s%s%s %s %s", _PATH_CP, |
428 | iamrecursive ? " -r" : "", pflag ? " -p" : "", | 425 | iamrecursive ? " -r" : "", pflag ? " -p" : "", |
429 | argv[i], argv[argc - 1]); | 426 | argv[i], argv[argc - 1]); |
430 | if (verbose) | 427 | if (verbose_mode) |
431 | fprintf(stderr, "Executing: %s\n", bp); | 428 | fprintf(stderr, "Executing: %s\n", bp); |
432 | if (system(bp)) | 429 | if (system(bp)) |
433 | ++errs; | 430 | ++errs; |
434 | (void)xfree(bp); | 431 | (void) xfree(bp); |
435 | continue; | 432 | continue; |
436 | } | 433 | } |
437 | *src++ = 0; | 434 | *src++ = 0; |
@@ -449,16 +446,16 @@ tolocal(argc, argv) | |||
449 | continue; | 446 | continue; |
450 | } | 447 | } |
451 | len = strlen(src) + CMDNEEDS + 20; | 448 | len = strlen(src) + CMDNEEDS + 20; |
452 | bp = xmalloc(len); | 449 | bp = xmalloc(len); |
453 | (void)sprintf(bp, "%s -f %s", cmd, src); | 450 | (void) sprintf(bp, "%s -f %s", cmd, src); |
454 | if (do_cmd(host, suser, bp, &remin, &remout) < 0) { | 451 | if (do_cmd(host, suser, bp, &remin, &remout) < 0) { |
455 | (void)xfree(bp); | 452 | (void) xfree(bp); |
456 | ++errs; | 453 | ++errs; |
457 | continue; | 454 | continue; |
458 | } | 455 | } |
459 | xfree(bp); | 456 | xfree(bp); |
460 | sink(1, argv + argc - 1); | 457 | sink(1, argv + argc - 1); |
461 | (void)close(remin); | 458 | (void) close(remin); |
462 | remin = remout = -1; | 459 | remin = remout = -1; |
463 | } | 460 | } |
464 | } | 461 | } |
@@ -476,7 +473,7 @@ source(argc, argv) | |||
476 | char *last, *name, buf[2048]; | 473 | char *last, *name, buf[2048]; |
477 | 474 | ||
478 | for (indx = 0; indx < argc; ++indx) { | 475 | for (indx = 0; indx < argc; ++indx) { |
479 | name = argv[indx]; | 476 | name = argv[indx]; |
480 | statbytes = 0; | 477 | statbytes = 0; |
481 | if ((fd = open(name, O_RDONLY, 0)) < 0) | 478 | if ((fd = open(name, O_RDONLY, 0)) < 0) |
482 | goto syserr; | 479 | goto syserr; |
@@ -507,36 +504,33 @@ syserr: run_err("%s: %s", name, strerror(errno)); | |||
507 | * Make it compatible with possible future | 504 | * Make it compatible with possible future |
508 | * versions expecting microseconds. | 505 | * versions expecting microseconds. |
509 | */ | 506 | */ |
510 | (void)sprintf(buf, "T%lu 0 %lu 0\n", | 507 | (void) sprintf(buf, "T%lu 0 %lu 0\n", |
511 | (unsigned long)stb.st_mtime, | 508 | (unsigned long) stb.st_mtime, |
512 | (unsigned long)stb.st_atime); | 509 | (unsigned long) stb.st_atime); |
513 | (void)write(remout, buf, strlen(buf)); | 510 | (void) write(remout, buf, strlen(buf)); |
514 | if (response() < 0) | 511 | if (response() < 0) |
515 | goto next; | 512 | goto next; |
516 | } | 513 | } |
517 | #define FILEMODEMASK (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) | 514 | #define FILEMODEMASK (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO) |
518 | (void)sprintf(buf, "C%04o %lu %s\n", | 515 | (void) sprintf(buf, "C%04o %lu %s\n", |
519 | (unsigned int)(stb.st_mode & FILEMODEMASK), | 516 | (unsigned int) (stb.st_mode & FILEMODEMASK), |
520 | (unsigned long)stb.st_size, | 517 | (unsigned long) stb.st_size, |
521 | last); | 518 | last); |
522 | if (verbose) | 519 | if (verbose_mode) { |
523 | { | 520 | fprintf(stderr, "Sending file modes: %s", buf); |
524 | fprintf(stderr, "Sending file modes: %s", buf); | 521 | fflush(stderr); |
525 | fflush(stderr); | 522 | } |
526 | } | 523 | (void) write(remout, buf, strlen(buf)); |
527 | (void)write(remout, buf, strlen(buf)); | ||
528 | if (response() < 0) | 524 | if (response() < 0) |
529 | goto next; | 525 | goto next; |
530 | if ((bp = allocbuf(&buffer, fd, 2048)) == NULL) { | 526 | if ((bp = allocbuf(&buffer, fd, 2048)) == NULL) { |
531 | next: (void)close(fd); | 527 | next: (void) close(fd); |
532 | continue; | 528 | continue; |
533 | } | 529 | } |
534 | |||
535 | if (showprogress) { | 530 | if (showprogress) { |
536 | totalbytes = stb.st_size; | 531 | totalbytes = stb.st_size; |
537 | progressmeter(-1); | 532 | progressmeter(-1); |
538 | } | 533 | } |
539 | |||
540 | /* Keep writing after an error so that we stay sync'd up. */ | 534 | /* Keep writing after an error so that we stay sync'd up. */ |
541 | for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { | 535 | for (haderr = i = 0; i < stb.st_size; i += bp->cnt) { |
542 | amt = bp->cnt; | 536 | amt = bp->cnt; |
@@ -548,7 +542,7 @@ next: (void)close(fd); | |||
548 | haderr = result >= 0 ? EIO : errno; | 542 | haderr = result >= 0 ? EIO : errno; |
549 | } | 543 | } |
550 | if (haderr) | 544 | if (haderr) |
551 | (void)write(remout, bp->buf, amt); | 545 | (void) write(remout, bp->buf, amt); |
552 | else { | 546 | else { |
553 | result = write(remout, bp->buf, amt); | 547 | result = write(remout, bp->buf, amt); |
554 | if (result != amt) | 548 | if (result != amt) |
@@ -556,16 +550,16 @@ next: (void)close(fd); | |||
556 | statbytes += result; | 550 | statbytes += result; |
557 | } | 551 | } |
558 | } | 552 | } |
559 | if(showprogress) | 553 | if (showprogress) |
560 | progressmeter(1); | 554 | progressmeter(1); |
561 | 555 | ||
562 | if (close(fd) < 0 && !haderr) | 556 | if (close(fd) < 0 && !haderr) |
563 | haderr = errno; | 557 | haderr = errno; |
564 | if (!haderr) | 558 | if (!haderr) |
565 | (void)write(remout, "", 1); | 559 | (void) write(remout, "", 1); |
566 | else | 560 | else |
567 | run_err("%s: %s", name, strerror(haderr)); | 561 | run_err("%s: %s", name, strerror(haderr)); |
568 | (void)response(); | 562 | (void) response(); |
569 | } | 563 | } |
570 | } | 564 | } |
571 | 565 | ||
@@ -588,21 +582,21 @@ rsource(name, statp) | |||
588 | else | 582 | else |
589 | last++; | 583 | last++; |
590 | if (pflag) { | 584 | if (pflag) { |
591 | (void)sprintf(path, "T%lu 0 %lu 0\n", | 585 | (void) sprintf(path, "T%lu 0 %lu 0\n", |
592 | (unsigned long)statp->st_mtime, | 586 | (unsigned long) statp->st_mtime, |
593 | (unsigned long)statp->st_atime); | 587 | (unsigned long) statp->st_atime); |
594 | (void)write(remout, path, strlen(path)); | 588 | (void) write(remout, path, strlen(path)); |
595 | if (response() < 0) { | 589 | if (response() < 0) { |
596 | closedir(dirp); | 590 | closedir(dirp); |
597 | return; | 591 | return; |
598 | } | 592 | } |
599 | } | 593 | } |
600 | (void)sprintf(path, | 594 | (void) sprintf(path, "D%04o %d %.1024s\n", |
601 | "D%04o %d %.1024s\n", (unsigned int)(statp->st_mode & FILEMODEMASK), | 595 | (unsigned int) (statp->st_mode & FILEMODEMASK), |
602 | 0, last); | 596 | 0, last); |
603 | if (verbose) | 597 | if (verbose_mode) |
604 | fprintf(stderr, "Entering directory: %s", path); | 598 | fprintf(stderr, "Entering directory: %s", path); |
605 | (void)write(remout, path, strlen(path)); | 599 | (void) write(remout, path, strlen(path)); |
606 | if (response() < 0) { | 600 | if (response() < 0) { |
607 | closedir(dirp); | 601 | closedir(dirp); |
608 | return; | 602 | return; |
@@ -616,13 +610,13 @@ rsource(name, statp) | |||
616 | run_err("%s/%s: name too long", name, dp->d_name); | 610 | run_err("%s/%s: name too long", name, dp->d_name); |
617 | continue; | 611 | continue; |
618 | } | 612 | } |
619 | (void)sprintf(path, "%s/%s", name, dp->d_name); | 613 | (void) sprintf(path, "%s/%s", name, dp->d_name); |
620 | vect[0] = path; | 614 | vect[0] = path; |
621 | source(1, vect); | 615 | source(1, vect); |
622 | } | 616 | } |
623 | (void)closedir(dirp); | 617 | (void) closedir(dirp); |
624 | (void)write(remout, "E\n", 2); | 618 | (void) write(remout, "E\n", 2); |
625 | (void)response(); | 619 | (void) response(); |
626 | } | 620 | } |
627 | 621 | ||
628 | void | 622 | void |
@@ -632,21 +626,23 @@ sink(argc, argv) | |||
632 | { | 626 | { |
633 | static BUF buffer; | 627 | static BUF buffer; |
634 | struct stat stb; | 628 | struct stat stb; |
635 | enum { YES, NO, DISPLAYED } wrerr; | 629 | enum { |
630 | YES, NO, DISPLAYED | ||
631 | } wrerr; | ||
636 | BUF *bp; | 632 | BUF *bp; |
637 | off_t i, j; | 633 | off_t i, j; |
638 | int amt, count, exists, first, mask, mode, ofd, omode; | 634 | int amt, count, exists, first, mask, mode, ofd, omode; |
639 | int setimes, size, targisdir, wrerrno = 0; | 635 | int setimes, size, targisdir, wrerrno = 0; |
640 | char ch, *cp, *np, *targ, *why, *vect[1], buf[2048]; | 636 | char ch, *cp, *np, *targ, *why, *vect[1], buf[2048]; |
641 | struct utimbuf ut; | 637 | struct utimbuf ut; |
642 | int dummy_usec; | 638 | int dummy_usec; |
643 | 639 | ||
644 | #define SCREWUP(str) { why = str; goto screwup; } | 640 | #define SCREWUP(str) { why = str; goto screwup; } |
645 | 641 | ||
646 | setimes = targisdir = 0; | 642 | setimes = targisdir = 0; |
647 | mask = umask(0); | 643 | mask = umask(0); |
648 | if (!pflag) | 644 | if (!pflag) |
649 | (void)umask(mask); | 645 | (void) umask(mask); |
650 | if (argc != 1) { | 646 | if (argc != 1) { |
651 | run_err("ambiguous target"); | 647 | run_err("ambiguous target"); |
652 | exit(1); | 648 | exit(1); |
@@ -654,8 +650,8 @@ sink(argc, argv) | |||
654 | targ = *argv; | 650 | targ = *argv; |
655 | if (targetshouldbedirectory) | 651 | if (targetshouldbedirectory) |
656 | verifydir(targ); | 652 | verifydir(targ); |
657 | 653 | ||
658 | (void)write(remout, "", 1); | 654 | (void) write(remout, "", 1); |
659 | if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) | 655 | if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) |
660 | targisdir = 1; | 656 | targisdir = 1; |
661 | for (first = 1;; first = 0) { | 657 | for (first = 1;; first = 0) { |
@@ -673,18 +669,17 @@ sink(argc, argv) | |||
673 | 669 | ||
674 | if (buf[0] == '\01' || buf[0] == '\02') { | 670 | if (buf[0] == '\01' || buf[0] == '\02') { |
675 | if (iamremote == 0) | 671 | if (iamremote == 0) |
676 | (void)write(STDERR_FILENO, | 672 | (void) write(STDERR_FILENO, |
677 | buf + 1, strlen(buf + 1)); | 673 | buf + 1, strlen(buf + 1)); |
678 | if (buf[0] == '\02') | 674 | if (buf[0] == '\02') |
679 | exit(1); | 675 | exit(1); |
680 | ++errs; | 676 | ++errs; |
681 | continue; | 677 | continue; |
682 | } | 678 | } |
683 | if (buf[0] == 'E') { | 679 | if (buf[0] == 'E') { |
684 | (void)write(remout, "", 1); | 680 | (void) write(remout, "", 1); |
685 | return; | 681 | return; |
686 | } | 682 | } |
687 | |||
688 | if (ch == '\n') | 683 | if (ch == '\n') |
689 | *--cp = 0; | 684 | *--cp = 0; |
690 | 685 | ||
@@ -706,7 +701,7 @@ sink(argc, argv) | |||
706 | getnum(dummy_usec); | 701 | getnum(dummy_usec); |
707 | if (*cp++ != '\0') | 702 | if (*cp++ != '\0') |
708 | SCREWUP("atime.usec not delimited"); | 703 | SCREWUP("atime.usec not delimited"); |
709 | (void)write(remout, "", 1); | 704 | (void) write(remout, "", 1); |
710 | continue; | 705 | continue; |
711 | } | 706 | } |
712 | if (*cp != 'C' && *cp != 'D') { | 707 | if (*cp != 'C' && *cp != 'D') { |
@@ -732,7 +727,7 @@ sink(argc, argv) | |||
732 | if (*cp++ != ' ') | 727 | if (*cp++ != ' ') |
733 | SCREWUP("mode not delimited"); | 728 | SCREWUP("mode not delimited"); |
734 | 729 | ||
735 | for (size = 0; *cp >= '0' && *cp <= '9';) | 730 | for (size = 0; *cp >= '0' && *cp <= '9';) |
736 | size = size * 10 + (*cp++ - '0'); | 731 | size = size * 10 + (*cp++ - '0'); |
737 | if (*cp++ != ' ') | 732 | if (*cp++ != ' ') |
738 | SCREWUP("size not delimited"); | 733 | SCREWUP("size not delimited"); |
@@ -743,9 +738,9 @@ sink(argc, argv) | |||
743 | 738 | ||
744 | need = strlen(targ) + strlen(cp) + 250; | 739 | need = strlen(targ) + strlen(cp) + 250; |
745 | if (need > cursize) | 740 | if (need > cursize) |
746 | namebuf = xmalloc(need); | 741 | namebuf = xmalloc(need); |
747 | (void)sprintf(namebuf, "%s%s%s", targ, | 742 | (void) sprintf(namebuf, "%s%s%s", targ, |
748 | *targ ? "/" : "", cp); | 743 | *targ ? "/" : "", cp); |
749 | np = namebuf; | 744 | np = namebuf; |
750 | } else | 745 | } else |
751 | np = targ; | 746 | np = targ; |
@@ -759,9 +754,10 @@ sink(argc, argv) | |||
759 | goto bad; | 754 | goto bad; |
760 | } | 755 | } |
761 | if (pflag) | 756 | if (pflag) |
762 | (void)chmod(np, mode); | 757 | (void) chmod(np, mode); |
763 | } else { | 758 | } else { |
764 | /* Handle copying from a read-only directory */ | 759 | /* Handle copying from a read-only |
760 | directory */ | ||
765 | mod_flag = 1; | 761 | mod_flag = 1; |
766 | if (mkdir(np, mode | S_IRWXU) < 0) | 762 | if (mkdir(np, mode | S_IRWXU) < 0) |
767 | goto bad; | 763 | goto bad; |
@@ -771,22 +767,22 @@ sink(argc, argv) | |||
771 | if (setimes) { | 767 | if (setimes) { |
772 | setimes = 0; | 768 | setimes = 0; |
773 | if (utime(np, &ut) < 0) | 769 | if (utime(np, &ut) < 0) |
774 | run_err("%s: set times: %s", | 770 | run_err("%s: set times: %s", |
775 | np, strerror(errno)); | 771 | np, strerror(errno)); |
776 | } | 772 | } |
777 | if (mod_flag) | 773 | if (mod_flag) |
778 | (void)chmod(np, mode); | 774 | (void) chmod(np, mode); |
779 | continue; | 775 | continue; |
780 | } | 776 | } |
781 | omode = mode; | 777 | omode = mode; |
782 | mode |= S_IWRITE; | 778 | mode |= S_IWRITE; |
783 | if ((ofd = open(np, O_WRONLY|O_CREAT|O_TRUNC, mode)) < 0) { | 779 | if ((ofd = open(np, O_WRONLY | O_CREAT | O_TRUNC, mode)) < 0) { |
784 | bad: run_err("%s: %s", np, strerror(errno)); | 780 | bad: run_err("%s: %s", np, strerror(errno)); |
785 | continue; | 781 | continue; |
786 | } | 782 | } |
787 | (void)write(remout, "", 1); | 783 | (void) write(remout, "", 1); |
788 | if ((bp = allocbuf(&buffer, ofd, 4096)) == NULL) { | 784 | if ((bp = allocbuf(&buffer, ofd, 4096)) == NULL) { |
789 | (void)close(ofd); | 785 | (void) close(ofd); |
790 | continue; | 786 | continue; |
791 | } | 787 | } |
792 | cp = bp->buf; | 788 | cp = bp->buf; |
@@ -806,12 +802,12 @@ bad: run_err("%s: %s", np, strerror(errno)); | |||
806 | j = read(remin, cp, amt); | 802 | j = read(remin, cp, amt); |
807 | if (j <= 0) { | 803 | if (j <= 0) { |
808 | run_err("%s", j ? strerror(errno) : | 804 | run_err("%s", j ? strerror(errno) : |
809 | "dropped connection"); | 805 | "dropped connection"); |
810 | exit(1); | 806 | exit(1); |
811 | } | 807 | } |
812 | amt -= j; | 808 | amt -= j; |
813 | cp += j; | 809 | cp += j; |
814 | statbytes += j; | 810 | statbytes += j; |
815 | } while (amt > 0); | 811 | } while (amt > 0); |
816 | if (count == bp->cnt) { | 812 | if (count == bp->cnt) { |
817 | /* Keep reading so we stay sync'd up. */ | 813 | /* Keep reading so we stay sync'd up. */ |
@@ -819,7 +815,7 @@ bad: run_err("%s: %s", np, strerror(errno)); | |||
819 | j = write(ofd, bp->buf, count); | 815 | j = write(ofd, bp->buf, count); |
820 | if (j != count) { | 816 | if (j != count) { |
821 | wrerr = YES; | 817 | wrerr = YES; |
822 | wrerrno = j >= 0 ? EIO : errno; | 818 | wrerrno = j >= 0 ? EIO : errno; |
823 | } | 819 | } |
824 | } | 820 | } |
825 | count = 0; | 821 | count = 0; |
@@ -831,7 +827,7 @@ bad: run_err("%s: %s", np, strerror(errno)); | |||
831 | if (count != 0 && wrerr == NO && | 827 | if (count != 0 && wrerr == NO && |
832 | (j = write(ofd, bp->buf, count)) != count) { | 828 | (j = write(ofd, bp->buf, count)) != count) { |
833 | wrerr = YES; | 829 | wrerr = YES; |
834 | wrerrno = j >= 0 ? EIO : errno; | 830 | wrerrno = j >= 0 ? EIO : errno; |
835 | } | 831 | } |
836 | #if 0 | 832 | #if 0 |
837 | if (ftruncate(ofd, size)) { | 833 | if (ftruncate(ofd, size)) { |
@@ -843,29 +839,29 @@ bad: run_err("%s: %s", np, strerror(errno)); | |||
843 | if (exists || omode != mode) | 839 | if (exists || omode != mode) |
844 | if (fchmod(ofd, omode)) | 840 | if (fchmod(ofd, omode)) |
845 | run_err("%s: set mode: %s", | 841 | run_err("%s: set mode: %s", |
846 | np, strerror(errno)); | 842 | np, strerror(errno)); |
847 | } else { | 843 | } else { |
848 | if (!exists && omode != mode) | 844 | if (!exists && omode != mode) |
849 | if (fchmod(ofd, omode & ~mask)) | 845 | if (fchmod(ofd, omode & ~mask)) |
850 | run_err("%s: set mode: %s", | 846 | run_err("%s: set mode: %s", |
851 | np, strerror(errno)); | 847 | np, strerror(errno)); |
852 | } | 848 | } |
853 | (void)close(ofd); | 849 | (void) close(ofd); |
854 | (void)response(); | 850 | (void) response(); |
855 | if (setimes && wrerr == NO) { | 851 | if (setimes && wrerr == NO) { |
856 | setimes = 0; | 852 | setimes = 0; |
857 | if (utime(np, &ut) < 0) { | 853 | if (utime(np, &ut) < 0) { |
858 | run_err("%s: set times: %s", | 854 | run_err("%s: set times: %s", |
859 | np, strerror(errno)); | 855 | np, strerror(errno)); |
860 | wrerr = DISPLAYED; | 856 | wrerr = DISPLAYED; |
861 | } | 857 | } |
862 | } | 858 | } |
863 | switch(wrerr) { | 859 | switch (wrerr) { |
864 | case YES: | 860 | case YES: |
865 | run_err("%s: %s", np, strerror(wrerrno)); | 861 | run_err("%s: %s", np, strerror(wrerrno)); |
866 | break; | 862 | break; |
867 | case NO: | 863 | case NO: |
868 | (void)write(remout, "", 1); | 864 | (void) write(remout, "", 1); |
869 | break; | 865 | break; |
870 | case DISPLAYED: | 866 | case DISPLAYED: |
871 | break; | 867 | break; |
@@ -885,14 +881,14 @@ response() | |||
885 | lostconn(0); | 881 | lostconn(0); |
886 | 882 | ||
887 | cp = rbuf; | 883 | cp = rbuf; |
888 | switch(resp) { | 884 | switch (resp) { |
889 | case 0: /* ok */ | 885 | case 0: /* ok */ |
890 | return (0); | 886 | return (0); |
891 | default: | 887 | default: |
892 | *cp++ = resp; | 888 | *cp++ = resp; |
893 | /* FALLTHROUGH */ | 889 | /* FALLTHROUGH */ |
894 | case 1: /* error, followed by error msg */ | 890 | case 1: /* error, followed by error msg */ |
895 | case 2: /* fatal error, "" */ | 891 | case 2: /* fatal error, "" */ |
896 | do { | 892 | do { |
897 | if (read(remin, &ch, sizeof(ch)) != sizeof(ch)) | 893 | if (read(remin, &ch, sizeof(ch)) != sizeof(ch)) |
898 | lostconn(0); | 894 | lostconn(0); |
@@ -900,7 +896,7 @@ response() | |||
900 | } while (cp < &rbuf[sizeof(rbuf) - 1] && ch != '\n'); | 896 | } while (cp < &rbuf[sizeof(rbuf) - 1] && ch != '\n'); |
901 | 897 | ||
902 | if (!iamremote) | 898 | if (!iamremote) |
903 | (void)write(STDERR_FILENO, rbuf, cp - rbuf); | 899 | (void) write(STDERR_FILENO, rbuf, cp - rbuf); |
904 | ++errs; | 900 | ++errs; |
905 | if (resp == 1) | 901 | if (resp == 1) |
906 | return (-1); | 902 | return (-1); |
@@ -912,13 +908,13 @@ response() | |||
912 | void | 908 | void |
913 | usage() | 909 | usage() |
914 | { | 910 | { |
915 | (void)fprintf(stderr, | 911 | (void) fprintf(stderr, |
916 | "usage: scp [-pqrvC] [-P port] [-c cipher] [-i identity] f1 f2; or:\n scp [options] f1 ... fn directory\n"); | 912 | "usage: scp [-pqrvC] [-P port] [-c cipher] [-i identity] f1 f2; or:\n scp [options] f1 ... fn directory\n"); |
917 | exit(1); | 913 | exit(1); |
918 | } | 914 | } |
919 | 915 | ||
920 | void | 916 | void |
921 | run_err(const char *fmt, ...) | 917 | run_err(const char *fmt,...) |
922 | { | 918 | { |
923 | static FILE *fp; | 919 | static FILE *fp; |
924 | va_list ap; | 920 | va_list ap; |
@@ -927,18 +923,16 @@ run_err(const char *fmt, ...) | |||
927 | ++errs; | 923 | ++errs; |
928 | if (fp == NULL && !(fp = fdopen(remout, "w"))) | 924 | if (fp == NULL && !(fp = fdopen(remout, "w"))) |
929 | return; | 925 | return; |
930 | (void)fprintf(fp, "%c", 0x01); | 926 | (void) fprintf(fp, "%c", 0x01); |
931 | (void)fprintf(fp, "scp: "); | 927 | (void) fprintf(fp, "scp: "); |
932 | (void)vfprintf(fp, fmt, ap); | 928 | (void) vfprintf(fp, fmt, ap); |
933 | (void)fprintf(fp, "\n"); | 929 | (void) fprintf(fp, "\n"); |
934 | (void)fflush(fp); | 930 | (void) fflush(fp); |
935 | 931 | ||
936 | if (!iamremote) | 932 | if (!iamremote) { |
937 | { | 933 | vfprintf(stderr, fmt, ap); |
938 | vfprintf(stderr, fmt, ap); | 934 | fprintf(stderr, "\n"); |
939 | fprintf(stderr, "\n"); | 935 | } |
940 | } | ||
941 | |||
942 | va_end(ap); | 936 | va_end(ap); |
943 | } | 937 | } |
944 | 938 | ||
@@ -976,7 +970,7 @@ run_err(const char *fmt, ...) | |||
976 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | 970 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
977 | * SUCH DAMAGE. | 971 | * SUCH DAMAGE. |
978 | * | 972 | * |
979 | * $Id: scp.c,v 1.8 1999/11/19 01:34:14 damien Exp $ | 973 | * $Id: scp.c,v 1.9 1999/11/24 13:26:22 damien Exp $ |
980 | */ | 974 | */ |
981 | 975 | ||
982 | char * | 976 | char * |
@@ -1043,17 +1037,17 @@ allocbuf(bp, fd, blksize) | |||
1043 | run_err("fstat: %s", strerror(errno)); | 1037 | run_err("fstat: %s", strerror(errno)); |
1044 | return (0); | 1038 | return (0); |
1045 | } | 1039 | } |
1046 | if (stb.st_blksize == 0) | 1040 | if (stb.st_blksize == 0) |
1047 | size = blksize; | 1041 | size = blksize; |
1048 | else | 1042 | else |
1049 | size = blksize + (stb.st_blksize - blksize % stb.st_blksize) % | 1043 | size = blksize + (stb.st_blksize - blksize % stb.st_blksize) % |
1050 | stb.st_blksize; | 1044 | stb.st_blksize; |
1051 | if (bp->cnt >= size) | 1045 | if (bp->cnt >= size) |
1052 | return (bp); | 1046 | return (bp); |
1053 | if (bp->buf == NULL) | 1047 | if (bp->buf == NULL) |
1054 | bp->buf = xmalloc(size); | 1048 | bp->buf = xmalloc(size); |
1055 | else | 1049 | else |
1056 | bp->buf = xrealloc(bp->buf, size); | 1050 | bp->buf = xrealloc(bp->buf, size); |
1057 | bp->cnt = size; | 1051 | bp->cnt = size; |
1058 | return (bp); | 1052 | return (bp); |
1059 | } | 1053 | } |
@@ -1072,16 +1066,16 @@ lostconn(signo) | |||
1072 | */ | 1066 | */ |
1073 | int | 1067 | int |
1074 | atomicio(f, fd, s, n) | 1068 | atomicio(f, fd, s, n) |
1075 | int (*f)(); | 1069 | int (*f) (); |
1076 | char *s; | 1070 | char *s; |
1077 | { | 1071 | { |
1078 | int res, pos = 0; | 1072 | int res, pos = 0; |
1079 | 1073 | ||
1080 | while (n>pos) { | 1074 | while (n > pos) { |
1081 | res = (f)(fd, s+pos, n-pos); | 1075 | res = (f) (fd, s + pos, n - pos); |
1082 | switch (res) { | 1076 | switch (res) { |
1083 | case -1: | 1077 | case -1: |
1084 | if (errno==EINTR || errno==EAGAIN) | 1078 | if (errno == EINTR || errno == EAGAIN) |
1085 | continue; | 1079 | continue; |
1086 | case 0: | 1080 | case 0: |
1087 | return (res); | 1081 | return (res); |
@@ -1095,12 +1089,12 @@ char *s; | |||
1095 | void | 1089 | void |
1096 | alarmtimer(int wait) | 1090 | alarmtimer(int wait) |
1097 | { | 1091 | { |
1098 | struct itimerval itv; | 1092 | struct itimerval itv; |
1099 | 1093 | ||
1100 | itv.it_value.tv_sec = wait; | 1094 | itv.it_value.tv_sec = wait; |
1101 | itv.it_value.tv_usec = 0; | 1095 | itv.it_value.tv_usec = 0; |
1102 | itv.it_interval = itv.it_value; | 1096 | itv.it_interval = itv.it_value; |
1103 | setitimer(ITIMER_REAL, &itv, NULL); | 1097 | setitimer(ITIMER_REAL, &itv, NULL); |
1104 | } | 1098 | } |
1105 | 1099 | ||
1106 | void | 1100 | void |
@@ -1121,8 +1115,8 @@ foregroundproc() | |||
1121 | if (pgrp == -1) | 1115 | if (pgrp == -1) |
1122 | pgrp = getpgrp(); | 1116 | pgrp = getpgrp(); |
1123 | 1117 | ||
1124 | return((ioctl(STDOUT_FILENO, TIOCGPGRP, &ctty_pgrp) != -1 && | 1118 | return ((ioctl(STDOUT_FILENO, TIOCGPGRP, &ctty_pgrp) != -1 && |
1125 | ctty_pgrp == pgrp)); | 1119 | ctty_pgrp == pgrp)); |
1126 | } | 1120 | } |
1127 | 1121 | ||
1128 | void | 1122 | void |
@@ -1138,35 +1132,33 @@ progressmeter(int flag) | |||
1138 | char buf[256]; | 1132 | char buf[256]; |
1139 | 1133 | ||
1140 | if (flag == -1) { | 1134 | if (flag == -1) { |
1141 | (void)gettimeofday(&start, (struct timezone *)0); | 1135 | (void) gettimeofday(&start, (struct timezone *) 0); |
1142 | lastupdate = start; | 1136 | lastupdate = start; |
1143 | lastsize = 0; | 1137 | lastsize = 0; |
1144 | } | 1138 | } |
1145 | if (foregroundproc() == 0) | 1139 | if (foregroundproc() == 0) |
1146 | return; | 1140 | return; |
1147 | 1141 | ||
1148 | (void)gettimeofday(&now, (struct timezone *)0); | 1142 | (void) gettimeofday(&now, (struct timezone *) 0); |
1149 | cursize = statbytes; | 1143 | cursize = statbytes; |
1150 | if (totalbytes != 0) { | 1144 | if (totalbytes != 0) { |
1151 | ratio = cursize * 100.0 / totalbytes; | 1145 | ratio = cursize * 100.0 / totalbytes; |
1152 | ratio = MAX(ratio, 0); | 1146 | ratio = MAX(ratio, 0); |
1153 | ratio = MIN(ratio, 100); | 1147 | ratio = MIN(ratio, 100); |
1154 | } | 1148 | } else |
1155 | else | ||
1156 | ratio = 100; | 1149 | ratio = 100; |
1157 | 1150 | ||
1158 | snprintf(buf, sizeof(buf), "\r%-20.20s %3d%% ", curfile, ratio); | 1151 | snprintf(buf, sizeof(buf), "\r%-20.20s %3d%% ", curfile, ratio); |
1159 | 1152 | ||
1160 | barlength = getttywidth() - 51; | 1153 | barlength = getttywidth() - 51; |
1161 | if (barlength > 0) { | 1154 | if (barlength > 0) { |
1162 | i = barlength * ratio / 100; | 1155 | i = barlength * ratio / 100; |
1163 | snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), | 1156 | snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), |
1164 | "|%.*s%*s|", i, | 1157 | "|%.*s%*s|", i, |
1165 | "*****************************************************************************" | 1158 | "*****************************************************************************" |
1166 | "*****************************************************************************", | 1159 | "*****************************************************************************", |
1167 | barlength - i, ""); | 1160 | barlength - i, ""); |
1168 | } | 1161 | } |
1169 | |||
1170 | i = 0; | 1162 | i = 0; |
1171 | abbrevsize = cursize; | 1163 | abbrevsize = cursize; |
1172 | while (abbrevsize >= 100000 && i < sizeof(prefixes)) { | 1164 | while (abbrevsize >= 100000 && i < sizeof(prefixes)) { |
@@ -1174,8 +1166,8 @@ progressmeter(int flag) | |||
1174 | abbrevsize >>= 10; | 1166 | abbrevsize >>= 10; |
1175 | } | 1167 | } |
1176 | snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %5qd %c%c ", | 1168 | snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), " %5qd %c%c ", |
1177 | (quad_t)abbrevsize, prefixes[i], prefixes[i] == ' ' ? ' ' : | 1169 | (quad_t) abbrevsize, prefixes[i], prefixes[i] == ' ' ? ' ' : |
1178 | 'B'); | 1170 | 'B'); |
1179 | 1171 | ||
1180 | timersub(&now, &lastupdate, &wait); | 1172 | timersub(&now, &lastupdate, &wait); |
1181 | if (cursize > lastsize) { | 1173 | if (cursize > lastsize) { |
@@ -1187,33 +1179,32 @@ progressmeter(int flag) | |||
1187 | } | 1179 | } |
1188 | wait.tv_sec = 0; | 1180 | wait.tv_sec = 0; |
1189 | } | 1181 | } |
1190 | |||
1191 | timersub(&now, &start, &td); | 1182 | timersub(&now, &start, &td); |
1192 | elapsed = td.tv_sec + (td.tv_usec / 1000000.0); | 1183 | elapsed = td.tv_sec + (td.tv_usec / 1000000.0); |
1193 | 1184 | ||
1194 | if (statbytes <= 0 || elapsed <= 0.0 || cursize > totalbytes) { | 1185 | if (statbytes <= 0 || elapsed <= 0.0 || cursize > totalbytes) { |
1195 | snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), | 1186 | snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), |
1196 | " --:-- ETA"); | 1187 | " --:-- ETA"); |
1197 | } else if (wait.tv_sec >= STALLTIME) { | 1188 | } else if (wait.tv_sec >= STALLTIME) { |
1198 | snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), | 1189 | snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), |
1199 | " - stalled -"); | 1190 | " - stalled -"); |
1200 | } else { | 1191 | } else { |
1201 | remaining = (int)(totalbytes / (statbytes / elapsed) - elapsed); | 1192 | remaining = (int) (totalbytes / (statbytes / elapsed) - elapsed); |
1202 | i = remaining / 3600; | 1193 | i = remaining / 3600; |
1203 | if (i) | 1194 | if (i) |
1204 | snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), | 1195 | snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), |
1205 | "%2d:", i); | 1196 | "%2d:", i); |
1206 | else | 1197 | else |
1207 | snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), | 1198 | snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), |
1208 | " "); | 1199 | " "); |
1209 | i = remaining % 3600; | 1200 | i = remaining % 3600; |
1210 | snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), | 1201 | snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), |
1211 | "%02d:%02d ETA", i / 60, i % 60); | 1202 | "%02d:%02d ETA", i / 60, i % 60); |
1212 | } | 1203 | } |
1213 | atomicio(write, fileno(stdout), buf, strlen(buf)); | 1204 | atomicio(write, fileno(stdout), buf, strlen(buf)); |
1214 | 1205 | ||
1215 | if (flag == -1) { | 1206 | if (flag == -1) { |
1216 | signal(SIGALRM, (void *)updateprogressmeter); | 1207 | signal(SIGALRM, (void *) updateprogressmeter); |
1217 | alarmtimer(1); | 1208 | alarmtimer(1); |
1218 | } else if (flag == 1) { | 1209 | } else if (flag == 1) { |
1219 | alarmtimer(0); | 1210 | alarmtimer(0); |
@@ -1228,9 +1219,7 @@ getttywidth(void) | |||
1228 | struct winsize winsize; | 1219 | struct winsize winsize; |
1229 | 1220 | ||
1230 | if (ioctl(fileno(stdout), TIOCGWINSZ, &winsize) != -1) | 1221 | if (ioctl(fileno(stdout), TIOCGWINSZ, &winsize) != -1) |
1231 | return(winsize.ws_col ? winsize.ws_col : 80); | 1222 | return (winsize.ws_col ? winsize.ws_col : 80); |
1232 | else | 1223 | else |
1233 | return(80); | 1224 | return (80); |
1234 | } | 1225 | } |
1235 | |||
1236 | |||
diff --git a/servconf.c b/servconf.c index 086bc0364..aa7d5b781 100644 --- a/servconf.c +++ b/servconf.c | |||
@@ -1,18 +1,18 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * | |
3 | servconf.c | 3 | * servconf.c |
4 | 4 | * | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
6 | 6 | * | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
8 | All rights reserved | 8 | * All rights reserved |
9 | 9 | * | |
10 | Created: Mon Aug 21 15:48:58 1995 ylo | 10 | * Created: Mon Aug 21 15:48:58 1995 ylo |
11 | 11 | * | |
12 | */ | 12 | */ |
13 | 13 | ||
14 | #include "includes.h" | 14 | #include "includes.h" |
15 | RCSID("$Id: servconf.c,v 1.5 1999/11/21 02:23:53 damien Exp $"); | 15 | RCSID("$Id: servconf.c,v 1.6 1999/11/24 13:26:22 damien Exp $"); |
16 | 16 | ||
17 | #include "ssh.h" | 17 | #include "ssh.h" |
18 | #include "servconf.h" | 18 | #include "servconf.h" |
@@ -20,535 +20,514 @@ RCSID("$Id: servconf.c,v 1.5 1999/11/21 02:23:53 damien Exp $"); | |||
20 | 20 | ||
21 | /* Initializes the server options to their default values. */ | 21 | /* Initializes the server options to their default values. */ |
22 | 22 | ||
23 | void initialize_server_options(ServerOptions *options) | 23 | void |
24 | initialize_server_options(ServerOptions *options) | ||
24 | { | 25 | { |
25 | memset(options, 0, sizeof(*options)); | 26 | memset(options, 0, sizeof(*options)); |
26 | options->port = -1; | 27 | options->port = -1; |
27 | options->listen_addr.s_addr = htonl(INADDR_ANY); | 28 | options->listen_addr.s_addr = htonl(INADDR_ANY); |
28 | options->host_key_file = NULL; | 29 | options->host_key_file = NULL; |
29 | options->server_key_bits = -1; | 30 | options->server_key_bits = -1; |
30 | options->login_grace_time = -1; | 31 | options->login_grace_time = -1; |
31 | options->key_regeneration_time = -1; | 32 | options->key_regeneration_time = -1; |
32 | options->permit_root_login = -1; | 33 | options->permit_root_login = -1; |
33 | options->ignore_rhosts = -1; | 34 | options->ignore_rhosts = -1; |
34 | options->ignore_user_known_hosts = -1; | 35 | options->ignore_user_known_hosts = -1; |
35 | options->print_motd = -1; | 36 | options->print_motd = -1; |
36 | options->check_mail = -1; | 37 | options->check_mail = -1; |
37 | options->x11_forwarding = -1; | 38 | options->x11_forwarding = -1; |
38 | options->x11_display_offset = -1; | 39 | options->x11_display_offset = -1; |
39 | options->strict_modes = -1; | 40 | options->strict_modes = -1; |
40 | options->keepalives = -1; | 41 | options->keepalives = -1; |
41 | options->log_facility = (SyslogFacility)-1; | 42 | options->log_facility = (SyslogFacility) - 1; |
42 | options->log_level = (LogLevel)-1; | 43 | options->log_level = (LogLevel) - 1; |
43 | options->rhosts_authentication = -1; | 44 | options->rhosts_authentication = -1; |
44 | options->rhosts_rsa_authentication = -1; | 45 | options->rhosts_rsa_authentication = -1; |
45 | options->rsa_authentication = -1; | 46 | options->rsa_authentication = -1; |
46 | #ifdef KRB4 | 47 | #ifdef KRB4 |
47 | options->kerberos_authentication = -1; | 48 | options->kerberos_authentication = -1; |
48 | options->kerberos_or_local_passwd = -1; | 49 | options->kerberos_or_local_passwd = -1; |
49 | options->kerberos_ticket_cleanup = -1; | 50 | options->kerberos_ticket_cleanup = -1; |
50 | #endif | 51 | #endif |
51 | #ifdef AFS | 52 | #ifdef AFS |
52 | options->kerberos_tgt_passing = -1; | 53 | options->kerberos_tgt_passing = -1; |
53 | options->afs_token_passing = -1; | 54 | options->afs_token_passing = -1; |
54 | #endif | 55 | #endif |
55 | options->password_authentication = -1; | 56 | options->password_authentication = -1; |
56 | #ifdef SKEY | 57 | #ifdef SKEY |
57 | options->skey_authentication = -1; | 58 | options->skey_authentication = -1; |
58 | #endif | 59 | #endif |
59 | options->permit_empty_passwd = -1; | 60 | options->permit_empty_passwd = -1; |
60 | options->use_login = -1; | 61 | options->use_login = -1; |
61 | options->num_allow_users = 0; | 62 | options->num_allow_users = 0; |
62 | options->num_deny_users = 0; | 63 | options->num_deny_users = 0; |
63 | options->num_allow_groups = 0; | 64 | options->num_allow_groups = 0; |
64 | options->num_deny_groups = 0; | 65 | options->num_deny_groups = 0; |
65 | } | 66 | } |
66 | 67 | ||
67 | void fill_default_server_options(ServerOptions *options) | 68 | void |
69 | fill_default_server_options(ServerOptions *options) | ||
68 | { | 70 | { |
69 | if (options->port == -1) | 71 | if (options->port == -1) { |
70 | { | 72 | struct servent *sp; |
71 | struct servent *sp; | 73 | |
72 | 74 | sp = getservbyname(SSH_SERVICE_NAME, "tcp"); | |
73 | sp = getservbyname(SSH_SERVICE_NAME, "tcp"); | 75 | if (sp) |
74 | if (sp) | 76 | options->port = ntohs(sp->s_port); |
75 | options->port = ntohs(sp->s_port); | 77 | else |
76 | else | 78 | options->port = SSH_DEFAULT_PORT; |
77 | options->port = SSH_DEFAULT_PORT; | 79 | endservent(); |
78 | endservent(); | 80 | } |
79 | } | 81 | if (options->host_key_file == NULL) |
80 | if (options->host_key_file == NULL) | 82 | options->host_key_file = HOST_KEY_FILE; |
81 | options->host_key_file = HOST_KEY_FILE; | 83 | if (options->server_key_bits == -1) |
82 | if (options->server_key_bits == -1) | 84 | options->server_key_bits = 768; |
83 | options->server_key_bits = 768; | 85 | if (options->login_grace_time == -1) |
84 | if (options->login_grace_time == -1) | 86 | options->login_grace_time = 600; |
85 | options->login_grace_time = 600; | 87 | if (options->key_regeneration_time == -1) |
86 | if (options->key_regeneration_time == -1) | 88 | options->key_regeneration_time = 3600; |
87 | options->key_regeneration_time = 3600; | 89 | if (options->permit_root_login == -1) |
88 | if (options->permit_root_login == -1) | 90 | options->permit_root_login = 1; /* yes */ |
89 | options->permit_root_login = 1; /* yes */ | 91 | if (options->ignore_rhosts == -1) |
90 | if (options->ignore_rhosts == -1) | 92 | options->ignore_rhosts = 0; |
91 | options->ignore_rhosts = 0; | 93 | if (options->ignore_user_known_hosts == -1) |
92 | if (options->ignore_user_known_hosts == -1) | 94 | options->ignore_user_known_hosts = 0; |
93 | options->ignore_user_known_hosts = 0; | 95 | if (options->check_mail == -1) |
94 | if (options->check_mail == -1) | 96 | options->check_mail = 0; |
95 | options->check_mail = 0; | 97 | if (options->print_motd == -1) |
96 | if (options->print_motd == -1) | 98 | options->print_motd = 1; |
97 | options->print_motd = 1; | 99 | if (options->x11_forwarding == -1) |
98 | if (options->x11_forwarding == -1) | 100 | options->x11_forwarding = 1; |
99 | options->x11_forwarding = 1; | 101 | if (options->x11_display_offset == -1) |
100 | if (options->x11_display_offset == -1) | 102 | options->x11_display_offset = 1; |
101 | options->x11_display_offset = 1; | 103 | if (options->strict_modes == -1) |
102 | if (options->strict_modes == -1) | 104 | options->strict_modes = 1; |
103 | options->strict_modes = 1; | 105 | if (options->keepalives == -1) |
104 | if (options->keepalives == -1) | 106 | options->keepalives = 1; |
105 | options->keepalives = 1; | 107 | if (options->log_facility == (SyslogFacility) (-1)) |
106 | if (options->log_facility == (SyslogFacility)(-1)) | 108 | options->log_facility = SYSLOG_FACILITY_AUTH; |
107 | options->log_facility = SYSLOG_FACILITY_AUTH; | 109 | if (options->log_level == (LogLevel) (-1)) |
108 | if (options->log_level == (LogLevel)(-1)) | 110 | options->log_level = SYSLOG_LEVEL_INFO; |
109 | options->log_level = SYSLOG_LEVEL_INFO; | 111 | if (options->rhosts_authentication == -1) |
110 | if (options->rhosts_authentication == -1) | 112 | options->rhosts_authentication = 0; |
111 | options->rhosts_authentication = 0; | 113 | if (options->rhosts_rsa_authentication == -1) |
112 | if (options->rhosts_rsa_authentication == -1) | 114 | options->rhosts_rsa_authentication = 1; |
113 | options->rhosts_rsa_authentication = 1; | 115 | if (options->rsa_authentication == -1) |
114 | if (options->rsa_authentication == -1) | 116 | options->rsa_authentication = 1; |
115 | options->rsa_authentication = 1; | ||
116 | #ifdef KRB4 | 117 | #ifdef KRB4 |
117 | if (options->kerberos_authentication == -1) | 118 | if (options->kerberos_authentication == -1) |
118 | options->kerberos_authentication = (access(KEYFILE, R_OK) == 0); | 119 | options->kerberos_authentication = (access(KEYFILE, R_OK) == 0); |
119 | if (options->kerberos_or_local_passwd == -1) | 120 | if (options->kerberos_or_local_passwd == -1) |
120 | options->kerberos_or_local_passwd = 1; | 121 | options->kerberos_or_local_passwd = 1; |
121 | if (options->kerberos_ticket_cleanup == -1) | 122 | if (options->kerberos_ticket_cleanup == -1) |
122 | options->kerberos_ticket_cleanup = 1; | 123 | options->kerberos_ticket_cleanup = 1; |
123 | #endif /* KRB4 */ | 124 | #endif /* KRB4 */ |
124 | #ifdef AFS | 125 | #ifdef AFS |
125 | if (options->kerberos_tgt_passing == -1) | 126 | if (options->kerberos_tgt_passing == -1) |
126 | options->kerberos_tgt_passing = 0; | 127 | options->kerberos_tgt_passing = 0; |
127 | if (options->afs_token_passing == -1) | 128 | if (options->afs_token_passing == -1) |
128 | options->afs_token_passing = k_hasafs(); | 129 | options->afs_token_passing = k_hasafs(); |
129 | #endif /* AFS */ | 130 | #endif /* AFS */ |
130 | if (options->password_authentication == -1) | 131 | if (options->password_authentication == -1) |
131 | options->password_authentication = 1; | 132 | options->password_authentication = 1; |
132 | #ifdef SKEY | 133 | #ifdef SKEY |
133 | if (options->skey_authentication == -1) | 134 | if (options->skey_authentication == -1) |
134 | options->skey_authentication = 1; | 135 | options->skey_authentication = 1; |
135 | #endif | 136 | #endif |
136 | if (options->permit_empty_passwd == -1) | 137 | if (options->permit_empty_passwd == -1) |
137 | options->permit_empty_passwd = 1; | 138 | options->permit_empty_passwd = 1; |
138 | if (options->use_login == -1) | 139 | if (options->use_login == -1) |
139 | options->use_login = 0; | 140 | options->use_login = 0; |
140 | } | 141 | } |
141 | 142 | ||
142 | #define WHITESPACE " \t\r\n" | 143 | #define WHITESPACE " \t\r\n" |
143 | 144 | ||
144 | /* Keyword tokens. */ | 145 | /* Keyword tokens. */ |
145 | typedef enum | 146 | typedef enum { |
146 | { | 147 | sBadOption, /* == unknown option */ |
147 | sBadOption, /* == unknown option */ | 148 | sPort, sHostKeyFile, sServerKeyBits, sLoginGraceTime, sKeyRegenerationTime, |
148 | sPort, sHostKeyFile, sServerKeyBits, sLoginGraceTime, sKeyRegenerationTime, | 149 | sPermitRootLogin, sLogFacility, sLogLevel, |
149 | sPermitRootLogin, sLogFacility, sLogLevel, | 150 | sRhostsAuthentication, sRhostsRSAAuthentication, sRSAAuthentication, |
150 | sRhostsAuthentication, sRhostsRSAAuthentication, sRSAAuthentication, | ||
151 | #ifdef KRB4 | 151 | #ifdef KRB4 |
152 | sKerberosAuthentication, sKerberosOrLocalPasswd, sKerberosTicketCleanup, | 152 | sKerberosAuthentication, sKerberosOrLocalPasswd, sKerberosTicketCleanup, |
153 | #endif | 153 | #endif |
154 | #ifdef AFS | 154 | #ifdef AFS |
155 | sKerberosTgtPassing, sAFSTokenPassing, | 155 | sKerberosTgtPassing, sAFSTokenPassing, |
156 | #endif | 156 | #endif |
157 | #ifdef SKEY | 157 | #ifdef SKEY |
158 | sSkeyAuthentication, | 158 | sSkeyAuthentication, |
159 | #endif | 159 | #endif |
160 | sPasswordAuthentication, sListenAddress, | 160 | sPasswordAuthentication, sListenAddress, |
161 | sPrintMotd, sIgnoreRhosts, sX11Forwarding, sX11DisplayOffset, | 161 | sPrintMotd, sIgnoreRhosts, sX11Forwarding, sX11DisplayOffset, |
162 | sStrictModes, sEmptyPasswd, sRandomSeedFile, sKeepAlives, sCheckMail, | 162 | sStrictModes, sEmptyPasswd, sRandomSeedFile, sKeepAlives, sCheckMail, |
163 | sUseLogin, sAllowUsers, sDenyUsers, sAllowGroups, sDenyGroups, | 163 | sUseLogin, sAllowUsers, sDenyUsers, sAllowGroups, sDenyGroups, |
164 | sIgnoreUserKnownHosts | 164 | sIgnoreUserKnownHosts |
165 | } ServerOpCodes; | 165 | } ServerOpCodes; |
166 | 166 | ||
167 | /* Textual representation of the tokens. */ | 167 | /* Textual representation of the tokens. */ |
168 | static struct | 168 | static struct { |
169 | { | 169 | const char *name; |
170 | const char *name; | 170 | ServerOpCodes opcode; |
171 | ServerOpCodes opcode; | 171 | } keywords[] = { |
172 | } keywords[] = | 172 | { "port", sPort }, |
173 | { | 173 | { "hostkey", sHostKeyFile }, |
174 | { "port", sPort }, | 174 | { "serverkeybits", sServerKeyBits }, |
175 | { "hostkey", sHostKeyFile }, | 175 | { "logingracetime", sLoginGraceTime }, |
176 | { "serverkeybits", sServerKeyBits }, | 176 | { "keyregenerationinterval", sKeyRegenerationTime }, |
177 | { "logingracetime", sLoginGraceTime }, | 177 | { "permitrootlogin", sPermitRootLogin }, |
178 | { "keyregenerationinterval", sKeyRegenerationTime }, | 178 | { "syslogfacility", sLogFacility }, |
179 | { "permitrootlogin", sPermitRootLogin }, | 179 | { "loglevel", sLogLevel }, |
180 | { "syslogfacility", sLogFacility }, | 180 | { "rhostsauthentication", sRhostsAuthentication }, |
181 | { "loglevel", sLogLevel }, | 181 | { "rhostsrsaauthentication", sRhostsRSAAuthentication }, |
182 | { "rhostsauthentication", sRhostsAuthentication }, | 182 | { "rsaauthentication", sRSAAuthentication }, |
183 | { "rhostsrsaauthentication", sRhostsRSAAuthentication }, | ||
184 | { "rsaauthentication", sRSAAuthentication }, | ||
185 | #ifdef KRB4 | 183 | #ifdef KRB4 |
186 | { "kerberosauthentication", sKerberosAuthentication }, | 184 | { "kerberosauthentication", sKerberosAuthentication }, |
187 | { "kerberosorlocalpasswd", sKerberosOrLocalPasswd }, | 185 | { "kerberosorlocalpasswd", sKerberosOrLocalPasswd }, |
188 | { "kerberosticketcleanup", sKerberosTicketCleanup }, | 186 | { "kerberosticketcleanup", sKerberosTicketCleanup }, |
189 | #endif | 187 | #endif |
190 | #ifdef AFS | 188 | #ifdef AFS |
191 | { "kerberostgtpassing", sKerberosTgtPassing }, | 189 | { "kerberostgtpassing", sKerberosTgtPassing }, |
192 | { "afstokenpassing", sAFSTokenPassing }, | 190 | { "afstokenpassing", sAFSTokenPassing }, |
193 | #endif | 191 | #endif |
194 | { "passwordauthentication", sPasswordAuthentication }, | 192 | { "passwordauthentication", sPasswordAuthentication }, |
195 | #ifdef SKEY | 193 | #ifdef SKEY |
196 | { "skeyauthentication", sSkeyAuthentication }, | 194 | { "skeyauthentication", sSkeyAuthentication }, |
197 | #endif | 195 | #endif |
198 | { "checkmail", sCheckMail }, | 196 | { "checkmail", sCheckMail }, |
199 | { "listenaddress", sListenAddress }, | 197 | { "listenaddress", sListenAddress }, |
200 | { "printmotd", sPrintMotd }, | 198 | { "printmotd", sPrintMotd }, |
201 | { "ignorerhosts", sIgnoreRhosts }, | 199 | { "ignorerhosts", sIgnoreRhosts }, |
202 | { "ignoreuserknownhosts", sIgnoreUserKnownHosts }, | 200 | { "ignoreuserknownhosts", sIgnoreUserKnownHosts }, |
203 | { "x11forwarding", sX11Forwarding }, | 201 | { "x11forwarding", sX11Forwarding }, |
204 | { "x11displayoffset", sX11DisplayOffset }, | 202 | { "x11displayoffset", sX11DisplayOffset }, |
205 | { "strictmodes", sStrictModes }, | 203 | { "strictmodes", sStrictModes }, |
206 | { "permitemptypasswords", sEmptyPasswd }, | 204 | { "permitemptypasswords", sEmptyPasswd }, |
207 | { "uselogin", sUseLogin }, | 205 | { "uselogin", sUseLogin }, |
208 | { "randomseed", sRandomSeedFile }, | 206 | { "randomseed", sRandomSeedFile }, |
209 | { "keepalive", sKeepAlives }, | 207 | { "keepalive", sKeepAlives }, |
210 | { "allowusers", sAllowUsers }, | 208 | { "allowusers", sAllowUsers }, |
211 | { "denyusers", sDenyUsers }, | 209 | { "denyusers", sDenyUsers }, |
212 | { "allowgroups", sAllowGroups }, | 210 | { "allowgroups", sAllowGroups }, |
213 | { "denygroups", sDenyGroups }, | 211 | { "denygroups", sDenyGroups }, |
214 | { NULL, 0 } | 212 | { NULL, 0 } |
215 | }; | 213 | }; |
216 | 214 | ||
217 | /* Returns the number of the token pointed to by cp of length len. | 215 | /* Returns the number of the token pointed to by cp of length len. |
218 | Never returns if the token is not known. */ | 216 | Never returns if the token is not known. */ |
219 | 217 | ||
220 | static ServerOpCodes parse_token(const char *cp, const char *filename, | 218 | static ServerOpCodes |
221 | int linenum) | 219 | parse_token(const char *cp, const char *filename, |
220 | int linenum) | ||
222 | { | 221 | { |
223 | unsigned int i; | 222 | unsigned int i; |
224 | 223 | ||
225 | for (i = 0; keywords[i].name; i++) | 224 | for (i = 0; keywords[i].name; i++) |
226 | if (strcmp(cp, keywords[i].name) == 0) | 225 | if (strcmp(cp, keywords[i].name) == 0) |
227 | return keywords[i].opcode; | 226 | return keywords[i].opcode; |
228 | 227 | ||
229 | fprintf(stderr, "%s: line %d: Bad configuration option: %s\n", | 228 | fprintf(stderr, "%s: line %d: Bad configuration option: %s\n", |
230 | filename, linenum, cp); | 229 | filename, linenum, cp); |
231 | return sBadOption; | 230 | return sBadOption; |
232 | } | 231 | } |
233 | 232 | ||
234 | /* Reads the server configuration file. */ | 233 | /* Reads the server configuration file. */ |
235 | 234 | ||
236 | void read_server_config(ServerOptions *options, const char *filename) | 235 | void |
236 | read_server_config(ServerOptions *options, const char *filename) | ||
237 | { | 237 | { |
238 | FILE *f; | 238 | FILE *f; |
239 | char line[1024]; | 239 | char line[1024]; |
240 | char *cp, **charptr; | 240 | char *cp, **charptr; |
241 | int linenum, *intptr, value; | 241 | int linenum, *intptr, value; |
242 | int bad_options = 0; | 242 | int bad_options = 0; |
243 | ServerOpCodes opcode; | 243 | ServerOpCodes opcode; |
244 | 244 | ||
245 | f = fopen(filename, "r"); | 245 | f = fopen(filename, "r"); |
246 | if (!f) | 246 | if (!f) { |
247 | { | 247 | perror(filename); |
248 | perror(filename); | ||
249 | exit(1); | ||
250 | } | ||
251 | |||
252 | linenum = 0; | ||
253 | while (fgets(line, sizeof(line), f)) | ||
254 | { | ||
255 | linenum++; | ||
256 | cp = line + strspn(line, WHITESPACE); | ||
257 | if (!*cp || *cp == '#') | ||
258 | continue; | ||
259 | cp = strtok(cp, WHITESPACE); | ||
260 | { | ||
261 | char *t = cp; | ||
262 | for (; *t != 0; t++) | ||
263 | if ('A' <= *t && *t <= 'Z') | ||
264 | *t = *t - 'A' + 'a'; /* tolower */ | ||
265 | |||
266 | } | ||
267 | opcode = parse_token(cp, filename, linenum); | ||
268 | switch (opcode) | ||
269 | { | ||
270 | case sBadOption: | ||
271 | bad_options++; | ||
272 | continue; | ||
273 | case sPort: | ||
274 | intptr = &options->port; | ||
275 | parse_int: | ||
276 | cp = strtok(NULL, WHITESPACE); | ||
277 | if (!cp) | ||
278 | { | ||
279 | fprintf(stderr, "%s line %d: missing integer value.\n", | ||
280 | filename, linenum); | ||
281 | exit(1); | ||
282 | } | ||
283 | value = atoi(cp); | ||
284 | if (*intptr == -1) | ||
285 | *intptr = value; | ||
286 | break; | ||
287 | |||
288 | case sServerKeyBits: | ||
289 | intptr = &options->server_key_bits; | ||
290 | goto parse_int; | ||
291 | |||
292 | case sLoginGraceTime: | ||
293 | intptr = &options->login_grace_time; | ||
294 | goto parse_int; | ||
295 | |||
296 | case sKeyRegenerationTime: | ||
297 | intptr = &options->key_regeneration_time; | ||
298 | goto parse_int; | ||
299 | |||
300 | case sListenAddress: | ||
301 | cp = strtok(NULL, WHITESPACE); | ||
302 | if (!cp) | ||
303 | { | ||
304 | fprintf(stderr, "%s line %d: missing inet addr.\n", | ||
305 | filename, linenum); | ||
306 | exit(1); | ||
307 | } | ||
308 | options->listen_addr.s_addr = inet_addr(cp); | ||
309 | break; | ||
310 | |||
311 | case sHostKeyFile: | ||
312 | charptr = &options->host_key_file; | ||
313 | cp = strtok(NULL, WHITESPACE); | ||
314 | if (!cp) | ||
315 | { | ||
316 | fprintf(stderr, "%s line %d: missing file name.\n", | ||
317 | filename, linenum); | ||
318 | exit(1); | ||
319 | } | ||
320 | if (*charptr == NULL) | ||
321 | *charptr = tilde_expand_filename(cp, getuid()); | ||
322 | break; | ||
323 | |||
324 | case sRandomSeedFile: | ||
325 | fprintf(stderr, "%s line %d: \"randomseed\" option is obsolete.\n", | ||
326 | filename, linenum); | ||
327 | cp = strtok(NULL, WHITESPACE); | ||
328 | break; | ||
329 | |||
330 | case sPermitRootLogin: | ||
331 | intptr = &options->permit_root_login; | ||
332 | cp = strtok(NULL, WHITESPACE); | ||
333 | if (!cp) | ||
334 | { | ||
335 | fprintf(stderr, "%s line %d: missing yes/without-password/no argument.\n", | ||
336 | filename, linenum); | ||
337 | exit(1); | ||
338 | } | ||
339 | if (strcmp(cp, "without-password") == 0) | ||
340 | value = 2; | ||
341 | else if (strcmp(cp, "yes") == 0) | ||
342 | value = 1; | ||
343 | else if (strcmp(cp, "no") == 0) | ||
344 | value = 0; | ||
345 | else | ||
346 | { | ||
347 | fprintf(stderr, "%s line %d: Bad yes/without-password/no argument: %s\n", | ||
348 | filename, linenum, cp); | ||
349 | exit(1); | ||
350 | } | ||
351 | if (*intptr == -1) | ||
352 | *intptr = value; | ||
353 | break; | ||
354 | |||
355 | case sIgnoreRhosts: | ||
356 | intptr = &options->ignore_rhosts; | ||
357 | parse_flag: | ||
358 | cp = strtok(NULL, WHITESPACE); | ||
359 | if (!cp) | ||
360 | { | ||
361 | fprintf(stderr, "%s line %d: missing yes/no argument.\n", | ||
362 | filename, linenum); | ||
363 | exit(1); | ||
364 | } | ||
365 | if (strcmp(cp, "yes") == 0) | ||
366 | value = 1; | ||
367 | else | ||
368 | if (strcmp(cp, "no") == 0) | ||
369 | value = 0; | ||
370 | else | ||
371 | { | ||
372 | fprintf(stderr, "%s line %d: Bad yes/no argument: %s\n", | ||
373 | filename, linenum, cp); | ||
374 | exit(1); | 248 | exit(1); |
375 | } | 249 | } |
376 | if (*intptr == -1) | 250 | linenum = 0; |
377 | *intptr = value; | 251 | while (fgets(line, sizeof(line), f)) { |
378 | break; | 252 | linenum++; |
379 | 253 | cp = line + strspn(line, WHITESPACE); | |
380 | case sIgnoreUserKnownHosts: | 254 | if (!*cp || *cp == '#') |
381 | intptr = &options->ignore_user_known_hosts; | 255 | continue; |
382 | goto parse_int; | 256 | cp = strtok(cp, WHITESPACE); |
383 | 257 | { | |
384 | case sRhostsAuthentication: | 258 | char *t = cp; |
385 | intptr = &options->rhosts_authentication; | 259 | for (; *t != 0; t++) |
386 | goto parse_flag; | 260 | if ('A' <= *t && *t <= 'Z') |
387 | 261 | *t = *t - 'A' + 'a'; /* tolower */ | |
388 | case sRhostsRSAAuthentication: | 262 | |
389 | intptr = &options->rhosts_rsa_authentication; | 263 | } |
390 | goto parse_flag; | 264 | opcode = parse_token(cp, filename, linenum); |
391 | 265 | switch (opcode) { | |
392 | case sRSAAuthentication: | 266 | case sBadOption: |
393 | intptr = &options->rsa_authentication; | 267 | bad_options++; |
394 | goto parse_flag; | 268 | continue; |
395 | 269 | case sPort: | |
270 | intptr = &options->port; | ||
271 | parse_int: | ||
272 | cp = strtok(NULL, WHITESPACE); | ||
273 | if (!cp) { | ||
274 | fprintf(stderr, "%s line %d: missing integer value.\n", | ||
275 | filename, linenum); | ||
276 | exit(1); | ||
277 | } | ||
278 | value = atoi(cp); | ||
279 | if (*intptr == -1) | ||
280 | *intptr = value; | ||
281 | break; | ||
282 | |||
283 | case sServerKeyBits: | ||
284 | intptr = &options->server_key_bits; | ||
285 | goto parse_int; | ||
286 | |||
287 | case sLoginGraceTime: | ||
288 | intptr = &options->login_grace_time; | ||
289 | goto parse_int; | ||
290 | |||
291 | case sKeyRegenerationTime: | ||
292 | intptr = &options->key_regeneration_time; | ||
293 | goto parse_int; | ||
294 | |||
295 | case sListenAddress: | ||
296 | cp = strtok(NULL, WHITESPACE); | ||
297 | if (!cp) { | ||
298 | fprintf(stderr, "%s line %d: missing inet addr.\n", | ||
299 | filename, linenum); | ||
300 | exit(1); | ||
301 | } | ||
302 | options->listen_addr.s_addr = inet_addr(cp); | ||
303 | break; | ||
304 | |||
305 | case sHostKeyFile: | ||
306 | charptr = &options->host_key_file; | ||
307 | cp = strtok(NULL, WHITESPACE); | ||
308 | if (!cp) { | ||
309 | fprintf(stderr, "%s line %d: missing file name.\n", | ||
310 | filename, linenum); | ||
311 | exit(1); | ||
312 | } | ||
313 | if (*charptr == NULL) | ||
314 | *charptr = tilde_expand_filename(cp, getuid()); | ||
315 | break; | ||
316 | |||
317 | case sRandomSeedFile: | ||
318 | fprintf(stderr, "%s line %d: \"randomseed\" option is obsolete.\n", | ||
319 | filename, linenum); | ||
320 | cp = strtok(NULL, WHITESPACE); | ||
321 | break; | ||
322 | |||
323 | case sPermitRootLogin: | ||
324 | intptr = &options->permit_root_login; | ||
325 | cp = strtok(NULL, WHITESPACE); | ||
326 | if (!cp) { | ||
327 | fprintf(stderr, "%s line %d: missing yes/without-password/no argument.\n", | ||
328 | filename, linenum); | ||
329 | exit(1); | ||
330 | } | ||
331 | if (strcmp(cp, "without-password") == 0) | ||
332 | value = 2; | ||
333 | else if (strcmp(cp, "yes") == 0) | ||
334 | value = 1; | ||
335 | else if (strcmp(cp, "no") == 0) | ||
336 | value = 0; | ||
337 | else { | ||
338 | fprintf(stderr, "%s line %d: Bad yes/without-password/no argument: %s\n", | ||
339 | filename, linenum, cp); | ||
340 | exit(1); | ||
341 | } | ||
342 | if (*intptr == -1) | ||
343 | *intptr = value; | ||
344 | break; | ||
345 | |||
346 | case sIgnoreRhosts: | ||
347 | intptr = &options->ignore_rhosts; | ||
348 | parse_flag: | ||
349 | cp = strtok(NULL, WHITESPACE); | ||
350 | if (!cp) { | ||
351 | fprintf(stderr, "%s line %d: missing yes/no argument.\n", | ||
352 | filename, linenum); | ||
353 | exit(1); | ||
354 | } | ||
355 | if (strcmp(cp, "yes") == 0) | ||
356 | value = 1; | ||
357 | else if (strcmp(cp, "no") == 0) | ||
358 | value = 0; | ||
359 | else { | ||
360 | fprintf(stderr, "%s line %d: Bad yes/no argument: %s\n", | ||
361 | filename, linenum, cp); | ||
362 | exit(1); | ||
363 | } | ||
364 | if (*intptr == -1) | ||
365 | *intptr = value; | ||
366 | break; | ||
367 | |||
368 | case sIgnoreUserKnownHosts: | ||
369 | intptr = &options->ignore_user_known_hosts; | ||
370 | goto parse_int; | ||
371 | |||
372 | case sRhostsAuthentication: | ||
373 | intptr = &options->rhosts_authentication; | ||
374 | goto parse_flag; | ||
375 | |||
376 | case sRhostsRSAAuthentication: | ||
377 | intptr = &options->rhosts_rsa_authentication; | ||
378 | goto parse_flag; | ||
379 | |||
380 | case sRSAAuthentication: | ||
381 | intptr = &options->rsa_authentication; | ||
382 | goto parse_flag; | ||
383 | |||
396 | #ifdef KRB4 | 384 | #ifdef KRB4 |
397 | case sKerberosAuthentication: | 385 | case sKerberosAuthentication: |
398 | intptr = &options->kerberos_authentication; | 386 | intptr = &options->kerberos_authentication; |
399 | goto parse_flag; | 387 | goto parse_flag; |
400 | 388 | ||
401 | case sKerberosOrLocalPasswd: | 389 | case sKerberosOrLocalPasswd: |
402 | intptr = &options->kerberos_or_local_passwd; | 390 | intptr = &options->kerberos_or_local_passwd; |
403 | goto parse_flag; | 391 | goto parse_flag; |
404 | 392 | ||
405 | case sKerberosTicketCleanup: | 393 | case sKerberosTicketCleanup: |
406 | intptr = &options->kerberos_ticket_cleanup; | 394 | intptr = &options->kerberos_ticket_cleanup; |
407 | goto parse_flag; | 395 | goto parse_flag; |
408 | #endif | 396 | #endif |
409 | 397 | ||
410 | #ifdef AFS | 398 | #ifdef AFS |
411 | case sKerberosTgtPassing: | 399 | case sKerberosTgtPassing: |
412 | intptr = &options->kerberos_tgt_passing; | 400 | intptr = &options->kerberos_tgt_passing; |
413 | goto parse_flag; | 401 | goto parse_flag; |
414 | 402 | ||
415 | case sAFSTokenPassing: | 403 | case sAFSTokenPassing: |
416 | intptr = &options->afs_token_passing; | 404 | intptr = &options->afs_token_passing; |
417 | goto parse_flag; | 405 | goto parse_flag; |
418 | #endif | 406 | #endif |
419 | 407 | ||
420 | case sPasswordAuthentication: | 408 | case sPasswordAuthentication: |
421 | intptr = &options->password_authentication; | 409 | intptr = &options->password_authentication; |
422 | goto parse_flag; | 410 | goto parse_flag; |
423 | 411 | ||
424 | case sCheckMail: | 412 | case sCheckMail: |
425 | intptr = &options->check_mail; | 413 | intptr = &options->check_mail; |
426 | goto parse_flag; | 414 | goto parse_flag; |
427 | 415 | ||
428 | #ifdef SKEY | 416 | #ifdef SKEY |
429 | case sSkeyAuthentication: | 417 | case sSkeyAuthentication: |
430 | intptr = &options->skey_authentication; | 418 | intptr = &options->skey_authentication; |
431 | goto parse_flag; | 419 | goto parse_flag; |
432 | #endif | 420 | #endif |
433 | 421 | ||
434 | case sPrintMotd: | 422 | case sPrintMotd: |
435 | intptr = &options->print_motd; | 423 | intptr = &options->print_motd; |
436 | goto parse_flag; | 424 | goto parse_flag; |
437 | 425 | ||
438 | case sX11Forwarding: | 426 | case sX11Forwarding: |
439 | intptr = &options->x11_forwarding; | 427 | intptr = &options->x11_forwarding; |
440 | goto parse_flag; | 428 | goto parse_flag; |
441 | 429 | ||
442 | case sX11DisplayOffset: | 430 | case sX11DisplayOffset: |
443 | intptr = &options->x11_display_offset; | 431 | intptr = &options->x11_display_offset; |
444 | goto parse_int; | 432 | goto parse_int; |
445 | 433 | ||
446 | case sStrictModes: | 434 | case sStrictModes: |
447 | intptr = &options->strict_modes; | 435 | intptr = &options->strict_modes; |
448 | goto parse_flag; | 436 | goto parse_flag; |
449 | 437 | ||
450 | case sKeepAlives: | 438 | case sKeepAlives: |
451 | intptr = &options->keepalives; | 439 | intptr = &options->keepalives; |
452 | goto parse_flag; | 440 | goto parse_flag; |
453 | 441 | ||
454 | case sEmptyPasswd: | 442 | case sEmptyPasswd: |
455 | intptr = &options->permit_empty_passwd; | 443 | intptr = &options->permit_empty_passwd; |
456 | goto parse_flag; | 444 | goto parse_flag; |
457 | 445 | ||
458 | case sUseLogin: | 446 | case sUseLogin: |
459 | intptr = &options->use_login; | 447 | intptr = &options->use_login; |
460 | goto parse_flag; | 448 | goto parse_flag; |
461 | 449 | ||
462 | case sLogFacility: | 450 | case sLogFacility: |
463 | intptr = (int *)&options->log_facility; | 451 | intptr = (int *) &options->log_facility; |
464 | cp = strtok(NULL, WHITESPACE); | 452 | cp = strtok(NULL, WHITESPACE); |
465 | value = log_facility_number(cp); | 453 | value = log_facility_number(cp); |
466 | if (value == (SyslogFacility)-1) | 454 | if (value == (SyslogFacility) - 1) |
467 | fatal("%.200s line %d: unsupported log facility '%s'\n", | 455 | fatal("%.200s line %d: unsupported log facility '%s'\n", |
468 | filename, linenum, cp ? cp : "<NONE>"); | 456 | filename, linenum, cp ? cp : "<NONE>"); |
469 | if (*intptr == -1) | 457 | if (*intptr == -1) |
470 | *intptr = (SyslogFacility)value; | 458 | *intptr = (SyslogFacility) value; |
471 | break; | 459 | break; |
472 | 460 | ||
473 | case sLogLevel: | 461 | case sLogLevel: |
474 | intptr = (int *)&options->log_level; | 462 | intptr = (int *) &options->log_level; |
475 | cp = strtok(NULL, WHITESPACE); | 463 | cp = strtok(NULL, WHITESPACE); |
476 | value = log_level_number(cp); | 464 | value = log_level_number(cp); |
477 | if (value == (LogLevel)-1) | 465 | if (value == (LogLevel) - 1) |
478 | fatal("%.200s line %d: unsupported log level '%s'\n", | 466 | fatal("%.200s line %d: unsupported log level '%s'\n", |
479 | filename, linenum, cp ? cp : "<NONE>"); | 467 | filename, linenum, cp ? cp : "<NONE>"); |
480 | if (*intptr == -1) | 468 | if (*intptr == -1) |
481 | *intptr = (LogLevel)value; | 469 | *intptr = (LogLevel) value; |
482 | break; | 470 | break; |
483 | 471 | ||
484 | case sAllowUsers: | 472 | case sAllowUsers: |
485 | while ((cp = strtok(NULL, WHITESPACE))) | 473 | while ((cp = strtok(NULL, WHITESPACE))) { |
486 | { | 474 | if (options->num_allow_users >= MAX_ALLOW_USERS) { |
487 | if (options->num_allow_users >= MAX_ALLOW_USERS) | 475 | fprintf(stderr, "%s line %d: too many allow users.\n", |
488 | { | 476 | filename, linenum); |
489 | fprintf(stderr, "%s line %d: too many allow users.\n", | 477 | exit(1); |
490 | filename, linenum); | 478 | } |
491 | exit(1); | 479 | options->allow_users[options->num_allow_users++] = xstrdup(cp); |
480 | } | ||
481 | break; | ||
482 | |||
483 | case sDenyUsers: | ||
484 | while ((cp = strtok(NULL, WHITESPACE))) { | ||
485 | if (options->num_deny_users >= MAX_DENY_USERS) { | ||
486 | fprintf(stderr, "%s line %d: too many deny users.\n", | ||
487 | filename, linenum); | ||
488 | exit(1); | ||
489 | } | ||
490 | options->deny_users[options->num_deny_users++] = xstrdup(cp); | ||
491 | } | ||
492 | break; | ||
493 | |||
494 | case sAllowGroups: | ||
495 | while ((cp = strtok(NULL, WHITESPACE))) { | ||
496 | if (options->num_allow_groups >= MAX_ALLOW_GROUPS) { | ||
497 | fprintf(stderr, "%s line %d: too many allow groups.\n", | ||
498 | filename, linenum); | ||
499 | exit(1); | ||
500 | } | ||
501 | options->allow_groups[options->num_allow_groups++] = xstrdup(cp); | ||
502 | } | ||
503 | break; | ||
504 | |||
505 | case sDenyGroups: | ||
506 | while ((cp = strtok(NULL, WHITESPACE))) { | ||
507 | if (options->num_deny_groups >= MAX_DENY_GROUPS) { | ||
508 | fprintf(stderr, "%s line %d: too many deny groups.\n", | ||
509 | filename, linenum); | ||
510 | exit(1); | ||
511 | } | ||
512 | options->deny_groups[options->num_deny_groups++] = xstrdup(cp); | ||
513 | } | ||
514 | break; | ||
515 | |||
516 | default: | ||
517 | fprintf(stderr, "%s line %d: Missing handler for opcode %s (%d)\n", | ||
518 | filename, linenum, cp, opcode); | ||
519 | exit(1); | ||
492 | } | 520 | } |
493 | options->allow_users[options->num_allow_users++] = xstrdup(cp); | 521 | if (strtok(NULL, WHITESPACE) != NULL) { |
494 | } | 522 | fprintf(stderr, "%s line %d: garbage at end of line.\n", |
495 | break; | 523 | filename, linenum); |
496 | 524 | exit(1); | |
497 | case sDenyUsers: | ||
498 | while ((cp = strtok(NULL, WHITESPACE))) | ||
499 | { | ||
500 | if (options->num_deny_users >= MAX_DENY_USERS) | ||
501 | { | ||
502 | fprintf(stderr, "%s line %d: too many deny users.\n", | ||
503 | filename, linenum); | ||
504 | exit(1); | ||
505 | } | ||
506 | options->deny_users[options->num_deny_users++] = xstrdup(cp); | ||
507 | } | ||
508 | break; | ||
509 | |||
510 | case sAllowGroups: | ||
511 | while ((cp = strtok(NULL, WHITESPACE))) | ||
512 | { | ||
513 | if (options->num_allow_groups >= MAX_ALLOW_GROUPS) | ||
514 | { | ||
515 | fprintf(stderr, "%s line %d: too many allow groups.\n", | ||
516 | filename, linenum); | ||
517 | exit(1); | ||
518 | } | 525 | } |
519 | options->allow_groups[options->num_allow_groups++] = xstrdup(cp); | ||
520 | } | ||
521 | break; | ||
522 | |||
523 | case sDenyGroups: | ||
524 | while ((cp = strtok(NULL, WHITESPACE))) | ||
525 | { | ||
526 | if (options->num_deny_groups >= MAX_DENY_GROUPS) | ||
527 | { | ||
528 | fprintf(stderr, "%s line %d: too many deny groups.\n", | ||
529 | filename, linenum); | ||
530 | exit(1); | ||
531 | } | ||
532 | options->deny_groups[options->num_deny_groups++] = xstrdup(cp); | ||
533 | } | ||
534 | break; | ||
535 | |||
536 | default: | ||
537 | fprintf(stderr, "%s line %d: Missing handler for opcode %s (%d)\n", | ||
538 | filename, linenum, cp, opcode); | ||
539 | exit(1); | ||
540 | } | 526 | } |
541 | if (strtok(NULL, WHITESPACE) != NULL) | 527 | fclose(f); |
542 | { | 528 | if (bad_options > 0) { |
543 | fprintf(stderr, "%s line %d: garbage at end of line.\n", | 529 | fprintf(stderr, "%s: terminating, %d bad configuration options\n", |
544 | filename, linenum); | 530 | filename, bad_options); |
545 | exit(1); | 531 | exit(1); |
546 | } | 532 | } |
547 | } | ||
548 | fclose(f); | ||
549 | if (bad_options > 0) { | ||
550 | fprintf(stderr, "%s: terminating, %d bad configuration options\n", | ||
551 | filename, bad_options); | ||
552 | exit(1); | ||
553 | } | ||
554 | } | 533 | } |
diff --git a/servconf.h b/servconf.h index e16f3d044..4f3238e0c 100644 --- a/servconf.h +++ b/servconf.h | |||
@@ -1,86 +1,98 @@ | |||
1 | /* | 1 | /* |
2 | * | ||
3 | * servconf.h | ||
4 | * | ||
5 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | * | ||
7 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | * All rights reserved | ||
9 | * | ||
10 | * Created: Mon Aug 21 15:35:03 1995 ylo | ||
11 | * | ||
12 | * Definitions for server configuration data and for the functions reading it. | ||
13 | * | ||
14 | */ | ||
2 | 15 | ||
3 | servconf.h | 16 | /* RCSID("$Id: servconf.h,v 1.4 1999/11/24 13:26:22 damien Exp $"); */ |
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | |||
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Mon Aug 21 15:35:03 1995 ylo | ||
11 | |||
12 | Definitions for server configuration data and for the functions reading it. | ||
13 | |||
14 | */ | ||
15 | |||
16 | /* RCSID("$Id: servconf.h,v 1.3 1999/11/12 00:33:04 damien Exp $"); */ | ||
17 | 17 | ||
18 | #ifndef SERVCONF_H | 18 | #ifndef SERVCONF_H |
19 | #define SERVCONF_H | 19 | #define SERVCONF_H |
20 | 20 | ||
21 | #define MAX_ALLOW_USERS 256 /* Max # users on allow list. */ | 21 | #define MAX_ALLOW_USERS 256 /* Max # users on allow list. */ |
22 | #define MAX_DENY_USERS 256 /* Max # users on deny list. */ | 22 | #define MAX_DENY_USERS 256 /* Max # users on deny list. */ |
23 | #define MAX_ALLOW_GROUPS 256 /* Max # groups on allow list. */ | 23 | #define MAX_ALLOW_GROUPS 256 /* Max # groups on allow list. */ |
24 | #define MAX_DENY_GROUPS 256 /* Max # groups on deny list. */ | 24 | #define MAX_DENY_GROUPS 256 /* Max # groups on deny list. */ |
25 | 25 | ||
26 | typedef struct | 26 | typedef struct { |
27 | { | 27 | int port; /* Port number to listen on. */ |
28 | int port; /* Port number to listen on. */ | 28 | struct in_addr listen_addr; /* Address on which the server |
29 | struct in_addr listen_addr; /* Address on which the server listens. */ | 29 | * listens. */ |
30 | char *host_key_file; /* File containing host key. */ | 30 | char *host_key_file; /* File containing host key. */ |
31 | int server_key_bits; /* Size of the server key. */ | 31 | int server_key_bits;/* Size of the server key. */ |
32 | int login_grace_time; /* Disconnect if no auth in this time (sec). */ | 32 | int login_grace_time; /* Disconnect if no auth in this time |
33 | int key_regeneration_time; /* Server key lifetime (seconds). */ | 33 | * (sec). */ |
34 | int permit_root_login; /* If true, permit root login. */ | 34 | int key_regeneration_time; /* Server key lifetime (seconds). */ |
35 | int ignore_rhosts; /* Ignore .rhosts and .shosts. */ | 35 | int permit_root_login; /* If true, permit root login. */ |
36 | int ignore_user_known_hosts; /* Ignore ~/.ssh/known_hosts for RhostsRsaAuth */ | 36 | int ignore_rhosts; /* Ignore .rhosts and .shosts. */ |
37 | int print_motd; /* If true, print /etc/motd. */ | 37 | int ignore_user_known_hosts; /* Ignore ~/.ssh/known_hosts |
38 | int check_mail; /* If true, check for new mail. */ | 38 | * for RhostsRsaAuth */ |
39 | int x11_forwarding; /* If true, permit inet (spoofing) X11 fwd. */ | 39 | int print_motd; /* If true, print /etc/motd. */ |
40 | int x11_display_offset; /* What DISPLAY number to start searching at */ | 40 | int check_mail; /* If true, check for new mail. */ |
41 | int strict_modes; /* If true, require string home dir modes. */ | 41 | int x11_forwarding; /* If true, permit inet (spoofing) X11 fwd. */ |
42 | int keepalives; /* If true, set SO_KEEPALIVE. */ | 42 | int x11_display_offset; /* What DISPLAY number to start |
43 | SyslogFacility log_facility; /* Facility for system logging. */ | 43 | * searching at */ |
44 | LogLevel log_level; /* Level for system logging. */ | 44 | int strict_modes; /* If true, require string home dir modes. */ |
45 | int rhosts_authentication; /* If true, permit rhosts authentication. */ | 45 | int keepalives; /* If true, set SO_KEEPALIVE. */ |
46 | int rhosts_rsa_authentication;/* If true, permit rhosts RSA authentication.*/ | 46 | SyslogFacility log_facility; /* Facility for system logging. */ |
47 | int rsa_authentication; /* If true, permit RSA authentication. */ | 47 | LogLevel log_level; /* Level for system logging. */ |
48 | int rhosts_authentication; /* If true, permit rhosts | ||
49 | * authentication. */ | ||
50 | int rhosts_rsa_authentication; /* If true, permit rhosts RSA | ||
51 | * authentication. */ | ||
52 | int rsa_authentication; /* If true, permit RSA authentication. */ | ||
48 | #ifdef KRB4 | 53 | #ifdef KRB4 |
49 | int kerberos_authentication; /* If true, permit Kerberos authentication. */ | 54 | int kerberos_authentication; /* If true, permit Kerberos |
50 | int kerberos_or_local_passwd; /* If true, permit kerberos and any other | 55 | * authentication. */ |
51 | password authentication mechanism, such | 56 | int kerberos_or_local_passwd; /* If true, permit kerberos |
52 | as SecurID or /etc/passwd */ | 57 | * and any other password |
53 | int kerberos_ticket_cleanup; /* If true, destroy ticket file on logout. */ | 58 | * authentication mechanism, |
59 | * such as SecurID or | ||
60 | * /etc/passwd */ | ||
61 | int kerberos_ticket_cleanup; /* If true, destroy ticket | ||
62 | * file on logout. */ | ||
54 | #endif | 63 | #endif |
55 | #ifdef AFS | 64 | #ifdef AFS |
56 | int kerberos_tgt_passing; /* If true, permit Kerberos tgt passing. */ | 65 | int kerberos_tgt_passing; /* If true, permit Kerberos tgt |
57 | int afs_token_passing; /* If true, permit AFS token passing. */ | 66 | * passing. */ |
67 | int afs_token_passing; /* If true, permit AFS token passing. */ | ||
58 | #endif | 68 | #endif |
59 | int password_authentication; /* If true, permit password authentication. */ | 69 | int password_authentication; /* If true, permit password |
70 | * authentication. */ | ||
60 | #ifdef SKEY | 71 | #ifdef SKEY |
61 | int skey_authentication; /* If true, permit s/key authentication. */ | 72 | int skey_authentication; /* If true, permit s/key |
73 | * authentication. */ | ||
62 | #endif | 74 | #endif |
63 | int permit_empty_passwd; /* If false, do not permit empty passwords. */ | 75 | int permit_empty_passwd; /* If false, do not permit empty |
64 | int use_login; /* If true, login(1) is used */ | 76 | * passwords. */ |
65 | unsigned int num_allow_users; | 77 | int use_login; /* If true, login(1) is used */ |
66 | char *allow_users[MAX_ALLOW_USERS]; | 78 | unsigned int num_allow_users; |
67 | unsigned int num_deny_users; | 79 | char *allow_users[MAX_ALLOW_USERS]; |
68 | char *deny_users[MAX_DENY_USERS]; | 80 | unsigned int num_deny_users; |
69 | unsigned int num_allow_groups; | 81 | char *deny_users[MAX_DENY_USERS]; |
70 | char *allow_groups[MAX_ALLOW_GROUPS]; | 82 | unsigned int num_allow_groups; |
71 | unsigned int num_deny_groups; | 83 | char *allow_groups[MAX_ALLOW_GROUPS]; |
72 | char *deny_groups[MAX_DENY_GROUPS]; | 84 | unsigned int num_deny_groups; |
73 | } ServerOptions; | 85 | char *deny_groups[MAX_DENY_GROUPS]; |
74 | 86 | } ServerOptions; | |
75 | /* Initializes the server options to special values that indicate that they | 87 | /* Initializes the server options to special values that indicate that they |
76 | have not yet been set. */ | 88 | have not yet been set. */ |
77 | void initialize_server_options(ServerOptions *options); | 89 | void initialize_server_options(ServerOptions * options); |
78 | 90 | ||
79 | /* Reads the server configuration file. This only sets the values for those | 91 | /* Reads the server configuration file. This only sets the values for those |
80 | options that have the special value indicating they have not been set. */ | 92 | options that have the special value indicating they have not been set. */ |
81 | void read_server_config(ServerOptions *options, const char *filename); | 93 | void read_server_config(ServerOptions * options, const char *filename); |
82 | 94 | ||
83 | /* Sets values for those values that have not yet been set. */ | 95 | /* Sets values for those values that have not yet been set. */ |
84 | void fill_default_server_options(ServerOptions *options); | 96 | void fill_default_server_options(ServerOptions * options); |
85 | 97 | ||
86 | #endif /* SERVCONF_H */ | 98 | #endif /* SERVCONF_H */ |
diff --git a/serverloop.c b/serverloop.c index 9961170a5..fc959baef 100644 --- a/serverloop.c +++ b/serverloop.c | |||
@@ -1,17 +1,10 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | |
3 | serverloop.c | 3 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
4 | 4 | * All rights reserved | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Created: Sun Sep 10 00:30:37 1995 ylo |
6 | 6 | * Server main loop for handling the interactive session. | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | */ |
8 | All rights reserved | ||
9 | |||
10 | Created: Sun Sep 10 00:30:37 1995 ylo | ||
11 | |||
12 | Server main loop for handling the interactive session. | ||
13 | |||
14 | */ | ||
15 | 8 | ||
16 | #include "includes.h" | 9 | #include "includes.h" |
17 | #include "xmalloc.h" | 10 | #include "xmalloc.h" |
@@ -43,603 +36,594 @@ static int max_fd; /* Max file descriptor number for select(). */ | |||
43 | /* This SIGCHLD kludge is used to detect when the child exits. The server | 36 | /* This SIGCHLD kludge is used to detect when the child exits. The server |
44 | will exit after that, as soon as forwarded connections have terminated. */ | 37 | will exit after that, as soon as forwarded connections have terminated. */ |
45 | 38 | ||
46 | static int child_pid; /* Pid of the child. */ | 39 | static int child_pid; /* Pid of the child. */ |
47 | static volatile int child_terminated; /* The child has terminated. */ | 40 | static volatile int child_terminated; /* The child has terminated. */ |
48 | static volatile int child_wait_status; /* Status from wait(). */ | 41 | static volatile int child_wait_status; /* Status from wait(). */ |
49 | 42 | ||
50 | void sigchld_handler(int sig) | 43 | void |
44 | sigchld_handler(int sig) | ||
51 | { | 45 | { |
52 | int save_errno = errno; | 46 | int save_errno = errno; |
53 | int wait_pid; | 47 | int wait_pid; |
54 | debug("Received SIGCHLD."); | 48 | debug("Received SIGCHLD."); |
55 | wait_pid = wait((int *)&child_wait_status); | 49 | wait_pid = wait((int *) &child_wait_status); |
56 | if (wait_pid != -1) | 50 | if (wait_pid != -1) { |
57 | { | 51 | if (wait_pid != child_pid) |
58 | if (wait_pid != child_pid) | 52 | error("Strange, got SIGCHLD and wait returned pid %d but child is %d", |
59 | error("Strange, got SIGCHLD and wait returned pid %d but child is %d", | 53 | wait_pid, child_pid); |
60 | wait_pid, child_pid); | 54 | if (WIFEXITED(child_wait_status) || |
61 | if (WIFEXITED(child_wait_status) || | 55 | WIFSIGNALED(child_wait_status)) |
62 | WIFSIGNALED(child_wait_status)) | 56 | child_terminated = 1; |
63 | child_terminated = 1; | 57 | } |
64 | } | 58 | signal(SIGCHLD, sigchld_handler); |
65 | signal(SIGCHLD, sigchld_handler); | 59 | errno = save_errno; |
66 | errno = save_errno; | ||
67 | } | 60 | } |
68 | 61 | ||
69 | /* Process any buffered packets that have been received from the client. */ | 62 | /* |
70 | 63 | * Process any buffered packets that have been received from the client. | |
71 | void process_buffered_input_packets() | 64 | */ |
65 | void | ||
66 | process_buffered_input_packets() | ||
72 | { | 67 | { |
73 | int type; | 68 | int type; |
74 | char *data; | 69 | char *data; |
75 | unsigned int data_len; | 70 | unsigned int data_len; |
76 | int row, col, xpixel, ypixel; | 71 | int row, col, xpixel, ypixel; |
77 | int payload_len; | 72 | int payload_len; |
78 | 73 | ||
79 | /* Process buffered packets from the client. */ | 74 | /* Process buffered packets from the client. */ |
80 | while ((type = packet_read_poll(&payload_len)) != SSH_MSG_NONE) | 75 | while ((type = packet_read_poll(&payload_len)) != SSH_MSG_NONE) { |
81 | { | 76 | switch (type) { |
82 | switch (type) | 77 | case SSH_CMSG_STDIN_DATA: |
83 | { | 78 | /* Stdin data from the client. Append it to the buffer. */ |
84 | case SSH_CMSG_STDIN_DATA: | 79 | /* Ignore any data if the client has closed stdin. */ |
85 | /* Stdin data from the client. Append it to the buffer. */ | 80 | if (fdin == -1) |
86 | if (fdin == -1) | 81 | break; |
87 | break; /* Ignore any data if the client has closed stdin. */ | 82 | data = packet_get_string(&data_len); |
88 | data = packet_get_string(&data_len); | 83 | packet_integrity_check(payload_len, (4 + data_len), type); |
89 | packet_integrity_check(payload_len, (4 + data_len), type); | 84 | buffer_append(&stdin_buffer, data, data_len); |
90 | buffer_append(&stdin_buffer, data, data_len); | 85 | memset(data, 0, data_len); |
91 | memset(data, 0, data_len); | 86 | xfree(data); |
92 | xfree(data); | 87 | break; |
93 | break; | 88 | |
94 | 89 | case SSH_CMSG_EOF: | |
95 | case SSH_CMSG_EOF: | 90 | /* Eof from the client. The stdin descriptor to |
96 | /* Eof from the client. The stdin descriptor to the program | 91 | the program will be closed when all buffered |
97 | will be closed when all buffered data has drained. */ | 92 | data has drained. */ |
98 | debug("EOF received for stdin."); | 93 | debug("EOF received for stdin."); |
99 | packet_integrity_check(payload_len, 0, type); | 94 | packet_integrity_check(payload_len, 0, type); |
100 | stdin_eof = 1; | 95 | stdin_eof = 1; |
101 | break; | 96 | break; |
102 | 97 | ||
103 | case SSH_CMSG_WINDOW_SIZE: | 98 | case SSH_CMSG_WINDOW_SIZE: |
104 | debug("Window change received."); | 99 | debug("Window change received."); |
105 | packet_integrity_check(payload_len, 4*4, type); | 100 | packet_integrity_check(payload_len, 4 * 4, type); |
106 | row = packet_get_int(); | 101 | row = packet_get_int(); |
107 | col = packet_get_int(); | 102 | col = packet_get_int(); |
108 | xpixel = packet_get_int(); | 103 | xpixel = packet_get_int(); |
109 | ypixel = packet_get_int(); | 104 | ypixel = packet_get_int(); |
110 | if (fdin != -1) | 105 | if (fdin != -1) |
111 | pty_change_window_size(fdin, row, col, xpixel, ypixel); | 106 | pty_change_window_size(fdin, row, col, xpixel, ypixel); |
112 | break; | 107 | break; |
113 | 108 | ||
114 | case SSH_MSG_PORT_OPEN: | 109 | case SSH_MSG_PORT_OPEN: |
115 | debug("Received port open request."); | 110 | debug("Received port open request."); |
116 | channel_input_port_open(payload_len); | 111 | channel_input_port_open(payload_len); |
117 | break; | 112 | break; |
118 | 113 | ||
119 | case SSH_MSG_CHANNEL_OPEN_CONFIRMATION: | 114 | case SSH_MSG_CHANNEL_OPEN_CONFIRMATION: |
120 | debug("Received channel open confirmation."); | 115 | debug("Received channel open confirmation."); |
121 | packet_integrity_check(payload_len, 4 + 4, type); | 116 | packet_integrity_check(payload_len, 4 + 4, type); |
122 | channel_input_open_confirmation(); | 117 | channel_input_open_confirmation(); |
123 | break; | 118 | break; |
124 | 119 | ||
125 | case SSH_MSG_CHANNEL_OPEN_FAILURE: | 120 | case SSH_MSG_CHANNEL_OPEN_FAILURE: |
126 | debug("Received channel open failure."); | 121 | debug("Received channel open failure."); |
127 | packet_integrity_check(payload_len, 4, type); | 122 | packet_integrity_check(payload_len, 4, type); |
128 | channel_input_open_failure(); | 123 | channel_input_open_failure(); |
129 | break; | 124 | break; |
130 | 125 | ||
131 | case SSH_MSG_CHANNEL_DATA: | 126 | case SSH_MSG_CHANNEL_DATA: |
132 | channel_input_data(payload_len); | 127 | channel_input_data(payload_len); |
133 | break; | 128 | break; |
134 | 129 | ||
135 | case SSH_MSG_CHANNEL_CLOSE: | 130 | case SSH_MSG_CHANNEL_CLOSE: |
136 | debug("Received channel close."); | 131 | debug("Received channel close."); |
137 | packet_integrity_check(payload_len, 4, type); | 132 | packet_integrity_check(payload_len, 4, type); |
138 | channel_input_close(); | 133 | channel_input_close(); |
139 | break; | 134 | break; |
140 | 135 | ||
141 | case SSH_MSG_CHANNEL_CLOSE_CONFIRMATION: | 136 | case SSH_MSG_CHANNEL_CLOSE_CONFIRMATION: |
142 | debug("Received channel close confirmation."); | 137 | debug("Received channel close confirmation."); |
143 | packet_integrity_check(payload_len, 4, type); | 138 | packet_integrity_check(payload_len, 4, type); |
144 | channel_input_close_confirmation(); | 139 | channel_input_close_confirmation(); |
145 | break; | 140 | break; |
146 | 141 | ||
147 | default: | 142 | default: |
148 | /* In this phase, any unexpected messages cause a protocol | 143 | /* In this phase, any unexpected messages cause a |
149 | error. This is to ease debugging; also, since no | 144 | protocol error. This is to ease debugging; |
150 | confirmations are sent messages, unprocessed unknown | 145 | also, since no confirmations are sent messages, |
151 | messages could cause strange problems. Any compatible | 146 | unprocessed unknown messages could cause |
152 | protocol extensions must be negotiated before entering the | 147 | strange problems. Any compatible protocol |
153 | interactive session. */ | 148 | extensions must be negotiated before entering |
154 | packet_disconnect("Protocol error during session: type %d", | 149 | the interactive session. */ |
155 | type); | 150 | packet_disconnect("Protocol error during session: type %d", |
151 | type); | ||
152 | } | ||
156 | } | 153 | } |
157 | } | ||
158 | } | 154 | } |
159 | 155 | ||
160 | /* Make packets from buffered stderr data, and buffer it for sending | 156 | /* |
161 | to the client. */ | 157 | * Make packets from buffered stderr data, and buffer it for sending |
162 | 158 | * to the client. | |
163 | void make_packets_from_stderr_data() | 159 | */ |
160 | void | ||
161 | make_packets_from_stderr_data() | ||
164 | { | 162 | { |
165 | int len; | 163 | int len; |
166 | 164 | ||
167 | /* Send buffered stderr data to the client. */ | 165 | /* Send buffered stderr data to the client. */ |
168 | while (buffer_len(&stderr_buffer) > 0 && | 166 | while (buffer_len(&stderr_buffer) > 0 && |
169 | packet_not_very_much_data_to_write()) | 167 | packet_not_very_much_data_to_write()) { |
170 | { | 168 | len = buffer_len(&stderr_buffer); |
171 | len = buffer_len(&stderr_buffer); | 169 | if (packet_is_interactive()) { |
172 | if (packet_is_interactive()) | 170 | if (len > 512) |
173 | { | 171 | len = 512; |
174 | if (len > 512) | 172 | } else { |
175 | len = 512; | 173 | /* Keep the packets at reasonable size. */ |
176 | } | 174 | if (len > packet_get_maxsize()) |
177 | else | 175 | len = packet_get_maxsize(); |
178 | { | 176 | } |
179 | if (len > packet_get_maxsize()) | 177 | packet_start(SSH_SMSG_STDERR_DATA); |
180 | len = packet_get_maxsize(); /* Keep the packets at reasonable size. */ | 178 | packet_put_string(buffer_ptr(&stderr_buffer), len); |
179 | packet_send(); | ||
180 | buffer_consume(&stderr_buffer, len); | ||
181 | stderr_bytes += len; | ||
181 | } | 182 | } |
182 | packet_start(SSH_SMSG_STDERR_DATA); | ||
183 | packet_put_string(buffer_ptr(&stderr_buffer), len); | ||
184 | packet_send(); | ||
185 | buffer_consume(&stderr_buffer, len); | ||
186 | stderr_bytes += len; | ||
187 | } | ||
188 | } | 183 | } |
189 | 184 | ||
190 | /* Make packets from buffered stdout data, and buffer it for sending to the | 185 | /* |
191 | client. */ | 186 | * Make packets from buffered stdout data, and buffer it for sending to the |
192 | 187 | * client. | |
193 | void make_packets_from_stdout_data() | 188 | */ |
189 | void | ||
190 | make_packets_from_stdout_data() | ||
194 | { | 191 | { |
195 | int len; | 192 | int len; |
196 | 193 | ||
197 | /* Send buffered stdout data to the client. */ | 194 | /* Send buffered stdout data to the client. */ |
198 | while (buffer_len(&stdout_buffer) > 0 && | 195 | while (buffer_len(&stdout_buffer) > 0 && |
199 | packet_not_very_much_data_to_write()) | 196 | packet_not_very_much_data_to_write()) { |
200 | { | 197 | len = buffer_len(&stdout_buffer); |
201 | len = buffer_len(&stdout_buffer); | 198 | if (packet_is_interactive()) { |
202 | if (packet_is_interactive()) | 199 | if (len > 512) |
203 | { | 200 | len = 512; |
204 | if (len > 512) | 201 | } else { |
205 | len = 512; | 202 | /* Keep the packets at reasonable size. */ |
206 | } | 203 | if (len > packet_get_maxsize()) |
207 | else | 204 | len = packet_get_maxsize(); |
208 | { | 205 | } |
209 | if (len > packet_get_maxsize()) | 206 | packet_start(SSH_SMSG_STDOUT_DATA); |
210 | len = packet_get_maxsize(); /* Keep the packets at reasonable size. */ | 207 | packet_put_string(buffer_ptr(&stdout_buffer), len); |
208 | packet_send(); | ||
209 | buffer_consume(&stdout_buffer, len); | ||
210 | stdout_bytes += len; | ||
211 | } | 211 | } |
212 | packet_start(SSH_SMSG_STDOUT_DATA); | ||
213 | packet_put_string(buffer_ptr(&stdout_buffer), len); | ||
214 | packet_send(); | ||
215 | buffer_consume(&stdout_buffer, len); | ||
216 | stdout_bytes += len; | ||
217 | } | ||
218 | } | 212 | } |
219 | 213 | ||
220 | /* Sleep in select() until we can do something. This will initialize the | 214 | /* |
221 | select masks. Upon return, the masks will indicate which descriptors | 215 | * Sleep in select() until we can do something. This will initialize the |
222 | have data or can accept data. Optionally, a maximum time can be specified | 216 | * select masks. Upon return, the masks will indicate which descriptors |
223 | for the duration of the wait (0 = infinite). */ | 217 | * have data or can accept data. Optionally, a maximum time can be specified |
224 | 218 | * for the duration of the wait (0 = infinite). | |
225 | void wait_until_can_do_something(fd_set *readset, fd_set *writeset, | 219 | */ |
226 | unsigned int max_time_milliseconds) | 220 | void |
221 | wait_until_can_do_something(fd_set * readset, fd_set * writeset, | ||
222 | unsigned int max_time_milliseconds) | ||
227 | { | 223 | { |
228 | struct timeval tv, *tvp; | 224 | struct timeval tv, *tvp; |
229 | int ret; | 225 | int ret; |
230 | 226 | ||
231 | /* When select fails we restart from here. */ | 227 | /* When select fails we restart from here. */ |
232 | retry_select: | 228 | retry_select: |
233 | 229 | ||
234 | /* Initialize select() masks. */ | 230 | /* Initialize select() masks. */ |
235 | FD_ZERO(readset); | 231 | FD_ZERO(readset); |
236 | 232 | ||
237 | /* Read packets from the client unless we have too much buffered stdin | 233 | /* Read packets from the client unless we have too much buffered |
238 | or channel data. */ | 234 | stdin or channel data. */ |
239 | if (buffer_len(&stdin_buffer) < 4096 && | 235 | if (buffer_len(&stdin_buffer) < 4096 && |
240 | channel_not_very_much_buffered_data()) | 236 | channel_not_very_much_buffered_data()) |
241 | FD_SET(connection_in, readset); | 237 | FD_SET(connection_in, readset); |
242 | 238 | ||
243 | /* If there is not too much data already buffered going to the client, | 239 | /* If there is not too much data already buffered going to the |
244 | try to get some more data from the program. */ | 240 | client, try to get some more data from the program. */ |
245 | if (packet_not_very_much_data_to_write()) | 241 | if (packet_not_very_much_data_to_write()) { |
246 | { | 242 | if (!fdout_eof) |
247 | if (!fdout_eof) | 243 | FD_SET(fdout, readset); |
248 | FD_SET(fdout, readset); | 244 | if (!fderr_eof) |
249 | if (!fderr_eof) | 245 | FD_SET(fderr, readset); |
250 | FD_SET(fderr, readset); | 246 | } |
251 | } | 247 | FD_ZERO(writeset); |
252 | 248 | ||
253 | FD_ZERO(writeset); | 249 | /* Set masks for channel descriptors. */ |
254 | 250 | channel_prepare_select(readset, writeset); | |
255 | /* Set masks for channel descriptors. */ | 251 | |
256 | channel_prepare_select(readset, writeset); | 252 | /* If we have buffered packet data going to the client, mark that |
257 | 253 | descriptor. */ | |
258 | /* If we have buffered packet data going to the client, mark that | 254 | if (packet_have_data_to_write()) |
259 | descriptor. */ | 255 | FD_SET(connection_out, writeset); |
260 | if (packet_have_data_to_write()) | 256 | |
261 | FD_SET(connection_out, writeset); | 257 | /* If we have buffered data, try to write some of that data to the |
262 | 258 | program. */ | |
263 | /* If we have buffered data, try to write some of that data to the | 259 | if (fdin != -1 && buffer_len(&stdin_buffer) > 0) |
264 | program. */ | 260 | FD_SET(fdin, writeset); |
265 | if (fdin != -1 && buffer_len(&stdin_buffer) > 0) | 261 | |
266 | FD_SET(fdin, writeset); | 262 | /* Update the maximum descriptor number if appropriate. */ |
267 | 263 | if (channel_max_fd() > max_fd) | |
268 | /* Update the maximum descriptor number if appropriate. */ | 264 | max_fd = channel_max_fd(); |
269 | if (channel_max_fd() > max_fd) | 265 | |
270 | max_fd = channel_max_fd(); | 266 | /* If child has terminated and there is enough buffer space to |
271 | 267 | read from it, then read as much as is available and exit. */ | |
272 | /* If child has terminated and there is enough buffer space to read from | 268 | if (child_terminated && packet_not_very_much_data_to_write()) |
273 | it, then read as much as is available and exit. */ | 269 | if (max_time_milliseconds == 0) |
274 | if (child_terminated && packet_not_very_much_data_to_write()) | 270 | max_time_milliseconds = 100; |
275 | if (max_time_milliseconds == 0) | 271 | |
276 | max_time_milliseconds = 100; | 272 | if (max_time_milliseconds == 0) |
277 | 273 | tvp = NULL; | |
278 | if (max_time_milliseconds == 0) | 274 | else { |
279 | tvp = NULL; | 275 | tv.tv_sec = max_time_milliseconds / 1000; |
280 | else | 276 | tv.tv_usec = 1000 * (max_time_milliseconds % 1000); |
281 | { | 277 | tvp = &tv; |
282 | tv.tv_sec = max_time_milliseconds / 1000; | 278 | } |
283 | tv.tv_usec = 1000 * (max_time_milliseconds % 1000); | ||
284 | tvp = &tv; | ||
285 | } | ||
286 | |||
287 | /* Wait for something to happen, or the timeout to expire. */ | ||
288 | ret = select(max_fd + 1, readset, writeset, NULL, tvp); | ||
289 | |||
290 | if (ret < 0) | ||
291 | { | ||
292 | if (errno != EINTR) | ||
293 | error("select: %.100s", strerror(errno)); | ||
294 | else | ||
295 | goto retry_select; | ||
296 | } | ||
297 | } | ||
298 | 279 | ||
299 | /* Processes input from the client and the program. Input data is stored | 280 | /* Wait for something to happen, or the timeout to expire. */ |
300 | in buffers and processed later. */ | 281 | ret = select(max_fd + 1, readset, writeset, NULL, tvp); |
301 | 282 | ||
302 | void process_input(fd_set *readset) | 283 | if (ret < 0) { |
303 | { | 284 | if (errno != EINTR) |
304 | int len; | 285 | error("select: %.100s", strerror(errno)); |
305 | char buf[16384]; | 286 | else |
306 | 287 | goto retry_select; | |
307 | /* Read and buffer any input data from the client. */ | ||
308 | if (FD_ISSET(connection_in, readset)) | ||
309 | { | ||
310 | len = read(connection_in, buf, sizeof(buf)); | ||
311 | if (len == 0) | ||
312 | fatal("Connection closed by remote host."); | ||
313 | |||
314 | /* There is a kernel bug on Solaris that causes select to sometimes | ||
315 | wake up even though there is no data available. */ | ||
316 | if (len < 0 && errno == EAGAIN) | ||
317 | len = 0; | ||
318 | |||
319 | if (len < 0) | ||
320 | fatal("Read error from remote host: %.100s", strerror(errno)); | ||
321 | |||
322 | /* Buffer any received data. */ | ||
323 | packet_process_incoming(buf, len); | ||
324 | } | ||
325 | |||
326 | /* Read and buffer any available stdout data from the program. */ | ||
327 | if (!fdout_eof && FD_ISSET(fdout, readset)) | ||
328 | { | ||
329 | len = read(fdout, buf, sizeof(buf)); | ||
330 | if (len <= 0) | ||
331 | fdout_eof = 1; | ||
332 | else | ||
333 | { | ||
334 | buffer_append(&stdout_buffer, buf, len); | ||
335 | fdout_bytes += len; | ||
336 | } | 288 | } |
337 | } | ||
338 | |||
339 | /* Read and buffer any available stderr data from the program. */ | ||
340 | if (!fderr_eof && FD_ISSET(fderr, readset)) | ||
341 | { | ||
342 | len = read(fderr, buf, sizeof(buf)); | ||
343 | if (len <= 0) | ||
344 | fderr_eof = 1; | ||
345 | else | ||
346 | buffer_append(&stderr_buffer, buf, len); | ||
347 | } | ||
348 | } | 289 | } |
349 | 290 | ||
350 | /* Sends data from internal buffers to client program stdin. */ | 291 | /* |
292 | * Processes input from the client and the program. Input data is stored | ||
293 | * in buffers and processed later. | ||
294 | */ | ||
295 | void | ||
296 | process_input(fd_set * readset) | ||
297 | { | ||
298 | int len; | ||
299 | char buf[16384]; | ||
300 | |||
301 | /* Read and buffer any input data from the client. */ | ||
302 | if (FD_ISSET(connection_in, readset)) { | ||
303 | len = read(connection_in, buf, sizeof(buf)); | ||
304 | if (len == 0) { | ||
305 | verbose("Connection closed by remote host."); | ||
306 | fatal_cleanup(); | ||
307 | } | ||
308 | /* There is a kernel bug on Solaris that causes select to | ||
309 | sometimes wake up even though there is no data | ||
310 | available. */ | ||
311 | if (len < 0 && errno == EAGAIN) | ||
312 | len = 0; | ||
313 | |||
314 | if (len < 0) { | ||
315 | verbose("Read error from remote host: %.100s", strerror(errno)); | ||
316 | fatal_cleanup(); | ||
317 | } | ||
318 | /* Buffer any received data. */ | ||
319 | packet_process_incoming(buf, len); | ||
320 | } | ||
321 | /* Read and buffer any available stdout data from the program. */ | ||
322 | if (!fdout_eof && FD_ISSET(fdout, readset)) { | ||
323 | len = read(fdout, buf, sizeof(buf)); | ||
324 | if (len <= 0) | ||
325 | fdout_eof = 1; | ||
326 | else { | ||
327 | buffer_append(&stdout_buffer, buf, len); | ||
328 | fdout_bytes += len; | ||
329 | } | ||
330 | } | ||
331 | /* Read and buffer any available stderr data from the program. */ | ||
332 | if (!fderr_eof && FD_ISSET(fderr, readset)) { | ||
333 | len = read(fderr, buf, sizeof(buf)); | ||
334 | if (len <= 0) | ||
335 | fderr_eof = 1; | ||
336 | else | ||
337 | buffer_append(&stderr_buffer, buf, len); | ||
338 | } | ||
339 | } | ||
351 | 340 | ||
352 | void process_output(fd_set *writeset) | 341 | /* |
342 | * Sends data from internal buffers to client program stdin. | ||
343 | */ | ||
344 | void | ||
345 | process_output(fd_set * writeset) | ||
353 | { | 346 | { |
354 | int len; | 347 | int len; |
355 | 348 | ||
356 | /* Write buffered data to program stdin. */ | 349 | /* Write buffered data to program stdin. */ |
357 | if (fdin != -1 && FD_ISSET(fdin, writeset)) | 350 | if (fdin != -1 && FD_ISSET(fdin, writeset)) { |
358 | { | 351 | len = write(fdin, buffer_ptr(&stdin_buffer), |
359 | len = write(fdin, buffer_ptr(&stdin_buffer), | 352 | buffer_len(&stdin_buffer)); |
360 | buffer_len(&stdin_buffer)); | 353 | if (len <= 0) { |
361 | if (len <= 0) | ||
362 | { | ||
363 | #ifdef USE_PIPES | 354 | #ifdef USE_PIPES |
364 | close(fdin); | 355 | close(fdin); |
365 | #else | 356 | #else |
366 | if (fdout == -1) | 357 | if (fdout == -1) |
367 | close(fdin); | 358 | close(fdin); |
368 | else | 359 | else |
369 | shutdown(fdin, SHUT_WR); /* We will no longer send. */ | 360 | shutdown(fdin, SHUT_WR); /* We will no longer send. */ |
370 | #endif | 361 | #endif |
371 | fdin = -1; | 362 | fdin = -1; |
372 | } | 363 | } else { |
373 | else | 364 | /* Successful write. Consume the data from the buffer. */ |
374 | { | 365 | buffer_consume(&stdin_buffer, len); |
375 | /* Successful write. Consume the data from the buffer. */ | 366 | /* Update the count of bytes written to the program. */ |
376 | buffer_consume(&stdin_buffer, len); | 367 | stdin_bytes += len; |
377 | /* Update the count of bytes written to the program. */ | 368 | } |
378 | stdin_bytes += len; | ||
379 | } | 369 | } |
380 | } | 370 | /* Send any buffered packet data to the client. */ |
381 | 371 | if (FD_ISSET(connection_out, writeset)) | |
382 | /* Send any buffered packet data to the client. */ | 372 | packet_write_poll(); |
383 | if (FD_ISSET(connection_out, writeset)) | ||
384 | packet_write_poll(); | ||
385 | } | 373 | } |
386 | 374 | ||
387 | /* Wait until all buffered output has been sent to the client. | 375 | /* |
388 | This is used when the program terminates. */ | 376 | * Wait until all buffered output has been sent to the client. |
389 | 377 | * This is used when the program terminates. | |
390 | void drain_output() | 378 | */ |
379 | void | ||
380 | drain_output() | ||
391 | { | 381 | { |
392 | /* Send any buffered stdout data to the client. */ | 382 | /* Send any buffered stdout data to the client. */ |
393 | if (buffer_len(&stdout_buffer) > 0) | 383 | if (buffer_len(&stdout_buffer) > 0) { |
394 | { | 384 | packet_start(SSH_SMSG_STDOUT_DATA); |
395 | packet_start(SSH_SMSG_STDOUT_DATA); | 385 | packet_put_string(buffer_ptr(&stdout_buffer), |
396 | packet_put_string(buffer_ptr(&stdout_buffer), | 386 | buffer_len(&stdout_buffer)); |
397 | buffer_len(&stdout_buffer)); | 387 | packet_send(); |
398 | packet_send(); | 388 | /* Update the count of sent bytes. */ |
399 | /* Update the count of sent bytes. */ | 389 | stdout_bytes += buffer_len(&stdout_buffer); |
400 | stdout_bytes += buffer_len(&stdout_buffer); | 390 | } |
401 | } | 391 | /* Send any buffered stderr data to the client. */ |
402 | 392 | if (buffer_len(&stderr_buffer) > 0) { | |
403 | /* Send any buffered stderr data to the client. */ | 393 | packet_start(SSH_SMSG_STDERR_DATA); |
404 | if (buffer_len(&stderr_buffer) > 0) | 394 | packet_put_string(buffer_ptr(&stderr_buffer), |
405 | { | 395 | buffer_len(&stderr_buffer)); |
406 | packet_start(SSH_SMSG_STDERR_DATA); | 396 | packet_send(); |
407 | packet_put_string(buffer_ptr(&stderr_buffer), | 397 | /* Update the count of sent bytes. */ |
408 | buffer_len(&stderr_buffer)); | 398 | stderr_bytes += buffer_len(&stderr_buffer); |
409 | packet_send(); | 399 | } |
410 | /* Update the count of sent bytes. */ | 400 | /* Wait until all buffered data has been written to the client. */ |
411 | stderr_bytes += buffer_len(&stderr_buffer); | 401 | packet_write_wait(); |
412 | } | ||
413 | |||
414 | /* Wait until all buffered data has been written to the client. */ | ||
415 | packet_write_wait(); | ||
416 | } | 402 | } |
417 | 403 | ||
418 | /* Performs the interactive session. This handles data transmission between | 404 | /* |
419 | the client and the program. Note that the notion of stdin, stdout, and | 405 | * Performs the interactive session. This handles data transmission between |
420 | stderr in this function is sort of reversed: this function writes to | 406 | * the client and the program. Note that the notion of stdin, stdout, and |
421 | stdin (of the child program), and reads from stdout and stderr (of the | 407 | * stderr in this function is sort of reversed: this function writes to |
422 | child program). */ | 408 | * stdin (of the child program), and reads from stdout and stderr (of the |
423 | 409 | * child program). | |
424 | void server_loop(int pid, int fdin_arg, int fdout_arg, int fderr_arg) | 410 | */ |
411 | void | ||
412 | server_loop(int pid, int fdin_arg, int fdout_arg, int fderr_arg) | ||
425 | { | 413 | { |
426 | int wait_status, wait_pid; /* Status and pid returned by wait(). */ | 414 | int wait_status, wait_pid; /* Status and pid returned by wait(). */ |
427 | int waiting_termination = 0; /* Have displayed waiting close message. */ | 415 | int waiting_termination = 0; /* Have displayed waiting close message. */ |
428 | unsigned int max_time_milliseconds; | 416 | unsigned int max_time_milliseconds; |
429 | unsigned int previous_stdout_buffer_bytes; | 417 | unsigned int previous_stdout_buffer_bytes; |
430 | unsigned int stdout_buffer_bytes; | 418 | unsigned int stdout_buffer_bytes; |
431 | int type; | 419 | int type; |
432 | 420 | ||
433 | debug("Entering interactive session."); | 421 | debug("Entering interactive session."); |
434 | 422 | ||
435 | /* Initialize the SIGCHLD kludge. */ | 423 | /* Initialize the SIGCHLD kludge. */ |
436 | child_pid = pid; | 424 | child_pid = pid; |
437 | child_terminated = 0; | 425 | child_terminated = 0; |
438 | signal(SIGCHLD, sigchld_handler); | 426 | signal(SIGCHLD, sigchld_handler); |
439 | 427 | ||
440 | /* Initialize our global variables. */ | 428 | /* Initialize our global variables. */ |
441 | fdin = fdin_arg; | 429 | fdin = fdin_arg; |
442 | fdout = fdout_arg; | 430 | fdout = fdout_arg; |
443 | fderr = fderr_arg; | 431 | fderr = fderr_arg; |
444 | connection_in = packet_get_connection_in(); | 432 | connection_in = packet_get_connection_in(); |
445 | connection_out = packet_get_connection_out(); | 433 | connection_out = packet_get_connection_out(); |
446 | 434 | ||
447 | previous_stdout_buffer_bytes = 0; | 435 | previous_stdout_buffer_bytes = 0; |
448 | 436 | ||
449 | /* Set approximate I/O buffer size. */ | 437 | /* Set approximate I/O buffer size. */ |
450 | if (packet_is_interactive()) | 438 | if (packet_is_interactive()) |
451 | buffer_high = 4096; | 439 | buffer_high = 4096; |
452 | else | 440 | else |
453 | buffer_high = 64 * 1024; | 441 | buffer_high = 64 * 1024; |
454 | 442 | ||
455 | /* Initialize max_fd to the maximum of the known file descriptors. */ | 443 | /* Initialize max_fd to the maximum of the known file descriptors. */ |
456 | max_fd = fdin; | 444 | max_fd = fdin; |
457 | if (fdout > max_fd) | 445 | if (fdout > max_fd) |
458 | max_fd = fdout; | 446 | max_fd = fdout; |
459 | if (fderr != -1 && fderr > max_fd) | 447 | if (fderr != -1 && fderr > max_fd) |
460 | max_fd = fderr; | 448 | max_fd = fderr; |
461 | if (connection_in > max_fd) | 449 | if (connection_in > max_fd) |
462 | max_fd = connection_in; | 450 | max_fd = connection_in; |
463 | if (connection_out > max_fd) | 451 | if (connection_out > max_fd) |
464 | max_fd = connection_out; | 452 | max_fd = connection_out; |
465 | 453 | ||
466 | /* Initialize Initialize buffers. */ | 454 | /* Initialize Initialize buffers. */ |
467 | buffer_init(&stdin_buffer); | 455 | buffer_init(&stdin_buffer); |
468 | buffer_init(&stdout_buffer); | 456 | buffer_init(&stdout_buffer); |
469 | buffer_init(&stderr_buffer); | 457 | buffer_init(&stderr_buffer); |
470 | 458 | ||
471 | /* If we have no separate fderr (which is the case when we have a pty - there | 459 | /* If we have no separate fderr (which is the case when we have a |
472 | we cannot make difference between data sent to stdout and stderr), | 460 | pty - there we cannot make difference between data sent to |
473 | indicate that we have seen an EOF from stderr. This way we don\'t | 461 | stdout and stderr), indicate that we have seen an EOF from |
474 | need to check the descriptor everywhere. */ | 462 | stderr. This way we don\'t need to check the descriptor |
475 | if (fderr == -1) | 463 | everywhere. */ |
476 | fderr_eof = 1; | 464 | if (fderr == -1) |
477 | 465 | fderr_eof = 1; | |
478 | /* Main loop of the server for the interactive session mode. */ | 466 | |
479 | for (;;) | 467 | /* Main loop of the server for the interactive session mode. */ |
480 | { | 468 | for (;;) { |
481 | fd_set readset, writeset; | 469 | fd_set readset, writeset; |
482 | 470 | ||
483 | /* Process buffered packets from the client. */ | 471 | /* Process buffered packets from the client. */ |
484 | process_buffered_input_packets(); | 472 | process_buffered_input_packets(); |
485 | 473 | ||
486 | /* If we have received eof, and there is no more pending input data, | 474 | /* If we have received eof, and there is no more pending |
487 | cause a real eof by closing fdin. */ | 475 | input data, cause a real eof by closing fdin. */ |
488 | if (stdin_eof && fdin != -1 && buffer_len(&stdin_buffer) == 0) | 476 | if (stdin_eof && fdin != -1 && buffer_len(&stdin_buffer) == 0) { |
489 | { | ||
490 | #ifdef USE_PIPES | 477 | #ifdef USE_PIPES |
491 | close(fdin); | 478 | close(fdin); |
492 | #else | 479 | #else |
493 | if (fdout == -1) | 480 | if (fdout == -1) |
494 | close(fdin); | 481 | close(fdin); |
495 | else | 482 | else |
496 | shutdown(fdin, SHUT_WR); /* We will no longer send. */ | 483 | shutdown(fdin, SHUT_WR); /* We will no longer send. */ |
497 | #endif | 484 | #endif |
498 | fdin = -1; | 485 | fdin = -1; |
486 | } | ||
487 | /* Make packets from buffered stderr data to send to the | ||
488 | client. */ | ||
489 | make_packets_from_stderr_data(); | ||
490 | |||
491 | /* Make packets from buffered stdout data to send to the | ||
492 | client. If there is very little to send, this arranges | ||
493 | to not send them now, but to wait a short while to see | ||
494 | if we are getting more data. This is necessary, as some | ||
495 | systems wake up readers from a pty after each separate | ||
496 | character. */ | ||
497 | max_time_milliseconds = 0; | ||
498 | stdout_buffer_bytes = buffer_len(&stdout_buffer); | ||
499 | if (stdout_buffer_bytes != 0 && stdout_buffer_bytes < 256 && | ||
500 | stdout_buffer_bytes != previous_stdout_buffer_bytes) { | ||
501 | /* try again after a while */ | ||
502 | max_time_milliseconds = 10; | ||
503 | } else { | ||
504 | /* Send it now. */ | ||
505 | make_packets_from_stdout_data(); | ||
506 | } | ||
507 | previous_stdout_buffer_bytes = buffer_len(&stdout_buffer); | ||
508 | |||
509 | /* Send channel data to the client. */ | ||
510 | if (packet_not_very_much_data_to_write()) | ||
511 | channel_output_poll(); | ||
512 | |||
513 | /* Bail out of the loop if the program has closed its | ||
514 | output descriptors, and we have no more data to send to | ||
515 | the client, and there is no pending buffered data. */ | ||
516 | if (fdout_eof && fderr_eof && !packet_have_data_to_write() && | ||
517 | buffer_len(&stdout_buffer) == 0 && buffer_len(&stderr_buffer) == 0) { | ||
518 | if (!channel_still_open()) | ||
519 | goto quit; | ||
520 | if (!waiting_termination) { | ||
521 | const char *s = "Waiting for forwarded connections to terminate...\r\n"; | ||
522 | char *cp; | ||
523 | waiting_termination = 1; | ||
524 | buffer_append(&stderr_buffer, s, strlen(s)); | ||
525 | |||
526 | /* Display list of open channels. */ | ||
527 | cp = channel_open_message(); | ||
528 | buffer_append(&stderr_buffer, cp, strlen(cp)); | ||
529 | xfree(cp); | ||
530 | } | ||
531 | } | ||
532 | /* Sleep in select() until we can do something. */ | ||
533 | wait_until_can_do_something(&readset, &writeset, | ||
534 | max_time_milliseconds); | ||
535 | |||
536 | /* Process any channel events. */ | ||
537 | channel_after_select(&readset, &writeset); | ||
538 | |||
539 | /* Process input from the client and from program stdout/stderr. */ | ||
540 | process_input(&readset); | ||
541 | |||
542 | /* Process output to the client and to program stdin. */ | ||
543 | process_output(&writeset); | ||
499 | } | 544 | } |
500 | 545 | ||
501 | /* Make packets from buffered stderr data to send to the client. */ | 546 | quit: |
502 | make_packets_from_stderr_data(); | 547 | /* Cleanup and termination code. */ |
503 | |||
504 | /* Make packets from buffered stdout data to send to the client. | ||
505 | If there is very little to send, this arranges to not send them | ||
506 | now, but to wait a short while to see if we are getting more data. | ||
507 | This is necessary, as some systems wake up readers from a pty after | ||
508 | each separate character. */ | ||
509 | max_time_milliseconds = 0; | ||
510 | stdout_buffer_bytes = buffer_len(&stdout_buffer); | ||
511 | if (stdout_buffer_bytes != 0 && stdout_buffer_bytes < 256 && | ||
512 | stdout_buffer_bytes != previous_stdout_buffer_bytes) | ||
513 | max_time_milliseconds = 10; /* try again after a while */ | ||
514 | else | ||
515 | make_packets_from_stdout_data(); /* Send it now. */ | ||
516 | previous_stdout_buffer_bytes = buffer_len(&stdout_buffer); | ||
517 | |||
518 | /* Send channel data to the client. */ | ||
519 | if (packet_not_very_much_data_to_write()) | ||
520 | channel_output_poll(); | ||
521 | |||
522 | /* Bail out of the loop if the program has closed its output descriptors, | ||
523 | and we have no more data to send to the client, and there is no | ||
524 | pending buffered data. */ | ||
525 | if (fdout_eof && fderr_eof && !packet_have_data_to_write() && | ||
526 | buffer_len(&stdout_buffer) == 0 && buffer_len(&stderr_buffer) == 0) | ||
527 | { | ||
528 | if (!channel_still_open()) | ||
529 | goto quit; | ||
530 | if (!waiting_termination) | ||
531 | { | ||
532 | const char *s = | ||
533 | "Waiting for forwarded connections to terminate...\r\n"; | ||
534 | char *cp; | ||
535 | waiting_termination = 1; | ||
536 | buffer_append(&stderr_buffer, s, strlen(s)); | ||
537 | |||
538 | /* Display list of open channels. */ | ||
539 | cp = channel_open_message(); | ||
540 | buffer_append(&stderr_buffer, cp, strlen(cp)); | ||
541 | xfree(cp); | ||
542 | } | ||
543 | } | ||
544 | 548 | ||
545 | /* Sleep in select() until we can do something. */ | 549 | /* Wait until all output has been sent to the client. */ |
546 | wait_until_can_do_something(&readset, &writeset, | 550 | drain_output(); |
547 | max_time_milliseconds); | ||
548 | |||
549 | /* Process any channel events. */ | ||
550 | channel_after_select(&readset, &writeset); | ||
551 | |||
552 | /* Process input from the client and from program stdout/stderr. */ | ||
553 | process_input(&readset); | ||
554 | |||
555 | /* Process output to the client and to program stdin. */ | ||
556 | process_output(&writeset); | ||
557 | } | ||
558 | |||
559 | quit: | ||
560 | /* Cleanup and termination code. */ | ||
561 | |||
562 | /* Wait until all output has been sent to the client. */ | ||
563 | drain_output(); | ||
564 | |||
565 | debug("End of interactive session; stdin %ld, stdout (read %ld, sent %ld), stderr %ld bytes.", | ||
566 | stdin_bytes, fdout_bytes, stdout_bytes, stderr_bytes); | ||
567 | |||
568 | /* Free and clear the buffers. */ | ||
569 | buffer_free(&stdin_buffer); | ||
570 | buffer_free(&stdout_buffer); | ||
571 | buffer_free(&stderr_buffer); | ||
572 | |||
573 | /* Close the file descriptors. */ | ||
574 | if (fdout != -1) | ||
575 | close(fdout); | ||
576 | fdout = -1; | ||
577 | fdout_eof = 1; | ||
578 | if (fderr != -1) | ||
579 | close(fderr); | ||
580 | fderr = -1; | ||
581 | fderr_eof = 1; | ||
582 | if (fdin != -1) | ||
583 | close(fdin); | ||
584 | fdin = -1; | ||
585 | |||
586 | /* Stop listening for channels; this removes unix domain sockets. */ | ||
587 | channel_stop_listening(); | ||
588 | |||
589 | /* Wait for the child to exit. Get its exit status. */ | ||
590 | wait_pid = wait(&wait_status); | ||
591 | if (wait_pid < 0) | ||
592 | { | ||
593 | /* It is possible that the wait was handled by SIGCHLD handler. This | ||
594 | may result in either: this call returning with EINTR, or: this | ||
595 | call returning ECHILD. */ | ||
596 | if (child_terminated) | ||
597 | wait_status = child_wait_status; | ||
598 | else | ||
599 | packet_disconnect("wait: %.100s", strerror(errno)); | ||
600 | } | ||
601 | else | ||
602 | { | ||
603 | /* Check if it matches the process we forked. */ | ||
604 | if (wait_pid != pid) | ||
605 | error("Strange, wait returned pid %d, expected %d", wait_pid, pid); | ||
606 | } | ||
607 | |||
608 | /* We no longer want our SIGCHLD handler to be called. */ | ||
609 | signal(SIGCHLD, SIG_DFL); | ||
610 | |||
611 | /* Check if it exited normally. */ | ||
612 | if (WIFEXITED(wait_status)) | ||
613 | { | ||
614 | /* Yes, normal exit. Get exit status and send it to the client. */ | ||
615 | debug("Command exited with status %d.", WEXITSTATUS(wait_status)); | ||
616 | packet_start(SSH_SMSG_EXITSTATUS); | ||
617 | packet_put_int(WEXITSTATUS(wait_status)); | ||
618 | packet_send(); | ||
619 | packet_write_wait(); | ||
620 | |||
621 | /* Wait for exit confirmation. Note that there might be other | ||
622 | packets coming before it; however, the program has already died | ||
623 | so we just ignore them. The client is supposed to respond with | ||
624 | the confirmation when it receives the exit status. */ | ||
625 | do | ||
626 | { | ||
627 | int plen; | ||
628 | type = packet_read(&plen); | ||
629 | } | ||
630 | while (type != SSH_CMSG_EXIT_CONFIRMATION); | ||
631 | 551 | ||
632 | debug("Received exit confirmation."); | 552 | debug("End of interactive session; stdin %ld, stdout (read %ld, sent %ld), stderr %ld bytes.", |
633 | return; | 553 | stdin_bytes, fdout_bytes, stdout_bytes, stderr_bytes); |
634 | } | ||
635 | 554 | ||
636 | /* Check if the program terminated due to a signal. */ | 555 | /* Free and clear the buffers. */ |
637 | if (WIFSIGNALED(wait_status)) | 556 | buffer_free(&stdin_buffer); |
638 | packet_disconnect("Command terminated on signal %d.", | 557 | buffer_free(&stdout_buffer); |
639 | WTERMSIG(wait_status)); | 558 | buffer_free(&stderr_buffer); |
640 | 559 | ||
641 | /* Some weird exit cause. Just exit. */ | 560 | /* Close the file descriptors. */ |
642 | packet_disconnect("wait returned status %04x.", wait_status); | 561 | if (fdout != -1) |
643 | /*NOTREACHED*/ | 562 | close(fdout); |
644 | } | 563 | fdout = -1; |
564 | fdout_eof = 1; | ||
565 | if (fderr != -1) | ||
566 | close(fderr); | ||
567 | fderr = -1; | ||
568 | fderr_eof = 1; | ||
569 | if (fdin != -1) | ||
570 | close(fdin); | ||
571 | fdin = -1; | ||
572 | |||
573 | /* Stop listening for channels; this removes unix domain sockets. */ | ||
574 | channel_stop_listening(); | ||
575 | |||
576 | /* Wait for the child to exit. Get its exit status. */ | ||
577 | wait_pid = wait(&wait_status); | ||
578 | if (wait_pid < 0) { | ||
579 | /* | ||
580 | * It is possible that the wait was handled by SIGCHLD | ||
581 | * handler. This may result in either: this call | ||
582 | * returning with EINTR, or: this call returning ECHILD. | ||
583 | */ | ||
584 | if (child_terminated) | ||
585 | wait_status = child_wait_status; | ||
586 | else | ||
587 | packet_disconnect("wait: %.100s", strerror(errno)); | ||
588 | } else { | ||
589 | /* Check if it matches the process we forked. */ | ||
590 | if (wait_pid != pid) | ||
591 | error("Strange, wait returned pid %d, expected %d", | ||
592 | wait_pid, pid); | ||
593 | } | ||
645 | 594 | ||
595 | /* We no longer want our SIGCHLD handler to be called. */ | ||
596 | signal(SIGCHLD, SIG_DFL); | ||
597 | |||
598 | /* Check if it exited normally. */ | ||
599 | if (WIFEXITED(wait_status)) { | ||
600 | /* Yes, normal exit. Get exit status and send it to the client. */ | ||
601 | debug("Command exited with status %d.", WEXITSTATUS(wait_status)); | ||
602 | packet_start(SSH_SMSG_EXITSTATUS); | ||
603 | packet_put_int(WEXITSTATUS(wait_status)); | ||
604 | packet_send(); | ||
605 | packet_write_wait(); | ||
606 | |||
607 | /* Wait for exit confirmation. Note that there might be | ||
608 | other packets coming before it; however, the program | ||
609 | has already died so we just ignore them. The client is | ||
610 | supposed to respond with the confirmation when it | ||
611 | receives the exit status. */ | ||
612 | do { | ||
613 | int plen; | ||
614 | type = packet_read(&plen); | ||
615 | } | ||
616 | while (type != SSH_CMSG_EXIT_CONFIRMATION); | ||
617 | |||
618 | debug("Received exit confirmation."); | ||
619 | return; | ||
620 | } | ||
621 | /* Check if the program terminated due to a signal. */ | ||
622 | if (WIFSIGNALED(wait_status)) | ||
623 | packet_disconnect("Command terminated on signal %d.", | ||
624 | WTERMSIG(wait_status)); | ||
625 | |||
626 | /* Some weird exit cause. Just exit. */ | ||
627 | packet_disconnect("wait returned status %04x.", wait_status); | ||
628 | /* NOTREACHED */ | ||
629 | } | ||
@@ -1,20 +1,13 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | |
3 | ssh-add.c | 3 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
4 | 4 | * All rights reserved | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Created: Thu Apr 6 00:52:24 1995 ylo |
6 | 6 | * Adds an identity to the authentication server, or removes an identity. | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | */ |
8 | All rights reserved | ||
9 | |||
10 | Created: Thu Apr 6 00:52:24 1995 ylo | ||
11 | |||
12 | Adds an identity to the authentication server, or removes an identity. | ||
13 | |||
14 | */ | ||
15 | 8 | ||
16 | #include "includes.h" | 9 | #include "includes.h" |
17 | RCSID("$Id: ssh-add.c,v 1.12 1999/11/23 00:24:32 damien Exp $"); | 10 | RCSID("$Id: ssh-add.c,v 1.13 1999/11/24 13:26:22 damien Exp $"); |
18 | 11 | ||
19 | #include "rsa.h" | 12 | #include "rsa.h" |
20 | #include "ssh.h" | 13 | #include "ssh.h" |
@@ -35,291 +28,279 @@ const char *__progname = "ssh-add"; | |||
35 | void | 28 | void |
36 | delete_file(AuthenticationConnection *ac, const char *filename) | 29 | delete_file(AuthenticationConnection *ac, const char *filename) |
37 | { | 30 | { |
38 | RSA *key; | 31 | RSA *key; |
39 | char *comment; | 32 | char *comment; |
40 | 33 | ||
41 | key = RSA_new(); | 34 | key = RSA_new(); |
42 | if (!load_public_key(filename, key, &comment)) | 35 | if (!load_public_key(filename, key, &comment)) { |
43 | { | 36 | printf("Bad key file %s: %s\n", filename, strerror(errno)); |
44 | printf("Bad key file %s: %s\n", filename, strerror(errno)); | 37 | return; |
45 | return; | 38 | } |
46 | } | 39 | if (ssh_remove_identity(ac, key)) |
47 | 40 | fprintf(stderr, "Identity removed: %s (%s)\n", filename, comment); | |
48 | if (ssh_remove_identity(ac, key)) | 41 | else |
49 | fprintf(stderr, "Identity removed: %s (%s)\n", filename, comment); | 42 | fprintf(stderr, "Could not remove identity: %s\n", filename); |
50 | else | 43 | RSA_free(key); |
51 | fprintf(stderr, "Could not remove identity: %s\n", filename); | 44 | xfree(comment); |
52 | RSA_free(key); | ||
53 | xfree(comment); | ||
54 | } | 45 | } |
55 | 46 | ||
56 | void | 47 | void |
57 | delete_all(AuthenticationConnection *ac) | 48 | delete_all(AuthenticationConnection *ac) |
58 | { | 49 | { |
59 | /* Send a request to remove all identities. */ | 50 | /* Send a request to remove all identities. */ |
60 | if (ssh_remove_all_identities(ac)) | 51 | if (ssh_remove_all_identities(ac)) |
61 | fprintf(stderr, "All identities removed.\n"); | 52 | fprintf(stderr, "All identities removed.\n"); |
62 | else | 53 | else |
63 | fprintf(stderr, "Failed to remove all identitities.\n"); | 54 | fprintf(stderr, "Failed to remove all identitities.\n"); |
64 | } | 55 | } |
65 | 56 | ||
66 | void | 57 | void |
67 | add_file(AuthenticationConnection *ac, const char *filename) | 58 | add_file(AuthenticationConnection *ac, const char *filename) |
68 | { | 59 | { |
69 | RSA *key; | 60 | RSA *key; |
70 | RSA *public_key; | 61 | RSA *public_key; |
71 | char *saved_comment, *comment; | 62 | char *saved_comment, *comment; |
72 | int success; | 63 | int success; |
73 | 64 | ||
74 | key = RSA_new(); | 65 | key = RSA_new(); |
75 | public_key = RSA_new(); | 66 | public_key = RSA_new(); |
76 | if (!load_public_key(filename, public_key, &saved_comment)) | 67 | if (!load_public_key(filename, public_key, &saved_comment)) { |
77 | { | 68 | printf("Bad key file %s: %s\n", filename, strerror(errno)); |
78 | printf("Bad key file %s: %s\n", filename, strerror(errno)); | 69 | return; |
79 | return; | 70 | } |
80 | } | 71 | RSA_free(public_key); |
81 | RSA_free(public_key); | ||
82 | |||
83 | /* At first, try empty passphrase */ | ||
84 | success = load_private_key(filename, "", key, &comment); | ||
85 | if (!success) { | ||
86 | printf("Need passphrase for %s (%s).\n", filename, saved_comment); | ||
87 | if (!isatty(STDIN_FILENO)) { | ||
88 | #ifdef USE_EXTERNAL_ASKPASS | ||
89 | int prompts = 3; | ||
90 | 72 | ||
91 | while (prompts && !success) | 73 | /* At first, try empty passphrase */ |
92 | { | 74 | success = load_private_key(filename, "", key, &comment); |
93 | success = askpass(filename, key, saved_comment, &comment); | 75 | if (!success) { |
94 | prompts--; | 76 | printf("Need passphrase for %s (%s).\n", filename, saved_comment); |
95 | } | 77 | if (!isatty(STDIN_FILENO)) { |
96 | if (!success) | 78 | #ifdef USE_EXTERNAL_ASKPASS |
97 | { | 79 | int prompts = 3; |
98 | xfree(saved_comment); | 80 | while (prompts && !success) { |
99 | return; | 81 | success = askpass(filename, key, saved_comment, &comment); |
100 | } | 82 | prompts--; |
83 | } | ||
84 | if (!success) { | ||
85 | xfree(saved_comment); | ||
86 | return; | ||
87 | } | ||
101 | #else /* !USE_EXTERNAL_ASKPASS */ | 88 | #else /* !USE_EXTERNAL_ASKPASS */ |
102 | xfree(saved_comment); | 89 | xfree(saved_comment); |
103 | return; | 90 | return; |
104 | #endif /* USE_EXTERNAL_ASKPASS */ | 91 | #endif /* USE_EXTERNAL_ASKPASS */ |
105 | } | 92 | } |
106 | 93 | ||
107 | while (!success) { | 94 | while (!success) { |
108 | char *pass = read_passphrase("Enter passphrase: ", 1); | 95 | char *pass = read_passphrase("Enter passphrase: ", 1); |
109 | if (strcmp(pass, "") == 0){ | 96 | if (strcmp(pass, "") == 0) { |
110 | xfree(pass); | 97 | xfree(pass); |
111 | xfree(saved_comment); | 98 | xfree(saved_comment); |
112 | return; | 99 | return; |
113 | } | 100 | } |
114 | success = load_private_key(filename, pass, key, &comment); | 101 | success = load_private_key(filename, pass, key, &comment); |
115 | memset(pass, 0, strlen(pass)); | 102 | memset(pass, 0, strlen(pass)); |
116 | xfree(pass); | 103 | xfree(pass); |
117 | if (success) | 104 | if (success) |
118 | break; | 105 | break; |
119 | printf("Bad passphrase.\n"); | 106 | printf("Bad passphrase.\n"); |
120 | } | 107 | } |
121 | } | 108 | } |
122 | xfree(saved_comment); | 109 | xfree(saved_comment); |
123 | 110 | ||
124 | if (ssh_add_identity(ac, key, comment)) | 111 | if (ssh_add_identity(ac, key, comment)) |
125 | fprintf(stderr, "Identity added: %s (%s)\n", filename, comment); | 112 | fprintf(stderr, "Identity added: %s (%s)\n", filename, comment); |
126 | else | 113 | else |
127 | fprintf(stderr, "Could not add identity: %s\n", filename); | 114 | fprintf(stderr, "Could not add identity: %s\n", filename); |
128 | RSA_free(key); | 115 | RSA_free(key); |
129 | xfree(comment); | 116 | xfree(comment); |
130 | } | 117 | } |
131 | 118 | ||
132 | void | 119 | void |
133 | list_identities(AuthenticationConnection *ac, int fp) | 120 | list_identities(AuthenticationConnection *ac, int fp) |
134 | { | 121 | { |
135 | BIGNUM *e, *n; | 122 | BIGNUM *e, *n; |
136 | int status; | 123 | int status; |
137 | char *comment; | 124 | char *comment; |
138 | int had_identities; | 125 | int had_identities; |
139 | 126 | ||
140 | e = BN_new(); | 127 | e = BN_new(); |
141 | n = BN_new(); | 128 | n = BN_new(); |
142 | had_identities = 0; | 129 | had_identities = 0; |
143 | for (status = ssh_get_first_identity(ac, e, n, &comment); | 130 | for (status = ssh_get_first_identity(ac, e, n, &comment); |
144 | status; | 131 | status; |
145 | status = ssh_get_next_identity(ac, e, n, &comment)) | 132 | status = ssh_get_next_identity(ac, e, n, &comment)) { |
146 | { | 133 | unsigned int bits = BN_num_bits(n); |
147 | unsigned int bits = BN_num_bits(n); | 134 | had_identities = 1; |
148 | had_identities = 1; | 135 | if (fp) { |
149 | if (fp) { | 136 | printf("%d %s %s\n", bits, fingerprint(e, n), comment); |
150 | printf("%d %s %s\n", bits, fingerprint(e, n), comment); | 137 | } else { |
151 | } else { | 138 | char *ebuf, *nbuf; |
152 | char *ebuf, *nbuf; | 139 | ebuf = BN_bn2dec(e); |
153 | ebuf = BN_bn2dec(e); | 140 | if (ebuf == NULL) { |
154 | if (ebuf == NULL) { | 141 | error("list_identities: BN_bn2dec(e) failed."); |
155 | error("list_identities: BN_bn2dec(e) failed."); | 142 | } else { |
156 | }else{ | 143 | nbuf = BN_bn2dec(n); |
157 | nbuf = BN_bn2dec(n); | 144 | if (nbuf == NULL) { |
158 | if (nbuf == NULL) { | 145 | error("list_identities: BN_bn2dec(n) failed."); |
159 | error("list_identities: BN_bn2dec(n) failed."); | 146 | } else { |
160 | }else{ | 147 | printf("%d %s %s %s\n", bits, ebuf, nbuf, comment); |
161 | printf("%d %s %s %s\n", bits, ebuf, nbuf, comment); | 148 | free(nbuf); |
162 | free(nbuf); | 149 | } |
163 | } | 150 | free(ebuf); |
164 | free(ebuf); | 151 | } |
152 | } | ||
153 | xfree(comment); | ||
165 | } | 154 | } |
166 | } | 155 | BN_clear_free(e); |
167 | xfree(comment); | 156 | BN_clear_free(n); |
168 | } | 157 | if (!had_identities) |
169 | BN_clear_free(e); | 158 | printf("The agent has no identities.\n"); |
170 | BN_clear_free(n); | ||
171 | if (!had_identities) | ||
172 | printf("The agent has no identities.\n"); | ||
173 | } | 159 | } |
174 | 160 | ||
175 | int | 161 | int |
176 | main(int argc, char **argv) | 162 | main(int argc, char **argv) |
177 | { | 163 | { |
178 | AuthenticationConnection *ac = NULL; | 164 | AuthenticationConnection *ac = NULL; |
179 | struct passwd *pw; | 165 | struct passwd *pw; |
180 | char buf[1024]; | 166 | char buf[1024]; |
181 | int no_files = 1; | 167 | int no_files = 1; |
182 | int i; | 168 | int i; |
183 | int deleting = 0; | 169 | int deleting = 0; |
184 | 170 | ||
185 | /* check if RSA support exists */ | 171 | /* check if RSA support exists */ |
186 | if (rsa_alive() == 0) { | 172 | if (rsa_alive() == 0) { |
187 | fprintf(stderr, | 173 | extern char *__progname; |
188 | "%s: no RSA support in libssl and libcrypto. See ssl(8).\n", | 174 | |
189 | __progname); | 175 | fprintf(stderr, |
190 | exit(1); | 176 | "%s: no RSA support in libssl and libcrypto. See ssl(8).\n", |
191 | } | 177 | __progname); |
192 | 178 | exit(1); | |
193 | /* At first, get a connection to the authentication agent. */ | ||
194 | ac = ssh_get_authentication_connection(); | ||
195 | if (ac == NULL) { | ||
196 | fprintf(stderr, "Could not open a connection to your authentication agent.\n"); | ||
197 | exit(1); | ||
198 | } | ||
199 | |||
200 | for (i = 1; i < argc; i++) | ||
201 | { | ||
202 | if ((strcmp(argv[i], "-l") == 0) || | ||
203 | (strcmp(argv[i], "-L") == 0)) | ||
204 | { | ||
205 | list_identities(ac, argv[i][1] == 'l' ? 1 : 0); | ||
206 | no_files = 0; /* Don't default-add/delete if -l. */ | ||
207 | continue; | ||
208 | } | 179 | } |
209 | if (strcmp(argv[i], "-d") == 0) | 180 | /* At first, get a connection to the authentication agent. */ |
210 | { | 181 | ac = ssh_get_authentication_connection(); |
211 | deleting = 1; | 182 | if (ac == NULL) { |
212 | continue; | 183 | fprintf(stderr, "Could not open a connection to your authentication agent.\n"); |
184 | exit(1); | ||
213 | } | 185 | } |
214 | if (strcmp(argv[i], "-D") == 0) | 186 | for (i = 1; i < argc; i++) { |
215 | { | 187 | if ((strcmp(argv[i], "-l") == 0) || |
216 | delete_all(ac); | 188 | (strcmp(argv[i], "-L") == 0)) { |
217 | no_files = 0; | 189 | list_identities(ac, argv[i][1] == 'l' ? 1 : 0); |
218 | continue; | 190 | /* Don't default-add/delete if -l. */ |
191 | no_files = 0; | ||
192 | continue; | ||
193 | } | ||
194 | if (strcmp(argv[i], "-d") == 0) { | ||
195 | deleting = 1; | ||
196 | continue; | ||
197 | } | ||
198 | if (strcmp(argv[i], "-D") == 0) { | ||
199 | delete_all(ac); | ||
200 | no_files = 0; | ||
201 | continue; | ||
202 | } | ||
203 | no_files = 0; | ||
204 | if (deleting) | ||
205 | delete_file(ac, argv[i]); | ||
206 | else | ||
207 | add_file(ac, argv[i]); | ||
219 | } | 208 | } |
220 | no_files = 0; | 209 | if (no_files) { |
221 | if (deleting) | 210 | pw = getpwuid(getuid()); |
222 | delete_file(ac, argv[i]); | 211 | if (!pw) { |
223 | else | 212 | fprintf(stderr, "No user found with uid %d\n", (int) getuid()); |
224 | add_file(ac, argv[i]); | 213 | ssh_close_authentication_connection(ac); |
225 | } | 214 | exit(1); |
226 | if (no_files) | 215 | } |
227 | { | 216 | snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir, SSH_CLIENT_IDENTITY); |
228 | pw = getpwuid(getuid()); | 217 | if (deleting) |
229 | if (!pw) | 218 | delete_file(ac, buf); |
230 | { | 219 | else |
231 | fprintf(stderr, "No user found with uid %d\n", (int)getuid()); | 220 | add_file(ac, buf); |
232 | ssh_close_authentication_connection(ac); | ||
233 | exit(1); | ||
234 | } | 221 | } |
235 | snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir, SSH_CLIENT_IDENTITY); | 222 | ssh_close_authentication_connection(ac); |
236 | if (deleting) | 223 | exit(0); |
237 | delete_file(ac, buf); | ||
238 | else | ||
239 | add_file(ac, buf); | ||
240 | } | ||
241 | ssh_close_authentication_connection(ac); | ||
242 | exit(0); | ||
243 | } | 224 | } |
244 | 225 | ||
245 | #ifdef USE_EXTERNAL_ASKPASS | 226 | #ifdef USE_EXTERNAL_ASKPASS |
246 | int askpass(const char *filename, RSA *key, const char *saved_comment, char **comment) | 227 | int askpass(const char *filename, RSA *key, const char *saved_comment, char **comment) |
247 | { | 228 | { |
248 | int pipes[2]; | 229 | int pipes[2]; |
249 | char buf[1024]; | 230 | char buf[1024]; |
250 | int tmp; | 231 | int tmp; |
251 | pid_t child; | 232 | pid_t child; |
252 | FILE *pipef; | 233 | FILE *pipef; |
253 | 234 | ||
254 | /* Check that we are X11-capable */ | 235 | /* Check that we are X11-capable */ |
255 | if (getenv("DISPLAY") == NULL) | 236 | if (getenv("DISPLAY") == NULL) |
256 | exit(1); | 237 | exit(1); |
257 | 238 | ||
258 | if (pipe(pipes) == -1) { | 239 | if (pipe(pipes) == -1) { |
259 | fprintf(stderr, "Creating pipes failed: %s\n", strerror(errno)); | 240 | fprintf(stderr, "Creating pipes failed: %s\n", strerror(errno)); |
260 | exit(1); | 241 | exit(1); |
261 | } | 242 | } |
262 | 243 | ||
263 | if (fflush(NULL) == EOF) { | 244 | if (fflush(NULL) == EOF) { |
264 | fprintf(stderr, "Cannot flush buffers: %s\n", strerror(errno)); | 245 | fprintf(stderr, "Cannot flush buffers: %s\n", strerror(errno)); |
265 | exit(1); | 246 | exit(1); |
266 | } | 247 | } |
267 | 248 | ||
268 | child = fork(); | 249 | child = fork(); |
269 | if (child == -1) { | 250 | if (child == -1) { |
270 | fprintf(stderr, "Cannot fork: %s\n", strerror(errno)); | 251 | fprintf(stderr, "Cannot fork: %s\n", strerror(errno)); |
271 | exit(1); | 252 | exit(1); |
272 | } | 253 | } |
273 | 254 | ||
274 | if (child == 0) { | 255 | if (child == 0) { |
275 | /* In child */ | 256 | /* In child */ |
276 | 257 | ||
277 | close(pipes[0]); | 258 | close(pipes[0]); |
278 | if (dup2(pipes[1], 1) ==-1) { | 259 | if (dup2(pipes[1], 1) ==-1) { |
279 | fprintf(stderr, "dup2 failed: %s\n", strerror(errno)); | 260 | fprintf(stderr, "dup2 failed: %s\n", strerror(errno)); |
280 | exit(1); | 261 | exit(1); |
281 | } | 262 | } |
282 | 263 | ||
283 | tmp = snprintf(buf, sizeof(buf), "Need passphrase for %s", saved_comment); | 264 | tmp = snprintf(buf, sizeof(buf), "Need passphrase for %s", saved_comment); |
284 | /* skip the prompt if it won't fit */ | 265 | /* skip the prompt if it won't fit */ |
285 | if ((tmp < 0) || (tmp >= sizeof(buf))) | 266 | if ((tmp < 0) || (tmp >= sizeof(buf))) |
286 | tmp = execlp(ASKPASS_PROGRAM, "ssh-askpass", 0); | 267 | tmp = execlp(ASKPASS_PROGRAM, "ssh-askpass", 0); |
287 | else | 268 | else |
288 | tmp = execlp(ASKPASS_PROGRAM, "ssh-askpass", buf, 0); | 269 | tmp = execlp(ASKPASS_PROGRAM, "ssh-askpass", buf, 0); |
289 | 270 | ||
290 | /* Shouldn't get this far */ | 271 | /* Shouldn't get this far */ |
291 | fprintf(stderr, "Executing ssh-askpass failed: %s\n", strerror(errno)); | 272 | fprintf(stderr, "Executing ssh-askpass failed: %s\n", strerror(errno)); |
292 | exit(1); | 273 | exit(1); |
293 | } | 274 | } |
294 | 275 | ||
295 | /* In parent */ | 276 | /* In parent */ |
296 | close(pipes[1]); | 277 | close(pipes[1]); |
297 | 278 | ||
298 | if ((pipef = fdopen(pipes[0], "r")) == NULL) { | 279 | if ((pipef = fdopen(pipes[0], "r")) == NULL) { |
299 | fprintf(stderr, "fdopen failed: %s\n", strerror(errno)); | 280 | fprintf(stderr, "fdopen failed: %s\n", strerror(errno)); |
300 | exit(1); | 281 | exit(1); |
301 | } | 282 | } |
302 | 283 | ||
303 | /* Read passphrase back from child, abort if none presented */ | 284 | /* Read passphrase back from child, abort if none presented */ |
304 | if(fgets(buf, sizeof(buf), pipef) == NULL) | 285 | if(fgets(buf, sizeof(buf), pipef) == NULL) |
305 | exit(1); | 286 | exit(1); |
306 | 287 | ||
307 | fclose(pipef); | 288 | fclose(pipef); |
308 | 289 | ||
309 | if (strchr(buf, '\n')) | 290 | if (strchr(buf, '\n')) |
310 | *strchr(buf, '\n') = 0; | 291 | *strchr(buf, '\n') = 0; |
311 | 292 | ||
312 | if (waitpid(child, NULL, 0) == -1) { | 293 | if (waitpid(child, NULL, 0) == -1) { |
313 | fprintf(stderr, "Waiting for child failed: %s\n", | 294 | fprintf(stderr, "Waiting for child failed: %s\n", |
314 | strerror(errno)); | 295 | strerror(errno)); |
315 | exit(1); | 296 | exit(1); |
316 | } | 297 | } |
317 | 298 | ||
318 | /* Try password as it was presented */ | 299 | /* Try password as it was presented */ |
319 | tmp = load_private_key(filename, buf, key, comment); | 300 | tmp = load_private_key(filename, buf, key, comment); |
320 | 301 | ||
321 | memset(buf, 0, sizeof(buf)); | 302 | memset(buf, 0, sizeof(buf)); |
322 | 303 | ||
323 | return(tmp); | 304 | return(tmp); |
324 | } | 305 | } |
325 | #endif /* USE_EXTERNAL_ASKPASS */ | 306 | #endif /* USE_EXTERNAL_ASKPASS */ |
diff --git a/ssh-agent.c b/ssh-agent.c index f1ceb5692..70c2a7f65 100644 --- a/ssh-agent.c +++ b/ssh-agent.c | |||
@@ -1,22 +1,15 @@ | |||
1 | /* $OpenBSD: ssh-agent.c,v 1.20 1999/11/19 10:20:51 markus Exp $ */ | 1 | /* $OpenBSD: ssh-agent.c,v 1.22 1999/11/24 00:26:03 deraadt Exp $ */ |
2 | 2 | ||
3 | /* | 3 | /* |
4 | 4 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | |
5 | ssh-agent.c | 5 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
6 | 6 | * All rights reserved | |
7 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 7 | * Created: Wed Mar 29 03:46:59 1995 ylo |
8 | 8 | * The authentication agent program. | |
9 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 9 | */ |
10 | All rights reserved | ||
11 | |||
12 | Created: Wed Mar 29 03:46:59 1995 ylo | ||
13 | |||
14 | The authentication agent program. | ||
15 | |||
16 | */ | ||
17 | 10 | ||
18 | #include "includes.h" | 11 | #include "includes.h" |
19 | RCSID("$OpenBSD: ssh-agent.c,v 1.20 1999/11/19 10:20:51 markus Exp $"); | 12 | RCSID("$OpenBSD: ssh-agent.c,v 1.22 1999/11/24 00:26:03 deraadt Exp $"); |
20 | 13 | ||
21 | #include "ssh.h" | 14 | #include "ssh.h" |
22 | #include "rsa.h" | 15 | #include "rsa.h" |
@@ -35,27 +28,21 @@ RCSID("$OpenBSD: ssh-agent.c,v 1.20 1999/11/19 10:20:51 markus Exp $"); | |||
35 | #include <ssl/md5.h> | 28 | #include <ssl/md5.h> |
36 | #endif | 29 | #endif |
37 | 30 | ||
38 | #ifdef HAVE___PROGNAME | 31 | typedef struct { |
39 | extern char *__progname; | 32 | int fd; |
40 | #else /* HAVE___PROGNAME */ | 33 | enum { |
41 | const char *__progname = "ssh-agent"; | 34 | AUTH_UNUSED, AUTH_SOCKET, AUTH_CONNECTION |
42 | #endif /* HAVE___PROGNAME */ | 35 | } type; |
43 | 36 | Buffer input; | |
44 | typedef struct | 37 | Buffer output; |
45 | { | ||
46 | int fd; | ||
47 | enum { AUTH_UNUSED, AUTH_SOCKET, AUTH_CONNECTION } type; | ||
48 | Buffer input; | ||
49 | Buffer output; | ||
50 | } SocketEntry; | 38 | } SocketEntry; |
51 | 39 | ||
52 | unsigned int sockets_alloc = 0; | 40 | unsigned int sockets_alloc = 0; |
53 | SocketEntry *sockets = NULL; | 41 | SocketEntry *sockets = NULL; |
54 | 42 | ||
55 | typedef struct | 43 | typedef struct { |
56 | { | 44 | RSA *key; |
57 | RSA *key; | 45 | char *comment; |
58 | char *comment; | ||
59 | } Identity; | 46 | } Identity; |
60 | 47 | ||
61 | unsigned int num_identities = 0; | 48 | unsigned int num_identities = 0; |
@@ -70,640 +57,601 @@ int parent_pid = -1; | |||
70 | char socket_name[1024]; | 57 | char socket_name[1024]; |
71 | char socket_dir[1024]; | 58 | char socket_dir[1024]; |
72 | 59 | ||
60 | #ifdef HAVE___PROGNAME | ||
61 | extern char *__progname; | ||
62 | #else /* HAVE___PROGNAME */ | ||
63 | const char *__progname = "ssh-agent"; | ||
64 | #endif /* HAVE___PROGNAME */ | ||
65 | |||
73 | void | 66 | void |
74 | process_request_identity(SocketEntry *e) | 67 | process_request_identity(SocketEntry *e) |
75 | { | 68 | { |
76 | Buffer msg; | 69 | Buffer msg; |
77 | int i; | 70 | int i; |
78 | 71 | ||
79 | buffer_init(&msg); | 72 | buffer_init(&msg); |
80 | buffer_put_char(&msg, SSH_AGENT_RSA_IDENTITIES_ANSWER); | 73 | buffer_put_char(&msg, SSH_AGENT_RSA_IDENTITIES_ANSWER); |
81 | buffer_put_int(&msg, num_identities); | 74 | buffer_put_int(&msg, num_identities); |
82 | for (i = 0; i < num_identities; i++) | 75 | for (i = 0; i < num_identities; i++) { |
83 | { | 76 | buffer_put_int(&msg, BN_num_bits(identities[i].key->n)); |
84 | buffer_put_int(&msg, BN_num_bits(identities[i].key->n)); | 77 | buffer_put_bignum(&msg, identities[i].key->e); |
85 | buffer_put_bignum(&msg, identities[i].key->e); | 78 | buffer_put_bignum(&msg, identities[i].key->n); |
86 | buffer_put_bignum(&msg, identities[i].key->n); | 79 | buffer_put_string(&msg, identities[i].comment, |
87 | buffer_put_string(&msg, identities[i].comment, | 80 | strlen(identities[i].comment)); |
88 | strlen(identities[i].comment)); | 81 | } |
89 | } | 82 | buffer_put_int(&e->output, buffer_len(&msg)); |
90 | buffer_put_int(&e->output, buffer_len(&msg)); | 83 | buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg)); |
91 | buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg)); | 84 | buffer_free(&msg); |
92 | buffer_free(&msg); | ||
93 | } | 85 | } |
94 | 86 | ||
95 | void | 87 | void |
96 | process_authentication_challenge(SocketEntry *e) | 88 | process_authentication_challenge(SocketEntry *e) |
97 | { | 89 | { |
98 | int i, pub_bits, len; | 90 | int i, pub_bits, len; |
99 | BIGNUM *pub_e, *pub_n, *challenge; | 91 | BIGNUM *pub_e, *pub_n, *challenge; |
100 | Buffer msg; | 92 | Buffer msg; |
101 | MD5_CTX md; | 93 | MD5_CTX md; |
102 | unsigned char buf[32], mdbuf[16], session_id[16]; | 94 | unsigned char buf[32], mdbuf[16], session_id[16]; |
103 | unsigned int response_type; | 95 | unsigned int response_type; |
104 | 96 | ||
105 | buffer_init(&msg); | 97 | buffer_init(&msg); |
106 | pub_e = BN_new(); | 98 | pub_e = BN_new(); |
107 | pub_n = BN_new(); | 99 | pub_n = BN_new(); |
108 | challenge = BN_new(); | 100 | challenge = BN_new(); |
109 | pub_bits = buffer_get_int(&e->input); | 101 | pub_bits = buffer_get_int(&e->input); |
110 | buffer_get_bignum(&e->input, pub_e); | 102 | buffer_get_bignum(&e->input, pub_e); |
111 | buffer_get_bignum(&e->input, pub_n); | 103 | buffer_get_bignum(&e->input, pub_n); |
112 | buffer_get_bignum(&e->input, challenge); | 104 | buffer_get_bignum(&e->input, challenge); |
113 | if (buffer_len(&e->input) == 0) | 105 | if (buffer_len(&e->input) == 0) { |
114 | { | 106 | /* Compatibility code for old servers. */ |
115 | /* Compatibility code for old servers. */ | 107 | memset(session_id, 0, 16); |
116 | memset(session_id, 0, 16); | 108 | response_type = 0; |
117 | response_type = 0; | 109 | } else { |
118 | } | 110 | /* New code. */ |
119 | else | 111 | buffer_get(&e->input, (char *) session_id, 16); |
120 | { | 112 | response_type = buffer_get_int(&e->input); |
121 | /* New code. */ | 113 | } |
122 | buffer_get(&e->input, (char *)session_id, 16); | 114 | for (i = 0; i < num_identities; i++) |
123 | response_type = buffer_get_int(&e->input); | 115 | if (pub_bits == BN_num_bits(identities[i].key->n) && |
124 | } | 116 | BN_cmp(pub_e, identities[i].key->e) == 0 && |
125 | for (i = 0; i < num_identities; i++) | 117 | BN_cmp(pub_n, identities[i].key->n) == 0) { |
126 | if (pub_bits == BN_num_bits(identities[i].key->n) && | 118 | /* Decrypt the challenge using the private key. */ |
127 | BN_cmp(pub_e, identities[i].key->e) == 0 && | 119 | rsa_private_decrypt(challenge, challenge, identities[i].key); |
128 | BN_cmp(pub_n, identities[i].key->n) == 0) | 120 | |
129 | { | 121 | /* Compute the desired response. */ |
130 | /* Decrypt the challenge using the private key. */ | 122 | switch (response_type) { |
131 | rsa_private_decrypt(challenge, challenge, identities[i].key); | 123 | case 0:/* As of protocol 1.0 */ |
132 | 124 | /* This response type is no longer supported. */ | |
133 | /* Compute the desired response. */ | 125 | log("Compatibility with ssh protocol 1.0 no longer supported."); |
134 | switch (response_type) | 126 | buffer_put_char(&msg, SSH_AGENT_FAILURE); |
135 | { | 127 | goto send; |
136 | case 0: /* As of protocol 1.0 */ | 128 | |
137 | /* This response type is no longer supported. */ | 129 | case 1:/* As of protocol 1.1 */ |
138 | log("Compatibility with ssh protocol 1.0 no longer supported."); | 130 | /* The response is MD5 of decrypted challenge plus session id. */ |
139 | buffer_put_char(&msg, SSH_AGENT_FAILURE); | 131 | len = BN_num_bytes(challenge); |
140 | goto send; | 132 | |
141 | 133 | if (len <= 0 || len > 32) { | |
142 | case 1: /* As of protocol 1.1 */ | 134 | fatal("process_authentication_challenge: " |
143 | /* The response is MD5 of decrypted challenge plus session id. */ | 135 | "bad challenge length %d", len); |
144 | len = BN_num_bytes(challenge); | 136 | } |
145 | 137 | memset(buf, 0, 32); | |
146 | if (len <= 0 || len > 32) { | 138 | BN_bn2bin(challenge, buf + 32 - len); |
147 | fatal("process_authentication_challenge: " | 139 | MD5_Init(&md); |
148 | "bad challenge length %d", len); | 140 | MD5_Update(&md, buf, 32); |
149 | } | 141 | MD5_Update(&md, session_id, 16); |
150 | 142 | MD5_Final(mdbuf, &md); | |
151 | memset(buf, 0, 32); | 143 | break; |
152 | BN_bn2bin(challenge, buf + 32 - len); | 144 | |
153 | MD5_Init(&md); | 145 | default: |
154 | MD5_Update(&md, buf, 32); | 146 | fatal("process_authentication_challenge: bad response_type %d", |
155 | MD5_Update(&md, session_id, 16); | 147 | response_type); |
156 | MD5_Final(mdbuf, &md); | 148 | break; |
157 | break; | 149 | } |
158 | 150 | ||
159 | default: | 151 | /* Send the response. */ |
160 | fatal("process_authentication_challenge: bad response_type %d", | 152 | buffer_put_char(&msg, SSH_AGENT_RSA_RESPONSE); |
161 | response_type); | 153 | for (i = 0; i < 16; i++) |
162 | break; | 154 | buffer_put_char(&msg, mdbuf[i]); |
163 | } | 155 | |
164 | 156 | goto send; | |
165 | /* Send the response. */ | 157 | } |
166 | buffer_put_char(&msg, SSH_AGENT_RSA_RESPONSE); | 158 | /* Unknown identity. Send failure. */ |
167 | for (i = 0; i < 16; i++) | 159 | buffer_put_char(&msg, SSH_AGENT_FAILURE); |
168 | buffer_put_char(&msg, mdbuf[i]); | 160 | send: |
169 | 161 | buffer_put_int(&e->output, buffer_len(&msg)); | |
170 | goto send; | 162 | buffer_append(&e->output, buffer_ptr(&msg), |
171 | } | 163 | buffer_len(&msg)); |
172 | /* Unknown identity. Send failure. */ | 164 | buffer_free(&msg); |
173 | buffer_put_char(&msg, SSH_AGENT_FAILURE); | 165 | BN_clear_free(pub_e); |
174 | send: | 166 | BN_clear_free(pub_n); |
175 | buffer_put_int(&e->output, buffer_len(&msg)); | 167 | BN_clear_free(challenge); |
176 | buffer_append(&e->output, buffer_ptr(&msg), | ||
177 | buffer_len(&msg)); | ||
178 | buffer_free(&msg); | ||
179 | BN_clear_free(pub_e); | ||
180 | BN_clear_free(pub_n); | ||
181 | BN_clear_free(challenge); | ||
182 | } | 168 | } |
183 | 169 | ||
184 | void | 170 | void |
185 | process_remove_identity(SocketEntry *e) | 171 | process_remove_identity(SocketEntry *e) |
186 | { | 172 | { |
187 | unsigned int bits; | 173 | unsigned int bits; |
188 | unsigned int i; | 174 | unsigned int i; |
189 | BIGNUM *dummy, *n; | 175 | BIGNUM *dummy, *n; |
190 | 176 | ||
191 | dummy = BN_new(); | 177 | dummy = BN_new(); |
192 | n = BN_new(); | 178 | n = BN_new(); |
193 | 179 | ||
194 | /* Get the key from the packet. */ | 180 | /* Get the key from the packet. */ |
195 | bits = buffer_get_int(&e->input); | 181 | bits = buffer_get_int(&e->input); |
196 | buffer_get_bignum(&e->input, dummy); | 182 | buffer_get_bignum(&e->input, dummy); |
197 | buffer_get_bignum(&e->input, n); | 183 | buffer_get_bignum(&e->input, n); |
198 | 184 | ||
199 | if (bits != BN_num_bits(n)) | 185 | if (bits != BN_num_bits(n)) |
200 | error("Warning: keysize mismatch: actual %d, announced %d", | 186 | error("Warning: keysize mismatch: actual %d, announced %d", |
201 | BN_num_bits(n), bits); | 187 | BN_num_bits(n), bits); |
202 | 188 | ||
203 | /* Check if we have the key. */ | 189 | /* Check if we have the key. */ |
204 | for (i = 0; i < num_identities; i++) | 190 | for (i = 0; i < num_identities; i++) |
205 | if (BN_cmp(identities[i].key->n, n) == 0) | 191 | if (BN_cmp(identities[i].key->n, n) == 0) { |
206 | { | 192 | /* We have this key. Free the old key. Since we |
207 | /* We have this key. Free the old key. Since we don\'t want to leave | 193 | don\'t want to leave empty slots in the middle |
208 | empty slots in the middle of the array, we actually free the | 194 | of the array, we actually free the key there |
209 | key there and copy data from the last entry. */ | 195 | and copy data from the last entry. */ |
210 | RSA_free(identities[i].key); | 196 | RSA_free(identities[i].key); |
211 | xfree(identities[i].comment); | 197 | xfree(identities[i].comment); |
212 | if (i < num_identities - 1) | 198 | if (i < num_identities - 1) |
213 | identities[i] = identities[num_identities - 1]; | 199 | identities[i] = identities[num_identities - 1]; |
214 | num_identities--; | 200 | num_identities--; |
215 | BN_clear_free(dummy); | 201 | BN_clear_free(dummy); |
216 | BN_clear_free(n); | 202 | BN_clear_free(n); |
217 | 203 | ||
218 | /* Send success. */ | 204 | /* Send success. */ |
205 | buffer_put_int(&e->output, 1); | ||
206 | buffer_put_char(&e->output, SSH_AGENT_SUCCESS); | ||
207 | return; | ||
208 | } | ||
209 | /* We did not have the key. */ | ||
210 | BN_clear(dummy); | ||
211 | BN_clear(n); | ||
212 | |||
213 | /* Send failure. */ | ||
219 | buffer_put_int(&e->output, 1); | 214 | buffer_put_int(&e->output, 1); |
220 | buffer_put_char(&e->output, SSH_AGENT_SUCCESS); | 215 | buffer_put_char(&e->output, SSH_AGENT_FAILURE); |
221 | return; | ||
222 | } | ||
223 | /* We did not have the key. */ | ||
224 | BN_clear(dummy); | ||
225 | BN_clear(n); | ||
226 | |||
227 | /* Send failure. */ | ||
228 | buffer_put_int(&e->output, 1); | ||
229 | buffer_put_char(&e->output, SSH_AGENT_FAILURE); | ||
230 | } | 216 | } |
231 | 217 | ||
232 | /* Removes all identities from the agent. */ | 218 | /* |
233 | 219 | * Removes all identities from the agent. | |
220 | */ | ||
234 | void | 221 | void |
235 | process_remove_all_identities(SocketEntry *e) | 222 | process_remove_all_identities(SocketEntry *e) |
236 | { | 223 | { |
237 | unsigned int i; | 224 | unsigned int i; |
238 | |||
239 | /* Loop over all identities and clear the keys. */ | ||
240 | for (i = 0; i < num_identities; i++) | ||
241 | { | ||
242 | RSA_free(identities[i].key); | ||
243 | xfree(identities[i].comment); | ||
244 | } | ||
245 | |||
246 | /* Mark that there are no identities. */ | ||
247 | num_identities = 0; | ||
248 | |||
249 | /* Send success. */ | ||
250 | buffer_put_int(&e->output, 1); | ||
251 | buffer_put_char(&e->output, SSH_AGENT_SUCCESS); | ||
252 | return; | ||
253 | } | ||
254 | 225 | ||
255 | /* Adds an identity to the agent. */ | 226 | /* Loop over all identities and clear the keys. */ |
227 | for (i = 0; i < num_identities; i++) { | ||
228 | RSA_free(identities[i].key); | ||
229 | xfree(identities[i].comment); | ||
230 | } | ||
256 | 231 | ||
257 | void | 232 | /* Mark that there are no identities. */ |
258 | process_add_identity(SocketEntry *e) | 233 | num_identities = 0; |
259 | { | ||
260 | RSA *k; | ||
261 | int i; | ||
262 | BIGNUM *aux; | ||
263 | BN_CTX *ctx; | ||
264 | |||
265 | if (num_identities == 0) | ||
266 | identities = xmalloc(sizeof(Identity)); | ||
267 | else | ||
268 | identities = xrealloc(identities, (num_identities + 1) * sizeof(Identity)); | ||
269 | |||
270 | identities[num_identities].key = RSA_new(); | ||
271 | k = identities[num_identities].key; | ||
272 | buffer_get_int(&e->input); /* bits */ | ||
273 | k->n = BN_new(); | ||
274 | buffer_get_bignum(&e->input, k->n); | ||
275 | k->e = BN_new(); | ||
276 | buffer_get_bignum(&e->input, k->e); | ||
277 | k->d = BN_new(); | ||
278 | buffer_get_bignum(&e->input, k->d); | ||
279 | k->iqmp = BN_new(); | ||
280 | buffer_get_bignum(&e->input, k->iqmp); | ||
281 | /* SSH and SSL have p and q swapped */ | ||
282 | k->q = BN_new(); | ||
283 | buffer_get_bignum(&e->input, k->q); /* p */ | ||
284 | k->p = BN_new(); | ||
285 | buffer_get_bignum(&e->input, k->p); /* q */ | ||
286 | |||
287 | /* Generate additional parameters */ | ||
288 | aux = BN_new(); | ||
289 | ctx = BN_CTX_new(); | ||
290 | |||
291 | BN_sub(aux, k->q, BN_value_one()); | ||
292 | k->dmq1 = BN_new(); | ||
293 | BN_mod(k->dmq1, k->d, aux, ctx); | ||
294 | |||
295 | BN_sub(aux, k->p, BN_value_one()); | ||
296 | k->dmp1 = BN_new(); | ||
297 | BN_mod(k->dmp1, k->d, aux, ctx); | ||
298 | |||
299 | BN_clear_free(aux); | ||
300 | BN_CTX_free(ctx); | ||
301 | |||
302 | identities[num_identities].comment = buffer_get_string(&e->input, NULL); | ||
303 | |||
304 | /* Check if we already have the key. */ | ||
305 | for (i = 0; i < num_identities; i++) | ||
306 | if (BN_cmp(identities[i].key->n, k->n) == 0) | ||
307 | { | ||
308 | /* We already have this key. Clear and free the new data and | ||
309 | return success. */ | ||
310 | RSA_free(k); | ||
311 | xfree(identities[num_identities].comment); | ||
312 | 234 | ||
313 | /* Send success. */ | 235 | /* Send success. */ |
314 | buffer_put_int(&e->output, 1); | 236 | buffer_put_int(&e->output, 1); |
315 | buffer_put_char(&e->output, SSH_AGENT_SUCCESS); | 237 | buffer_put_char(&e->output, SSH_AGENT_SUCCESS); |
316 | return; | 238 | return; |
317 | } | 239 | } |
318 | 240 | ||
319 | /* Increment the number of identities. */ | 241 | /* |
320 | num_identities++; | 242 | * Adds an identity to the agent. |
321 | 243 | */ | |
322 | /* Send a success message. */ | 244 | void |
323 | buffer_put_int(&e->output, 1); | 245 | process_add_identity(SocketEntry *e) |
324 | buffer_put_char(&e->output, SSH_AGENT_SUCCESS); | 246 | { |
247 | RSA *k; | ||
248 | int i; | ||
249 | BIGNUM *aux; | ||
250 | BN_CTX *ctx; | ||
251 | |||
252 | if (num_identities == 0) | ||
253 | identities = xmalloc(sizeof(Identity)); | ||
254 | else | ||
255 | identities = xrealloc(identities, (num_identities + 1) * sizeof(Identity)); | ||
256 | |||
257 | identities[num_identities].key = RSA_new(); | ||
258 | k = identities[num_identities].key; | ||
259 | buffer_get_int(&e->input); /* bits */ | ||
260 | k->n = BN_new(); | ||
261 | buffer_get_bignum(&e->input, k->n); | ||
262 | k->e = BN_new(); | ||
263 | buffer_get_bignum(&e->input, k->e); | ||
264 | k->d = BN_new(); | ||
265 | buffer_get_bignum(&e->input, k->d); | ||
266 | k->iqmp = BN_new(); | ||
267 | buffer_get_bignum(&e->input, k->iqmp); | ||
268 | /* SSH and SSL have p and q swapped */ | ||
269 | k->q = BN_new(); | ||
270 | buffer_get_bignum(&e->input, k->q); /* p */ | ||
271 | k->p = BN_new(); | ||
272 | buffer_get_bignum(&e->input, k->p); /* q */ | ||
273 | |||
274 | /* Generate additional parameters */ | ||
275 | aux = BN_new(); | ||
276 | ctx = BN_CTX_new(); | ||
277 | |||
278 | BN_sub(aux, k->q, BN_value_one()); | ||
279 | k->dmq1 = BN_new(); | ||
280 | BN_mod(k->dmq1, k->d, aux, ctx); | ||
281 | |||
282 | BN_sub(aux, k->p, BN_value_one()); | ||
283 | k->dmp1 = BN_new(); | ||
284 | BN_mod(k->dmp1, k->d, aux, ctx); | ||
285 | |||
286 | BN_clear_free(aux); | ||
287 | BN_CTX_free(ctx); | ||
288 | |||
289 | identities[num_identities].comment = buffer_get_string(&e->input, NULL); | ||
290 | |||
291 | /* Check if we already have the key. */ | ||
292 | for (i = 0; i < num_identities; i++) | ||
293 | if (BN_cmp(identities[i].key->n, k->n) == 0) { | ||
294 | /* We already have this key. Clear and free the | ||
295 | new data and return success. */ | ||
296 | RSA_free(k); | ||
297 | xfree(identities[num_identities].comment); | ||
298 | |||
299 | /* Send success. */ | ||
300 | buffer_put_int(&e->output, 1); | ||
301 | buffer_put_char(&e->output, SSH_AGENT_SUCCESS); | ||
302 | return; | ||
303 | } | ||
304 | /* Increment the number of identities. */ | ||
305 | num_identities++; | ||
306 | |||
307 | /* Send a success message. */ | ||
308 | buffer_put_int(&e->output, 1); | ||
309 | buffer_put_char(&e->output, SSH_AGENT_SUCCESS); | ||
325 | } | 310 | } |
326 | 311 | ||
327 | void | 312 | void |
328 | process_message(SocketEntry *e) | 313 | process_message(SocketEntry *e) |
329 | { | 314 | { |
330 | unsigned int msg_len; | 315 | unsigned int msg_len; |
331 | unsigned int type; | 316 | unsigned int type; |
332 | unsigned char *cp; | 317 | unsigned char *cp; |
333 | if (buffer_len(&e->input) < 5) | 318 | if (buffer_len(&e->input) < 5) |
334 | return; /* Incomplete message. */ | 319 | return; /* Incomplete message. */ |
335 | cp = (unsigned char *)buffer_ptr(&e->input); | 320 | cp = (unsigned char *) buffer_ptr(&e->input); |
336 | msg_len = GET_32BIT(cp); | 321 | msg_len = GET_32BIT(cp); |
337 | if (msg_len > 256 * 1024) | 322 | if (msg_len > 256 * 1024) { |
338 | { | 323 | shutdown(e->fd, SHUT_RDWR); |
339 | shutdown(e->fd, SHUT_RDWR); | 324 | close(e->fd); |
340 | close(e->fd); | 325 | e->type = AUTH_UNUSED; |
341 | e->type = AUTH_UNUSED; | 326 | return; |
342 | return; | 327 | } |
343 | } | 328 | if (buffer_len(&e->input) < msg_len + 4) |
344 | if (buffer_len(&e->input) < msg_len + 4) | 329 | return; |
345 | return; | 330 | buffer_consume(&e->input, 4); |
346 | buffer_consume(&e->input, 4); | 331 | type = buffer_get_char(&e->input); |
347 | type = buffer_get_char(&e->input); | 332 | |
348 | 333 | switch (type) { | |
349 | switch (type) | 334 | case SSH_AGENTC_REQUEST_RSA_IDENTITIES: |
350 | { | 335 | process_request_identity(e); |
351 | case SSH_AGENTC_REQUEST_RSA_IDENTITIES: | 336 | break; |
352 | process_request_identity(e); | 337 | case SSH_AGENTC_RSA_CHALLENGE: |
353 | break; | 338 | process_authentication_challenge(e); |
354 | case SSH_AGENTC_RSA_CHALLENGE: | 339 | break; |
355 | process_authentication_challenge(e); | 340 | case SSH_AGENTC_ADD_RSA_IDENTITY: |
356 | break; | 341 | process_add_identity(e); |
357 | case SSH_AGENTC_ADD_RSA_IDENTITY: | 342 | break; |
358 | process_add_identity(e); | 343 | case SSH_AGENTC_REMOVE_RSA_IDENTITY: |
359 | break; | 344 | process_remove_identity(e); |
360 | case SSH_AGENTC_REMOVE_RSA_IDENTITY: | 345 | break; |
361 | process_remove_identity(e); | 346 | case SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES: |
362 | break; | 347 | process_remove_all_identities(e); |
363 | case SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES: | 348 | break; |
364 | process_remove_all_identities(e); | 349 | default: |
365 | break; | 350 | /* Unknown message. Respond with failure. */ |
366 | default: | 351 | error("Unknown message %d", type); |
367 | /* Unknown message. Respond with failure. */ | 352 | buffer_clear(&e->input); |
368 | error("Unknown message %d", type); | 353 | buffer_put_int(&e->output, 1); |
369 | buffer_clear(&e->input); | 354 | buffer_put_char(&e->output, SSH_AGENT_FAILURE); |
370 | buffer_put_int(&e->output, 1); | 355 | break; |
371 | buffer_put_char(&e->output, SSH_AGENT_FAILURE); | 356 | } |
372 | break; | ||
373 | } | ||
374 | } | 357 | } |
375 | 358 | ||
376 | void | 359 | void |
377 | new_socket(int type, int fd) | 360 | new_socket(int type, int fd) |
378 | { | 361 | { |
379 | unsigned int i, old_alloc; | 362 | unsigned int i, old_alloc; |
380 | if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) | 363 | if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) |
381 | error("fcntl O_NONBLOCK: %s", strerror(errno)); | 364 | error("fcntl O_NONBLOCK: %s", strerror(errno)); |
382 | 365 | ||
383 | if (fd > max_fd) | 366 | if (fd > max_fd) |
384 | max_fd = fd; | 367 | max_fd = fd; |
385 | 368 | ||
386 | for (i = 0; i < sockets_alloc; i++) | 369 | for (i = 0; i < sockets_alloc; i++) |
387 | if (sockets[i].type == AUTH_UNUSED) | 370 | if (sockets[i].type == AUTH_UNUSED) { |
388 | { | 371 | sockets[i].fd = fd; |
389 | sockets[i].fd = fd; | 372 | sockets[i].type = type; |
390 | sockets[i].type = type; | 373 | buffer_init(&sockets[i].input); |
391 | buffer_init(&sockets[i].input); | 374 | buffer_init(&sockets[i].output); |
392 | buffer_init(&sockets[i].output); | 375 | return; |
393 | return; | 376 | } |
394 | } | 377 | old_alloc = sockets_alloc; |
395 | old_alloc = sockets_alloc; | 378 | sockets_alloc += 10; |
396 | sockets_alloc += 10; | 379 | if (sockets) |
397 | if (sockets) | 380 | sockets = xrealloc(sockets, sockets_alloc * sizeof(sockets[0])); |
398 | sockets = xrealloc(sockets, sockets_alloc * sizeof(sockets[0])); | 381 | else |
399 | else | 382 | sockets = xmalloc(sockets_alloc * sizeof(sockets[0])); |
400 | sockets = xmalloc(sockets_alloc * sizeof(sockets[0])); | 383 | for (i = old_alloc; i < sockets_alloc; i++) |
401 | for (i = old_alloc; i < sockets_alloc; i++) | 384 | sockets[i].type = AUTH_UNUSED; |
402 | sockets[i].type = AUTH_UNUSED; | 385 | sockets[old_alloc].type = type; |
403 | sockets[old_alloc].type = type; | 386 | sockets[old_alloc].fd = fd; |
404 | sockets[old_alloc].fd = fd; | 387 | buffer_init(&sockets[old_alloc].input); |
405 | buffer_init(&sockets[old_alloc].input); | 388 | buffer_init(&sockets[old_alloc].output); |
406 | buffer_init(&sockets[old_alloc].output); | ||
407 | } | 389 | } |
408 | 390 | ||
409 | void | 391 | void |
410 | prepare_select(fd_set *readset, fd_set *writeset) | 392 | prepare_select(fd_set *readset, fd_set *writeset) |
411 | { | 393 | { |
412 | unsigned int i; | 394 | unsigned int i; |
413 | for (i = 0; i < sockets_alloc; i++) | 395 | for (i = 0; i < sockets_alloc; i++) |
414 | switch (sockets[i].type) | 396 | switch (sockets[i].type) { |
415 | { | 397 | case AUTH_SOCKET: |
416 | case AUTH_SOCKET: | 398 | case AUTH_CONNECTION: |
417 | case AUTH_CONNECTION: | 399 | FD_SET(sockets[i].fd, readset); |
418 | FD_SET(sockets[i].fd, readset); | 400 | if (buffer_len(&sockets[i].output) > 0) |
419 | if (buffer_len(&sockets[i].output) > 0) | 401 | FD_SET(sockets[i].fd, writeset); |
420 | FD_SET(sockets[i].fd, writeset); | 402 | break; |
421 | break; | 403 | case AUTH_UNUSED: |
422 | case AUTH_UNUSED: | 404 | break; |
423 | break; | 405 | default: |
424 | default: | 406 | fatal("Unknown socket type %d", sockets[i].type); |
425 | fatal("Unknown socket type %d", sockets[i].type); | 407 | break; |
426 | break; | 408 | } |
427 | } | ||
428 | } | 409 | } |
429 | 410 | ||
430 | void after_select(fd_set *readset, fd_set *writeset) | 411 | void |
412 | after_select(fd_set *readset, fd_set *writeset) | ||
431 | { | 413 | { |
432 | unsigned int i; | 414 | unsigned int i; |
433 | int len, sock; | 415 | int len, sock; |
434 | char buf[1024]; | 416 | char buf[1024]; |
435 | struct sockaddr_un sunaddr; | 417 | struct sockaddr_un sunaddr; |
436 | 418 | ||
437 | for (i = 0; i < sockets_alloc; i++) | 419 | for (i = 0; i < sockets_alloc; i++) |
438 | switch (sockets[i].type) | 420 | switch (sockets[i].type) { |
439 | { | 421 | case AUTH_UNUSED: |
440 | case AUTH_UNUSED: | 422 | break; |
441 | break; | 423 | case AUTH_SOCKET: |
442 | case AUTH_SOCKET: | 424 | if (FD_ISSET(sockets[i].fd, readset)) { |
443 | if (FD_ISSET(sockets[i].fd, readset)) | 425 | len = sizeof(sunaddr); |
444 | { | 426 | sock = accept(sockets[i].fd, (struct sockaddr *) & sunaddr, &len); |
445 | len = sizeof(sunaddr); | 427 | if (sock < 0) { |
446 | sock = accept(sockets[i].fd, (struct sockaddr *)&sunaddr, &len); | 428 | perror("accept from AUTH_SOCKET"); |
447 | if (sock < 0) | 429 | break; |
448 | { | 430 | } |
449 | perror("accept from AUTH_SOCKET"); | 431 | new_socket(AUTH_CONNECTION, sock); |
450 | break; | 432 | } |
451 | } | 433 | break; |
452 | new_socket(AUTH_CONNECTION, sock); | 434 | case AUTH_CONNECTION: |
453 | } | 435 | if (buffer_len(&sockets[i].output) > 0 && |
454 | break; | 436 | FD_ISSET(sockets[i].fd, writeset)) { |
455 | case AUTH_CONNECTION: | 437 | len = write(sockets[i].fd, buffer_ptr(&sockets[i].output), |
456 | if (buffer_len(&sockets[i].output) > 0 && | 438 | buffer_len(&sockets[i].output)); |
457 | FD_ISSET(sockets[i].fd, writeset)) | 439 | if (len <= 0) { |
458 | { | 440 | shutdown(sockets[i].fd, SHUT_RDWR); |
459 | len = write(sockets[i].fd, buffer_ptr(&sockets[i].output), | 441 | close(sockets[i].fd); |
460 | buffer_len(&sockets[i].output)); | 442 | sockets[i].type = AUTH_UNUSED; |
461 | if (len <= 0) | 443 | break; |
462 | { | 444 | } |
463 | shutdown(sockets[i].fd, SHUT_RDWR); | 445 | buffer_consume(&sockets[i].output, len); |
464 | close(sockets[i].fd); | 446 | } |
465 | sockets[i].type = AUTH_UNUSED; | 447 | if (FD_ISSET(sockets[i].fd, readset)) { |
466 | break; | 448 | len = read(sockets[i].fd, buf, sizeof(buf)); |
467 | } | 449 | if (len <= 0) { |
468 | buffer_consume(&sockets[i].output, len); | 450 | shutdown(sockets[i].fd, SHUT_RDWR); |
469 | } | 451 | close(sockets[i].fd); |
470 | if (FD_ISSET(sockets[i].fd, readset)) | 452 | sockets[i].type = AUTH_UNUSED; |
471 | { | 453 | break; |
472 | len = read(sockets[i].fd, buf, sizeof(buf)); | 454 | } |
473 | if (len <= 0) | 455 | buffer_append(&sockets[i].input, buf, len); |
474 | { | 456 | process_message(&sockets[i]); |
475 | shutdown(sockets[i].fd, SHUT_RDWR); | 457 | } |
476 | close(sockets[i].fd); | 458 | break; |
477 | sockets[i].type = AUTH_UNUSED; | 459 | default: |
478 | break; | 460 | fatal("Unknown type %d", sockets[i].type); |
479 | } | 461 | } |
480 | buffer_append(&sockets[i].input, buf, len); | ||
481 | process_message(&sockets[i]); | ||
482 | } | ||
483 | break; | ||
484 | default: | ||
485 | fatal("Unknown type %d", sockets[i].type); | ||
486 | } | ||
487 | } | 462 | } |
488 | 463 | ||
489 | void | 464 | void |
490 | check_parent_exists(int sig) | 465 | check_parent_exists(int sig) |
491 | { | 466 | { |
492 | if (kill(parent_pid, 0) < 0) | 467 | if (kill(parent_pid, 0) < 0) { |
493 | { | 468 | /* printf("Parent has died - Authentication agent exiting.\n"); */ |
494 | /* printf("Parent has died - Authentication agent exiting.\n"); */ | 469 | exit(1); |
495 | exit(1); | 470 | } |
496 | } | 471 | signal(SIGALRM, check_parent_exists); |
497 | signal(SIGALRM, check_parent_exists); | 472 | alarm(10); |
498 | alarm(10); | ||
499 | } | 473 | } |
500 | 474 | ||
501 | void | 475 | void |
502 | cleanup_socket(void) | 476 | cleanup_socket(void) |
503 | { | 477 | { |
504 | remove(socket_name); | 478 | remove(socket_name); |
505 | rmdir(socket_dir); | 479 | rmdir(socket_dir); |
506 | } | 480 | } |
507 | 481 | ||
508 | void | 482 | void |
509 | cleanup_exit(int i) | 483 | cleanup_exit(int i) |
510 | { | 484 | { |
511 | cleanup_socket(); | 485 | cleanup_socket(); |
512 | exit(i); | 486 | exit(i); |
513 | } | 487 | } |
514 | 488 | ||
515 | void | 489 | void |
516 | usage() | 490 | usage() |
517 | { | 491 | { |
518 | fprintf(stderr, "ssh-agent version %s\n", SSH_VERSION); | 492 | fprintf(stderr, "ssh-agent version %s\n", SSH_VERSION); |
519 | fprintf(stderr, "Usage: %s [-c | -s] [-k] [command {args...]]\n", | 493 | fprintf(stderr, "Usage: %s [-c | -s] [-k] [command {args...]]\n", |
520 | __progname); | 494 | __progname); |
521 | exit(1); | 495 | exit(1); |
522 | } | 496 | } |
523 | 497 | ||
524 | int | 498 | int |
525 | main(int ac, char **av) | 499 | main(int ac, char **av) |
526 | { | 500 | { |
527 | fd_set readset, writeset; | 501 | fd_set readset, writeset; |
528 | int sock, c_flag = 0, k_flag = 0, s_flag = 0, ch; | 502 | int sock, c_flag = 0, k_flag = 0, s_flag = 0, ch; |
529 | struct sockaddr_un sunaddr; | 503 | struct sockaddr_un sunaddr; |
530 | pid_t pid; | 504 | pid_t pid; |
531 | char *shell, *format, *pidstr, pidstrbuf[1 + 3 * sizeof pid]; | 505 | char *shell, *format, *pidstr, pidstrbuf[1 + 3 * sizeof pid]; |
532 | 506 | ||
533 | /* check if RSA support exists */ | 507 | /* check if RSA support exists */ |
534 | if (rsa_alive() == 0) { | 508 | if (rsa_alive() == 0) { |
535 | fprintf(stderr, | 509 | fprintf(stderr, |
536 | "%s: no RSA support in libssl and libcrypto. See ssl(8).\n", | 510 | "%s: no RSA support in libssl and libcrypto. See ssl(8).\n", |
537 | __progname); | 511 | __progname); |
538 | exit(1); | 512 | exit(1); |
539 | } | 513 | } |
540 | |||
541 | #if defined(__GNU_LIBRARY__) | 514 | #if defined(__GNU_LIBRARY__) |
542 | while ((ch = getopt(ac, av, "+cks")) != -1) | 515 | while ((ch = getopt(ac, av, "+cks")) != -1) { |
543 | #else | 516 | #else |
544 | while ((ch = getopt(ac, av, "cks")) != -1) | 517 | while ((ch = getopt(ac, av, "cks")) != -1) { |
545 | #endif /* defined(__GNU_LIBRARY__) */ | 518 | #endif /* defined(__GNU_LIBRARY__) */ |
546 | { | 519 | switch (ch) { |
547 | switch (ch) | 520 | case 'c': |
548 | { | 521 | if (s_flag) |
549 | case 'c': | 522 | usage(); |
550 | if (s_flag) | 523 | c_flag++; |
551 | usage(); | 524 | break; |
552 | c_flag++; | 525 | case 'k': |
553 | break; | 526 | k_flag++; |
554 | case 'k': | 527 | break; |
555 | k_flag++; | 528 | case 's': |
556 | break; | 529 | if (c_flag) |
557 | case 's': | 530 | usage(); |
558 | if (c_flag) | 531 | s_flag++; |
559 | usage(); | 532 | break; |
560 | s_flag++; | 533 | default: |
561 | break; | 534 | usage(); |
562 | default: | 535 | } |
563 | usage(); | ||
564 | } | 536 | } |
565 | } | 537 | ac -= optind; |
566 | ac -= optind; | 538 | av += optind; |
567 | av += optind; | 539 | |
568 | 540 | if (ac > 0 && (c_flag || k_flag || s_flag)) | |
569 | if (ac > 0 && (c_flag || k_flag || s_flag)) | 541 | usage(); |
570 | usage(); | 542 | |
571 | 543 | if (ac == 0 && !c_flag && !k_flag && !s_flag) { | |
572 | if (ac == 0 && !c_flag && !k_flag && !s_flag) | 544 | shell = getenv("SHELL"); |
573 | { | 545 | if (shell != NULL && strncmp(shell + strlen(shell) - 3, "csh", 3) == 0) |
574 | shell = getenv("SHELL"); | 546 | c_flag = 1; |
575 | if (shell != NULL && strncmp(shell + strlen(shell) - 3, "csh", 3) == 0) | ||
576 | c_flag = 1; | ||
577 | } | ||
578 | |||
579 | if (k_flag) | ||
580 | { | ||
581 | pidstr = getenv(SSH_AGENTPID_ENV_NAME); | ||
582 | if (pidstr == NULL) | ||
583 | { | ||
584 | fprintf(stderr, "%s not set, cannot kill agent\n", | ||
585 | SSH_AGENTPID_ENV_NAME); | ||
586 | exit(1); | ||
587 | } | 547 | } |
588 | pid = atoi(pidstr); | 548 | if (k_flag) { |
589 | if (pid < 1) /* XXX PID_MAX check too */ | 549 | pidstr = getenv(SSH_AGENTPID_ENV_NAME); |
590 | { | 550 | if (pidstr == NULL) { |
591 | fprintf(stderr, "%s=\"%s\", which is not a good PID\n", | 551 | fprintf(stderr, "%s not set, cannot kill agent\n", |
592 | SSH_AGENTPID_ENV_NAME, pidstr); | 552 | SSH_AGENTPID_ENV_NAME); |
593 | exit(1); | 553 | exit(1); |
554 | } | ||
555 | pid = atoi(pidstr); | ||
556 | if (pid < 1) { /* XXX PID_MAX check too */ | ||
557 | fprintf(stderr, "%s=\"%s\", which is not a good PID\n", | ||
558 | SSH_AGENTPID_ENV_NAME, pidstr); | ||
559 | exit(1); | ||
560 | } | ||
561 | if (kill(pid, SIGTERM) == -1) { | ||
562 | perror("kill"); | ||
563 | exit(1); | ||
564 | } | ||
565 | format = c_flag ? "unsetenv %s;\n" : "unset %s;\n"; | ||
566 | printf(format, SSH_AUTHSOCKET_ENV_NAME); | ||
567 | printf(format, SSH_AGENTPID_ENV_NAME); | ||
568 | printf("echo Agent pid %d killed;\n", pid); | ||
569 | exit(0); | ||
594 | } | 570 | } |
595 | if (kill(pid, SIGTERM) == -1) | 571 | parent_pid = getpid(); |
596 | { | 572 | |
597 | perror("kill"); | 573 | /* Create private directory for agent socket */ |
598 | exit(1); | 574 | strlcpy(socket_dir, "/tmp/ssh-XXXXXXXX", sizeof socket_dir); |
575 | if (mkdtemp(socket_dir) == NULL) { | ||
576 | perror("mkdtemp: private socket dir"); | ||
577 | exit(1); | ||
578 | } | ||
579 | snprintf(socket_name, sizeof socket_name, "%s/agent.%d", socket_dir, | ||
580 | parent_pid); | ||
581 | |||
582 | /* Create socket early so it will exist before command gets run | ||
583 | from the parent. */ | ||
584 | sock = socket(AF_UNIX, SOCK_STREAM, 0); | ||
585 | if (sock < 0) { | ||
586 | perror("socket"); | ||
587 | cleanup_exit(1); | ||
588 | } | ||
589 | memset(&sunaddr, 0, sizeof(sunaddr)); | ||
590 | sunaddr.sun_family = AF_UNIX; | ||
591 | strlcpy(sunaddr.sun_path, socket_name, sizeof(sunaddr.sun_path)); | ||
592 | if (bind(sock, (struct sockaddr *) & sunaddr, sizeof(sunaddr)) < 0) { | ||
593 | perror("bind"); | ||
594 | cleanup_exit(1); | ||
595 | } | ||
596 | if (listen(sock, 5) < 0) { | ||
597 | perror("listen"); | ||
598 | cleanup_exit(1); | ||
599 | } | 599 | } |
600 | format = c_flag ? "unsetenv %s;\n" : "unset %s;\n"; | 600 | /* Fork, and have the parent execute the command, if any, or |
601 | printf(format, SSH_AUTHSOCKET_ENV_NAME); | 601 | present the socket data. The child continues as the |
602 | printf(format, SSH_AGENTPID_ENV_NAME); | 602 | authentication agent. */ |
603 | printf("echo Agent pid %d killed;\n", pid); | 603 | pid = fork(); |
604 | exit(0); | 604 | if (pid == -1) { |
605 | } | 605 | perror("fork"); |
606 | 606 | exit(1); | |
607 | parent_pid = getpid(); | ||
608 | |||
609 | /* Create private directory for agent socket */ | ||
610 | strlcpy(socket_dir, "/tmp/ssh-XXXXXXXX", sizeof socket_dir); | ||
611 | if (mkdtemp(socket_dir) == NULL) { | ||
612 | perror("mkdtemp: private socket dir"); | ||
613 | exit(1); | ||
614 | } | ||
615 | snprintf(socket_name, sizeof socket_name, "%s/agent.%d", socket_dir, | ||
616 | parent_pid); | ||
617 | |||
618 | /* Create socket early so it will exist before command gets run from | ||
619 | the parent. */ | ||
620 | sock = socket(AF_UNIX, SOCK_STREAM, 0); | ||
621 | if (sock < 0) | ||
622 | { | ||
623 | perror("socket"); | ||
624 | cleanup_exit(1); | ||
625 | } | ||
626 | memset(&sunaddr, 0, sizeof(sunaddr)); | ||
627 | sunaddr.sun_family = AF_UNIX; | ||
628 | strlcpy(sunaddr.sun_path, socket_name, sizeof(sunaddr.sun_path)); | ||
629 | if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0) | ||
630 | { | ||
631 | perror("bind"); | ||
632 | cleanup_exit(1); | ||
633 | } | ||
634 | if (listen(sock, 5) < 0) | ||
635 | { | ||
636 | perror("listen"); | ||
637 | cleanup_exit(1); | ||
638 | } | ||
639 | |||
640 | /* Fork, and have the parent execute the command, if any, or present the | ||
641 | socket data. The child continues as the authentication agent. */ | ||
642 | pid = fork(); | ||
643 | if (pid == -1) | ||
644 | { | ||
645 | perror("fork"); | ||
646 | exit(1); | ||
647 | } | ||
648 | if (pid != 0) | ||
649 | { /* Parent - execute the given command. */ | ||
650 | close(sock); | ||
651 | snprintf(pidstrbuf, sizeof pidstrbuf, "%d", pid); | ||
652 | if (ac == 0) | ||
653 | { | ||
654 | format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n"; | ||
655 | printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name, | ||
656 | SSH_AUTHSOCKET_ENV_NAME); | ||
657 | printf(format, SSH_AGENTPID_ENV_NAME, pidstrbuf, | ||
658 | SSH_AGENTPID_ENV_NAME); | ||
659 | printf("echo Agent pid %d;\n", pid); | ||
660 | exit(0); | ||
661 | } | 607 | } |
608 | if (pid != 0) { /* Parent - execute the given command. */ | ||
609 | close(sock); | ||
610 | snprintf(pidstrbuf, sizeof pidstrbuf, "%d", pid); | ||
611 | if (ac == 0) { | ||
612 | format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n"; | ||
613 | printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name, | ||
614 | SSH_AUTHSOCKET_ENV_NAME); | ||
615 | printf(format, SSH_AGENTPID_ENV_NAME, pidstrbuf, | ||
616 | SSH_AGENTPID_ENV_NAME); | ||
617 | printf("echo Agent pid %d;\n", pid); | ||
618 | exit(0); | ||
619 | } | ||
620 | setenv(SSH_AUTHSOCKET_ENV_NAME, socket_name, 1); | ||
621 | setenv(SSH_AGENTPID_ENV_NAME, pidstrbuf, 1); | ||
622 | execvp(av[0], av); | ||
623 | perror(av[0]); | ||
624 | exit(1); | ||
625 | } | ||
626 | close(0); | ||
627 | close(1); | ||
628 | close(2); | ||
662 | 629 | ||
663 | setenv(SSH_AUTHSOCKET_ENV_NAME, socket_name, 1); | 630 | if (setsid() == -1) { |
664 | setenv(SSH_AGENTPID_ENV_NAME, pidstrbuf, 1); | 631 | perror("setsid"); |
665 | execvp(av[0], av); | 632 | cleanup_exit(1); |
666 | perror(av[0]); | 633 | } |
667 | exit(1); | 634 | if (atexit(cleanup_socket) < 0) { |
668 | } | 635 | perror("atexit"); |
669 | 636 | cleanup_exit(1); | |
670 | close(0); | 637 | } |
671 | close(1); | 638 | new_socket(AUTH_SOCKET, sock); |
672 | close(2); | 639 | if (ac > 0) { |
673 | 640 | signal(SIGALRM, check_parent_exists); | |
674 | if (setsid() == -1) | 641 | alarm(10); |
675 | { | 642 | } |
676 | perror("setsid"); | 643 | signal(SIGINT, SIG_IGN); |
677 | cleanup_exit(1); | 644 | signal(SIGPIPE, SIG_IGN); |
678 | } | 645 | while (1) { |
679 | 646 | FD_ZERO(&readset); | |
680 | if (atexit(cleanup_socket) < 0) | 647 | FD_ZERO(&writeset); |
681 | { | 648 | prepare_select(&readset, &writeset); |
682 | perror("atexit"); | 649 | if (select(max_fd + 1, &readset, &writeset, NULL, NULL) < 0) { |
683 | cleanup_exit(1); | 650 | if (errno == EINTR) |
684 | } | 651 | continue; |
685 | 652 | exit(1); | |
686 | new_socket(AUTH_SOCKET, sock); | 653 | } |
687 | if (ac > 0) | 654 | after_select(&readset, &writeset); |
688 | { | ||
689 | signal(SIGALRM, check_parent_exists); | ||
690 | alarm(10); | ||
691 | } | ||
692 | |||
693 | signal(SIGINT, SIG_IGN); | ||
694 | signal(SIGPIPE, SIG_IGN); | ||
695 | while (1) | ||
696 | { | ||
697 | FD_ZERO(&readset); | ||
698 | FD_ZERO(&writeset); | ||
699 | prepare_select(&readset, &writeset); | ||
700 | if (select(max_fd + 1, &readset, &writeset, NULL, NULL) < 0) | ||
701 | { | ||
702 | if (errno == EINTR) | ||
703 | continue; | ||
704 | exit(1); | ||
705 | } | 655 | } |
706 | after_select(&readset, &writeset); | 656 | /* NOTREACHED */ |
707 | } | ||
708 | /*NOTREACHED*/ | ||
709 | } | 657 | } |
diff --git a/ssh-keygen.c b/ssh-keygen.c index 47e1cca07..6f2d426b8 100644 --- a/ssh-keygen.c +++ b/ssh-keygen.c | |||
@@ -1,32 +1,19 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | |
3 | ssh-keygen.c | 3 | * Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
4 | 4 | * All rights reserved | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Created: Mon Mar 27 02:26:40 1995 ylo |
6 | 6 | * Identity and host key generation and maintenance. | |
7 | Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | */ |
8 | All rights reserved | ||
9 | |||
10 | Created: Mon Mar 27 02:26:40 1995 ylo | ||
11 | |||
12 | Identity and host key generation and maintenance. | ||
13 | |||
14 | */ | ||
15 | 8 | ||
16 | #include "includes.h" | 9 | #include "includes.h" |
17 | RCSID("$Id: ssh-keygen.c,v 1.8 1999/11/22 02:22:29 damien Exp $"); | 10 | RCSID("$Id: ssh-keygen.c,v 1.9 1999/11/24 13:26:23 damien Exp $"); |
18 | 11 | ||
19 | #include "rsa.h" | 12 | #include "rsa.h" |
20 | #include "ssh.h" | 13 | #include "ssh.h" |
21 | #include "xmalloc.h" | 14 | #include "xmalloc.h" |
22 | #include "fingerprint.h" | 15 | #include "fingerprint.h" |
23 | 16 | ||
24 | #ifdef HAVE___PROGNAME | ||
25 | extern char *__progname; | ||
26 | #else /* HAVE___PROGNAME */ | ||
27 | const char *__progname = "ssh-keygen"; | ||
28 | #endif /* HAVE___PROGNAME */ | ||
29 | |||
30 | /* Generated private key. */ | 17 | /* Generated private key. */ |
31 | RSA *private_key; | 18 | RSA *private_key; |
32 | 19 | ||
@@ -64,533 +51,493 @@ char *identity_new_passphrase = NULL; | |||
64 | char *identity_comment = NULL; | 51 | char *identity_comment = NULL; |
65 | 52 | ||
66 | /* argv0 */ | 53 | /* argv0 */ |
54 | #ifdef HAVE___PROGNAME | ||
67 | extern char *__progname; | 55 | extern char *__progname; |
56 | #else /* HAVE___PROGNAME */ | ||
57 | const char *__progname = "ssh-keygen"; | ||
58 | #endif /* HAVE___PROGNAME */ | ||
68 | 59 | ||
69 | void | 60 | void |
70 | ask_filename(struct passwd *pw, const char *prompt) | 61 | ask_filename(struct passwd *pw, const char *prompt) |
71 | { | 62 | { |
72 | char buf[1024]; | 63 | char buf[1024]; |
73 | snprintf(identity_file, sizeof(identity_file), "%s/%s", | 64 | snprintf(identity_file, sizeof(identity_file), "%s/%s", |
74 | pw->pw_dir, SSH_CLIENT_IDENTITY); | 65 | pw->pw_dir, SSH_CLIENT_IDENTITY); |
75 | printf("%s (%s): ", prompt, identity_file); | 66 | printf("%s (%s): ", prompt, identity_file); |
76 | fflush(stdout); | 67 | fflush(stdout); |
77 | if (fgets(buf, sizeof(buf), stdin) == NULL) | 68 | if (fgets(buf, sizeof(buf), stdin) == NULL) |
78 | exit(1); | 69 | exit(1); |
79 | if (strchr(buf, '\n')) | 70 | if (strchr(buf, '\n')) |
80 | *strchr(buf, '\n') = 0; | 71 | *strchr(buf, '\n') = 0; |
81 | if (strcmp(buf, "") != 0) | 72 | if (strcmp(buf, "") != 0) |
82 | strlcpy(identity_file, buf, sizeof(identity_file)); | 73 | strlcpy(identity_file, buf, sizeof(identity_file)); |
83 | have_identity = 1; | 74 | have_identity = 1; |
84 | } | 75 | } |
85 | 76 | ||
86 | void | 77 | void |
87 | do_fingerprint(struct passwd *pw) | 78 | do_fingerprint(struct passwd *pw) |
88 | { | 79 | { |
89 | char *comment; | 80 | char *comment; |
90 | RSA *public_key; | 81 | RSA *public_key; |
91 | struct stat st; | 82 | struct stat st; |
92 | 83 | ||
93 | if (!have_identity) | 84 | if (!have_identity) |
94 | ask_filename(pw, "Enter file in which the key is"); | 85 | ask_filename(pw, "Enter file in which the key is"); |
95 | if (stat(identity_file, &st) < 0) | 86 | if (stat(identity_file, &st) < 0) { |
96 | { | 87 | perror(identity_file); |
97 | perror(identity_file); | 88 | exit(1); |
98 | exit(1); | 89 | } |
99 | } | 90 | public_key = RSA_new(); |
100 | public_key = RSA_new(); | 91 | if (!load_public_key(identity_file, public_key, &comment)) { |
101 | if (!load_public_key(identity_file, public_key, &comment)) { | 92 | char *cp, line[1024]; |
102 | char *cp, line[1024]; | 93 | BIGNUM *e, *n; |
103 | BIGNUM *e, *n; | 94 | int dummy, invalid = 0; |
104 | int dummy, invalid = 0; | 95 | FILE *f = fopen(identity_file, "r"); |
105 | FILE *f = fopen(identity_file, "r"); | 96 | n = BN_new(); |
106 | n = BN_new(); | 97 | e = BN_new(); |
107 | e = BN_new(); | 98 | if (f && fgets(line, sizeof(line), f)) { |
108 | if (f && fgets(line, sizeof(line), f)) { | 99 | cp = line; |
109 | cp = line; | 100 | line[strlen(line) - 1] = '\0'; |
110 | line[strlen(line)-1] = '\0'; | 101 | if (auth_rsa_read_key(&cp, &dummy, e, n)) { |
111 | if (auth_rsa_read_key(&cp, &dummy, e, n)) { | 102 | public_key->e = e; |
112 | public_key->e = e; | 103 | public_key->n = n; |
113 | public_key->n = n; | 104 | comment = xstrdup(cp ? cp : "no comment"); |
114 | comment = xstrdup(cp ? cp : "no comment"); | 105 | } else { |
115 | } else { | 106 | invalid = 1; |
116 | invalid = 1; | 107 | } |
117 | } | 108 | } else { |
118 | } else { | 109 | invalid = 1; |
119 | invalid = 1; | 110 | } |
120 | } | 111 | if (invalid) { |
121 | if (invalid) { | 112 | printf("%s is not a valid key file.\n", identity_file); |
122 | printf("%s is not a valid key file.\n", identity_file); | 113 | BN_free(e); |
123 | BN_free(e); | 114 | BN_free(n); |
124 | BN_free(n); | 115 | exit(1); |
125 | exit(1); | 116 | } |
126 | } | 117 | } |
127 | } | 118 | printf("%d %s %s\n", BN_num_bits(public_key->n), |
128 | 119 | fingerprint(public_key->e, public_key->n), | |
129 | printf("%d %s %s\n", BN_num_bits(public_key->n), | 120 | comment); |
130 | fingerprint(public_key->e, public_key->n), | 121 | RSA_free(public_key); |
131 | comment); | 122 | exit(0); |
132 | RSA_free(public_key); | ||
133 | exit(0); | ||
134 | } | 123 | } |
135 | 124 | ||
136 | /* Perform changing a passphrase. The argument is the passwd structure | 125 | /* |
137 | for the current user. */ | 126 | * Perform changing a passphrase. The argument is the passwd structure |
138 | 127 | * for the current user. | |
128 | */ | ||
139 | void | 129 | void |
140 | do_change_passphrase(struct passwd *pw) | 130 | do_change_passphrase(struct passwd *pw) |
141 | { | 131 | { |
142 | char *comment; | 132 | char *comment; |
143 | char *old_passphrase, *passphrase1, *passphrase2; | 133 | char *old_passphrase, *passphrase1, *passphrase2; |
144 | struct stat st; | 134 | struct stat st; |
145 | RSA *private_key; | 135 | RSA *private_key; |
146 | 136 | ||
147 | if (!have_identity) | 137 | if (!have_identity) |
148 | ask_filename(pw, "Enter file in which the key is"); | 138 | ask_filename(pw, "Enter file in which the key is"); |
149 | /* Check if the file exists. */ | 139 | /* Check if the file exists. */ |
150 | if (stat(identity_file, &st) < 0) | 140 | if (stat(identity_file, &st) < 0) { |
151 | { | 141 | perror(identity_file); |
152 | perror(identity_file); | 142 | exit(1); |
153 | exit(1); | 143 | } |
154 | } | 144 | /* Try to load the public key from the file the verify that it is |
155 | 145 | readable and of the proper format. */ | |
156 | /* Try to load the public key from the file the verify that it is | 146 | public_key = RSA_new(); |
157 | readable and of the proper format. */ | 147 | if (!load_public_key(identity_file, public_key, NULL)) { |
158 | public_key = RSA_new(); | 148 | printf("%s is not a valid key file.\n", identity_file); |
159 | if (!load_public_key(identity_file, public_key, NULL)) | 149 | exit(1); |
160 | { | 150 | } |
161 | printf("%s is not a valid key file.\n", identity_file); | 151 | /* Clear the public key since we are just about to load the whole file. */ |
162 | exit(1); | 152 | RSA_free(public_key); |
163 | } | 153 | |
164 | /* Clear the public key since we are just about to load the whole file. */ | 154 | /* Try to load the file with empty passphrase. */ |
165 | RSA_free(public_key); | 155 | private_key = RSA_new(); |
166 | 156 | if (!load_private_key(identity_file, "", private_key, &comment)) { | |
167 | /* Try to load the file with empty passphrase. */ | 157 | /* Read passphrase from the user. */ |
168 | private_key = RSA_new(); | 158 | if (identity_passphrase) |
169 | if (!load_private_key(identity_file, "", private_key, &comment)) { | 159 | old_passphrase = xstrdup(identity_passphrase); |
170 | /* Read passphrase from the user. */ | 160 | else |
171 | if (identity_passphrase) | 161 | old_passphrase = read_passphrase("Enter old passphrase: ", 1); |
172 | old_passphrase = xstrdup(identity_passphrase); | 162 | /* Try to load using the passphrase. */ |
173 | else | 163 | if (!load_private_key(identity_file, old_passphrase, private_key, &comment)) { |
174 | old_passphrase = read_passphrase("Enter old passphrase: ", 1); | 164 | memset(old_passphrase, 0, strlen(old_passphrase)); |
175 | /* Try to load using the passphrase. */ | 165 | xfree(old_passphrase); |
176 | if (!load_private_key(identity_file, old_passphrase, private_key, &comment)) | 166 | printf("Bad passphrase.\n"); |
177 | { | 167 | exit(1); |
178 | memset(old_passphrase, 0, strlen(old_passphrase)); | 168 | } |
179 | xfree(old_passphrase); | 169 | /* Destroy the passphrase. */ |
180 | printf("Bad passphrase.\n"); | 170 | memset(old_passphrase, 0, strlen(old_passphrase)); |
181 | exit(1); | 171 | xfree(old_passphrase); |
182 | } | 172 | } |
183 | /* Destroy the passphrase. */ | 173 | printf("Key has comment '%s'\n", comment); |
184 | memset(old_passphrase, 0, strlen(old_passphrase)); | 174 | |
185 | xfree(old_passphrase); | 175 | /* Ask the new passphrase (twice). */ |
186 | } | 176 | if (identity_new_passphrase) { |
187 | printf("Key has comment '%s'\n", comment); | 177 | passphrase1 = xstrdup(identity_new_passphrase); |
188 | 178 | passphrase2 = NULL; | |
189 | /* Ask the new passphrase (twice). */ | 179 | } else { |
190 | if (identity_new_passphrase) | 180 | passphrase1 = |
191 | { | 181 | read_passphrase("Enter new passphrase (empty for no passphrase): ", 1); |
192 | passphrase1 = xstrdup(identity_new_passphrase); | 182 | passphrase2 = read_passphrase("Enter same passphrase again: ", 1); |
193 | passphrase2 = NULL; | 183 | |
194 | } | 184 | /* Verify that they are the same. */ |
195 | else | 185 | if (strcmp(passphrase1, passphrase2) != 0) { |
196 | { | 186 | memset(passphrase1, 0, strlen(passphrase1)); |
197 | passphrase1 = | 187 | memset(passphrase2, 0, strlen(passphrase2)); |
198 | read_passphrase("Enter new passphrase (empty for no passphrase): ", 1); | 188 | xfree(passphrase1); |
199 | passphrase2 = read_passphrase("Enter same passphrase again: ", 1); | 189 | xfree(passphrase2); |
200 | 190 | printf("Pass phrases do not match. Try again.\n"); | |
201 | /* Verify that they are the same. */ | 191 | exit(1); |
202 | if (strcmp(passphrase1, passphrase2) != 0) | 192 | } |
203 | { | 193 | /* Destroy the other copy. */ |
204 | memset(passphrase1, 0, strlen(passphrase1)); | 194 | memset(passphrase2, 0, strlen(passphrase2)); |
205 | memset(passphrase2, 0, strlen(passphrase2)); | 195 | xfree(passphrase2); |
206 | xfree(passphrase1); | ||
207 | xfree(passphrase2); | ||
208 | printf("Pass phrases do not match. Try again.\n"); | ||
209 | exit(1); | ||
210 | } | 196 | } |
211 | /* Destroy the other copy. */ | ||
212 | memset(passphrase2, 0, strlen(passphrase2)); | ||
213 | xfree(passphrase2); | ||
214 | } | ||
215 | |||
216 | /* Save the file using the new passphrase. */ | ||
217 | if (!save_private_key(identity_file, passphrase1, private_key, comment)) | ||
218 | { | ||
219 | printf("Saving the key failed: %s: %s.\n", | ||
220 | identity_file, strerror(errno)); | ||
221 | memset(passphrase1, 0, strlen(passphrase1)); | ||
222 | xfree(passphrase1); | ||
223 | RSA_free(private_key); | ||
224 | xfree(comment); | ||
225 | exit(1); | ||
226 | } | ||
227 | /* Destroy the passphrase and the copy of the key in memory. */ | ||
228 | memset(passphrase1, 0, strlen(passphrase1)); | ||
229 | xfree(passphrase1); | ||
230 | RSA_free(private_key); /* Destroys contents */ | ||
231 | xfree(comment); | ||
232 | |||
233 | printf("Your identification has been saved with the new passphrase.\n"); | ||
234 | exit(0); | ||
235 | } | ||
236 | 197 | ||
237 | /* Change the comment of a private key file. */ | 198 | /* Save the file using the new passphrase. */ |
199 | if (!save_private_key(identity_file, passphrase1, private_key, comment)) { | ||
200 | printf("Saving the key failed: %s: %s.\n", | ||
201 | identity_file, strerror(errno)); | ||
202 | memset(passphrase1, 0, strlen(passphrase1)); | ||
203 | xfree(passphrase1); | ||
204 | RSA_free(private_key); | ||
205 | xfree(comment); | ||
206 | exit(1); | ||
207 | } | ||
208 | /* Destroy the passphrase and the copy of the key in memory. */ | ||
209 | memset(passphrase1, 0, strlen(passphrase1)); | ||
210 | xfree(passphrase1); | ||
211 | RSA_free(private_key); /* Destroys contents */ | ||
212 | xfree(comment); | ||
213 | |||
214 | printf("Your identification has been saved with the new passphrase.\n"); | ||
215 | exit(0); | ||
216 | } | ||
238 | 217 | ||
218 | /* | ||
219 | * Change the comment of a private key file. | ||
220 | */ | ||
239 | void | 221 | void |
240 | do_change_comment(struct passwd *pw) | 222 | do_change_comment(struct passwd *pw) |
241 | { | 223 | { |
242 | char new_comment[1024], *comment; | 224 | char new_comment[1024], *comment; |
243 | RSA *private_key; | 225 | RSA *private_key; |
244 | char *passphrase; | 226 | char *passphrase; |
245 | struct stat st; | 227 | struct stat st; |
246 | FILE *f; | 228 | FILE *f; |
247 | char *tmpbuf; | 229 | char *tmpbuf; |
248 | 230 | ||
249 | if (!have_identity) | 231 | if (!have_identity) |
250 | ask_filename(pw, "Enter file in which the key is"); | 232 | ask_filename(pw, "Enter file in which the key is"); |
251 | /* Check if the file exists. */ | 233 | /* Check if the file exists. */ |
252 | if (stat(identity_file, &st) < 0) | 234 | if (stat(identity_file, &st) < 0) { |
253 | { | 235 | perror(identity_file); |
254 | perror(identity_file); | 236 | exit(1); |
255 | exit(1); | 237 | } |
256 | } | 238 | /* Try to load the public key from the file the verify that it is |
257 | 239 | readable and of the proper format. */ | |
258 | /* Try to load the public key from the file the verify that it is | 240 | public_key = RSA_new(); |
259 | readable and of the proper format. */ | 241 | if (!load_public_key(identity_file, public_key, NULL)) { |
260 | public_key = RSA_new(); | 242 | printf("%s is not a valid key file.\n", identity_file); |
261 | if (!load_public_key(identity_file, public_key, NULL)) | 243 | exit(1); |
262 | { | ||
263 | printf("%s is not a valid key file.\n", identity_file); | ||
264 | exit(1); | ||
265 | } | ||
266 | |||
267 | private_key = RSA_new(); | ||
268 | /* Try to load the file with empty passphrase. */ | ||
269 | if (load_private_key(identity_file, "", private_key, &comment)) | ||
270 | passphrase = xstrdup(""); | ||
271 | else | ||
272 | { | ||
273 | /* Read passphrase from the user. */ | ||
274 | if (identity_passphrase) | ||
275 | passphrase = xstrdup(identity_passphrase); | ||
276 | else | ||
277 | if (identity_new_passphrase) | ||
278 | passphrase = xstrdup(identity_new_passphrase); | ||
279 | else | ||
280 | passphrase = read_passphrase("Enter passphrase: ", 1); | ||
281 | /* Try to load using the passphrase. */ | ||
282 | if (!load_private_key(identity_file, passphrase, private_key, &comment)) | ||
283 | { | ||
284 | memset(passphrase, 0, strlen(passphrase)); | ||
285 | xfree(passphrase); | ||
286 | printf("Bad passphrase.\n"); | ||
287 | exit(1); | ||
288 | } | 244 | } |
289 | } | 245 | private_key = RSA_new(); |
290 | printf("Key now has comment '%s'\n", comment); | 246 | /* Try to load the file with empty passphrase. */ |
291 | 247 | if (load_private_key(identity_file, "", private_key, &comment)) | |
292 | if (identity_comment) | 248 | passphrase = xstrdup(""); |
293 | { | 249 | else { |
294 | strlcpy(new_comment, identity_comment, sizeof(new_comment)); | 250 | /* Read passphrase from the user. */ |
295 | } | 251 | if (identity_passphrase) |
296 | else | 252 | passphrase = xstrdup(identity_passphrase); |
297 | { | 253 | else if (identity_new_passphrase) |
298 | printf("Enter new comment: "); | 254 | passphrase = xstrdup(identity_new_passphrase); |
299 | fflush(stdout); | 255 | else |
300 | if (!fgets(new_comment, sizeof(new_comment), stdin)) | 256 | passphrase = read_passphrase("Enter passphrase: ", 1); |
301 | { | 257 | /* Try to load using the passphrase. */ |
302 | memset(passphrase, 0, strlen(passphrase)); | 258 | if (!load_private_key(identity_file, passphrase, private_key, &comment)) { |
303 | RSA_free(private_key); | 259 | memset(passphrase, 0, strlen(passphrase)); |
304 | exit(1); | 260 | xfree(passphrase); |
261 | printf("Bad passphrase.\n"); | ||
262 | exit(1); | ||
263 | } | ||
264 | } | ||
265 | printf("Key now has comment '%s'\n", comment); | ||
266 | |||
267 | if (identity_comment) { | ||
268 | strlcpy(new_comment, identity_comment, sizeof(new_comment)); | ||
269 | } else { | ||
270 | printf("Enter new comment: "); | ||
271 | fflush(stdout); | ||
272 | if (!fgets(new_comment, sizeof(new_comment), stdin)) { | ||
273 | memset(passphrase, 0, strlen(passphrase)); | ||
274 | RSA_free(private_key); | ||
275 | exit(1); | ||
276 | } | ||
277 | /* Remove terminating newline from comment. */ | ||
278 | if (strchr(new_comment, '\n')) | ||
279 | *strchr(new_comment, '\n') = 0; | ||
280 | } | ||
281 | |||
282 | /* Save the file using the new passphrase. */ | ||
283 | if (!save_private_key(identity_file, passphrase, private_key, new_comment)) { | ||
284 | printf("Saving the key failed: %s: %s.\n", | ||
285 | identity_file, strerror(errno)); | ||
286 | memset(passphrase, 0, strlen(passphrase)); | ||
287 | xfree(passphrase); | ||
288 | RSA_free(private_key); | ||
289 | xfree(comment); | ||
290 | exit(1); | ||
305 | } | 291 | } |
306 | 292 | /* Destroy the passphrase and the private key in memory. */ | |
307 | /* Remove terminating newline from comment. */ | 293 | memset(passphrase, 0, strlen(passphrase)); |
308 | if (strchr(new_comment, '\n')) | 294 | xfree(passphrase); |
309 | *strchr(new_comment, '\n') = 0; | 295 | RSA_free(private_key); |
310 | } | 296 | |
311 | 297 | /* Save the public key in text format in a file with the same name | |
312 | /* Save the file using the new passphrase. */ | 298 | but .pub appended. */ |
313 | if (!save_private_key(identity_file, passphrase, private_key, new_comment)) | 299 | strlcat(identity_file, ".pub", sizeof(identity_file)); |
314 | { | 300 | f = fopen(identity_file, "w"); |
315 | printf("Saving the key failed: %s: %s.\n", | 301 | if (!f) { |
316 | identity_file, strerror(errno)); | 302 | printf("Could not save your public key in %s\n", identity_file); |
317 | memset(passphrase, 0, strlen(passphrase)); | 303 | exit(1); |
318 | xfree(passphrase); | 304 | } |
319 | RSA_free(private_key); | 305 | fprintf(f, "%d ", BN_num_bits(public_key->n)); |
320 | xfree(comment); | 306 | tmpbuf = BN_bn2dec(public_key->e); |
321 | exit(1); | 307 | fprintf(f, "%s ", tmpbuf); |
322 | } | 308 | free(tmpbuf); |
323 | 309 | tmpbuf = BN_bn2dec(public_key->n); | |
324 | /* Destroy the passphrase and the private key in memory. */ | 310 | fprintf(f, "%s %s\n", tmpbuf, new_comment); |
325 | memset(passphrase, 0, strlen(passphrase)); | 311 | free(tmpbuf); |
326 | xfree(passphrase); | 312 | fclose(f); |
327 | RSA_free(private_key); | 313 | |
328 | 314 | xfree(comment); | |
329 | /* Save the public key in text format in a file with the same name but | 315 | |
330 | .pub appended. */ | 316 | printf("The comment in your key file has been changed.\n"); |
331 | strlcat(identity_file, ".pub", sizeof(identity_file)); | 317 | exit(0); |
332 | f = fopen(identity_file, "w"); | ||
333 | if (!f) | ||
334 | { | ||
335 | printf("Could not save your public key in %s\n", identity_file); | ||
336 | exit(1); | ||
337 | } | ||
338 | fprintf(f, "%d ", BN_num_bits(public_key->n)); | ||
339 | tmpbuf = BN_bn2dec(public_key->e); | ||
340 | fprintf(f, "%s ", tmpbuf); | ||
341 | free (tmpbuf); | ||
342 | tmpbuf = BN_bn2dec(public_key->n); | ||
343 | fprintf(f, "%s %s\n", tmpbuf, new_comment); | ||
344 | free (tmpbuf); | ||
345 | fclose(f); | ||
346 | |||
347 | xfree(comment); | ||
348 | |||
349 | printf("The comment in your key file has been changed.\n"); | ||
350 | exit(0); | ||
351 | } | 318 | } |
352 | 319 | ||
353 | void | 320 | void |
354 | usage(void) | 321 | usage(void) |
355 | { | 322 | { |
356 | printf("ssh-keygen version %s\n", SSH_VERSION); | 323 | printf("ssh-keygen version %s\n", SSH_VERSION); |
357 | printf("Usage: %s [-b bits] [-p] [-c] [-f file] [-P pass] [-N new-pass] [-C comment]\n", __progname); | 324 | printf("Usage: %s [-b bits] [-p] [-c] [-f file] [-P pass] [-N new-pass] [-C comment]\n", __progname); |
358 | exit(1); | 325 | exit(1); |
359 | } | 326 | } |
360 | 327 | ||
361 | /* Main program for key management. */ | 328 | /* |
362 | 329 | * Main program for key management. | |
330 | */ | ||
363 | int | 331 | int |
364 | main(int ac, char **av) | 332 | main(int ac, char **av) |
365 | { | 333 | { |
366 | char dotsshdir[16*1024], comment[1024], *passphrase1, *passphrase2; | 334 | char dotsshdir[16 * 1024], comment[1024], *passphrase1, *passphrase2; |
367 | struct passwd *pw; | 335 | struct passwd *pw; |
368 | char *tmpbuf; | 336 | char *tmpbuf; |
369 | int opt; | 337 | int opt; |
370 | struct stat st; | 338 | struct stat st; |
371 | FILE *f; | 339 | FILE *f; |
372 | char hostname[MAXHOSTNAMELEN]; | 340 | char hostname[MAXHOSTNAMELEN]; |
373 | extern int optind; | 341 | extern int optind; |
374 | extern char *optarg; | 342 | extern char *optarg; |
375 | 343 | ||
376 | /* check if RSA support exists */ | 344 | /* check if RSA support exists */ |
377 | if (rsa_alive() == 0) { | 345 | if (rsa_alive() == 0) { |
378 | extern char *__progname; | 346 | extern char *__progname; |
379 | 347 | ||
380 | fprintf(stderr, | 348 | fprintf(stderr, |
381 | "%s: no RSA support in libssl and libcrypto. See ssl(8).\n", | 349 | "%s: no RSA support in libssl and libcrypto. See ssl(8).\n", |
382 | __progname); | 350 | __progname); |
383 | exit(1); | 351 | exit(1); |
384 | } | ||
385 | |||
386 | /* Get user\'s passwd structure. We need this for the home directory. */ | ||
387 | pw = getpwuid(getuid()); | ||
388 | if (!pw) | ||
389 | { | ||
390 | printf("You don't exist, go away!\n"); | ||
391 | exit(1); | ||
392 | } | ||
393 | |||
394 | /* Parse command line arguments. */ | ||
395 | while ((opt = getopt(ac, av, "qpclb:f:P:N:C:")) != EOF) | ||
396 | { | ||
397 | switch (opt) | ||
398 | { | ||
399 | case 'b': | ||
400 | bits = atoi(optarg); | ||
401 | if (bits < 512 || bits > 32768) | ||
402 | { | ||
403 | printf("Bits has bad value.\n"); | ||
404 | exit(1); | ||
405 | } | ||
406 | break; | ||
407 | |||
408 | case 'l': | ||
409 | print_fingerprint = 1; | ||
410 | break; | ||
411 | |||
412 | case 'p': | ||
413 | change_passphrase = 1; | ||
414 | break; | ||
415 | |||
416 | case 'c': | ||
417 | change_comment = 1; | ||
418 | break; | ||
419 | |||
420 | case 'f': | ||
421 | strlcpy(identity_file, optarg, sizeof(identity_file)); | ||
422 | have_identity = 1; | ||
423 | break; | ||
424 | |||
425 | case 'P': | ||
426 | identity_passphrase = optarg; | ||
427 | break; | ||
428 | |||
429 | case 'N': | ||
430 | identity_new_passphrase = optarg; | ||
431 | break; | ||
432 | |||
433 | case 'C': | ||
434 | identity_comment = optarg; | ||
435 | break; | ||
436 | |||
437 | case 'q': | ||
438 | quiet = 1; | ||
439 | break; | ||
440 | |||
441 | case '?': | ||
442 | default: | ||
443 | usage(); | ||
444 | } | 352 | } |
445 | } | 353 | /* Get user\'s passwd structure. We need this for the home |
446 | if (optind < ac) | 354 | directory. */ |
447 | { | 355 | pw = getpwuid(getuid()); |
448 | printf("Too many arguments.\n"); | 356 | if (!pw) { |
449 | usage(); | 357 | printf("You don't exist, go away!\n"); |
450 | } | 358 | exit(1); |
451 | if (change_passphrase && change_comment) | 359 | } |
452 | { | 360 | /* Parse command line arguments. */ |
453 | printf("Can only have one of -p and -c.\n"); | 361 | while ((opt = getopt(ac, av, "qpclb:f:P:N:C:")) != EOF) { |
454 | usage(); | 362 | switch (opt) { |
455 | } | 363 | case 'b': |
456 | 364 | bits = atoi(optarg); | |
457 | if (print_fingerprint) | 365 | if (bits < 512 || bits > 32768) { |
458 | do_fingerprint(pw); | 366 | printf("Bits has bad value.\n"); |
459 | 367 | exit(1); | |
460 | /* If the user requested to change the passphrase, do it now. This | 368 | } |
461 | function never returns. */ | 369 | break; |
462 | if (change_passphrase) | 370 | |
463 | do_change_passphrase(pw); | 371 | case 'l': |
464 | 372 | print_fingerprint = 1; | |
465 | /* If the user requested to change the comment, do it now. This function | 373 | break; |
466 | never returns. */ | 374 | |
467 | if (change_comment) | 375 | case 'p': |
468 | do_change_comment(pw); | 376 | change_passphrase = 1; |
469 | 377 | break; | |
470 | arc4random_stir(); | 378 | |
471 | 379 | case 'c': | |
472 | if (quiet) | 380 | change_comment = 1; |
473 | rsa_set_verbose(0); | 381 | break; |
474 | 382 | ||
475 | /* Generate the rsa key pair. */ | 383 | case 'f': |
476 | private_key = RSA_new(); | 384 | strlcpy(identity_file, optarg, sizeof(identity_file)); |
477 | public_key = RSA_new(); | 385 | have_identity = 1; |
478 | rsa_generate_key(private_key, public_key, bits); | 386 | break; |
479 | 387 | ||
480 | if (!have_identity) | 388 | case 'P': |
481 | ask_filename(pw, "Enter file in which to save the key"); | 389 | identity_passphrase = optarg; |
482 | 390 | break; | |
483 | /* Create ~/.ssh directory if it doesn\'t already exist. */ | 391 | |
484 | snprintf(dotsshdir, sizeof dotsshdir, "%s/%s", pw->pw_dir, SSH_USER_DIR); | 392 | case 'N': |
485 | if (strstr(identity_file, dotsshdir) != NULL && | 393 | identity_new_passphrase = optarg; |
486 | stat(dotsshdir, &st) < 0) { | 394 | break; |
487 | if (mkdir(dotsshdir, 0755) < 0) | 395 | |
488 | error("Could not create directory '%s'.", dotsshdir); | 396 | case 'C': |
489 | else if(!quiet) | 397 | identity_comment = optarg; |
490 | printf("Created directory '%s'.\n", dotsshdir); | 398 | break; |
491 | } | 399 | |
492 | 400 | case 'q': | |
493 | /* If the file already exists, ask the user to confirm. */ | 401 | quiet = 1; |
494 | if (stat(identity_file, &st) >= 0) | 402 | break; |
495 | { | 403 | |
496 | char yesno[3]; | 404 | case '?': |
497 | printf("%s already exists.\n", identity_file); | 405 | default: |
498 | printf("Overwrite (y/n)? "); | 406 | usage(); |
499 | fflush(stdout); | 407 | } |
500 | if (fgets(yesno, sizeof(yesno), stdin) == NULL) | 408 | } |
501 | exit(1); | 409 | if (optind < ac) { |
502 | if (yesno[0] != 'y' && yesno[0] != 'Y') | 410 | printf("Too many arguments.\n"); |
503 | exit(1); | 411 | usage(); |
504 | } | 412 | } |
505 | 413 | if (change_passphrase && change_comment) { | |
506 | /* Ask for a passphrase (twice). */ | 414 | printf("Can only have one of -p and -c.\n"); |
507 | if (identity_passphrase) | 415 | usage(); |
508 | passphrase1 = xstrdup(identity_passphrase); | 416 | } |
509 | else | 417 | if (print_fingerprint) |
510 | if (identity_new_passphrase) | 418 | do_fingerprint(pw); |
511 | passphrase1 = xstrdup(identity_new_passphrase); | 419 | |
512 | else | 420 | /* If the user requested to change the passphrase, do it now. |
513 | { | 421 | This function never returns. */ |
514 | passphrase_again: | 422 | if (change_passphrase) |
515 | passphrase1 = | 423 | do_change_passphrase(pw); |
516 | read_passphrase("Enter passphrase (empty for no passphrase): ", 1); | 424 | |
517 | passphrase2 = read_passphrase("Enter same passphrase again: ", 1); | 425 | /* If the user requested to change the comment, do it now. This |
518 | if (strcmp(passphrase1, passphrase2) != 0) | 426 | function never returns. */ |
519 | { | 427 | if (change_comment) |
520 | /* The passphrases do not match. Clear them and retry. */ | 428 | do_change_comment(pw); |
521 | memset(passphrase1, 0, strlen(passphrase1)); | 429 | |
522 | memset(passphrase2, 0, strlen(passphrase2)); | 430 | arc4random_stir(); |
523 | xfree(passphrase1); | 431 | |
524 | xfree(passphrase2); | 432 | if (quiet) |
525 | printf("Passphrases do not match. Try again.\n"); | 433 | rsa_set_verbose(0); |
526 | goto passphrase_again; | 434 | |
527 | } | 435 | /* Generate the rsa key pair. */ |
528 | /* Clear the other copy of the passphrase. */ | 436 | private_key = RSA_new(); |
529 | memset(passphrase2, 0, strlen(passphrase2)); | 437 | public_key = RSA_new(); |
530 | xfree(passphrase2); | 438 | rsa_generate_key(private_key, public_key, bits); |
531 | } | 439 | |
532 | 440 | if (!have_identity) | |
533 | /* Create default commend field for the passphrase. The user can later | 441 | ask_filename(pw, "Enter file in which to save the key"); |
534 | edit this field. */ | 442 | |
535 | if (identity_comment) | 443 | /* Create ~/.ssh directory if it doesn\'t already exist. */ |
536 | { | 444 | snprintf(dotsshdir, sizeof dotsshdir, "%s/%s", pw->pw_dir, SSH_USER_DIR); |
537 | strlcpy(comment, identity_comment, sizeof(comment)); | 445 | if (strstr(identity_file, dotsshdir) != NULL && |
538 | } | 446 | stat(dotsshdir, &st) < 0) { |
539 | else | 447 | if (mkdir(dotsshdir, 0755) < 0) |
540 | { | 448 | error("Could not create directory '%s'.", dotsshdir); |
541 | if (gethostname(hostname, sizeof(hostname)) < 0) | 449 | else if (!quiet) |
542 | { | 450 | printf("Created directory '%s'.\n", dotsshdir); |
543 | perror("gethostname"); | 451 | } |
544 | exit(1); | 452 | /* If the file already exists, ask the user to confirm. */ |
453 | if (stat(identity_file, &st) >= 0) { | ||
454 | char yesno[3]; | ||
455 | printf("%s already exists.\n", identity_file); | ||
456 | printf("Overwrite (y/n)? "); | ||
457 | fflush(stdout); | ||
458 | if (fgets(yesno, sizeof(yesno), stdin) == NULL) | ||
459 | exit(1); | ||
460 | if (yesno[0] != 'y' && yesno[0] != 'Y') | ||
461 | exit(1); | ||
462 | } | ||
463 | /* Ask for a passphrase (twice). */ | ||
464 | if (identity_passphrase) | ||
465 | passphrase1 = xstrdup(identity_passphrase); | ||
466 | else if (identity_new_passphrase) | ||
467 | passphrase1 = xstrdup(identity_new_passphrase); | ||
468 | else { | ||
469 | passphrase_again: | ||
470 | passphrase1 = | ||
471 | read_passphrase("Enter passphrase (empty for no passphrase): ", 1); | ||
472 | passphrase2 = read_passphrase("Enter same passphrase again: ", 1); | ||
473 | if (strcmp(passphrase1, passphrase2) != 0) { | ||
474 | /* The passphrases do not match. Clear them and retry. */ | ||
475 | memset(passphrase1, 0, strlen(passphrase1)); | ||
476 | memset(passphrase2, 0, strlen(passphrase2)); | ||
477 | xfree(passphrase1); | ||
478 | xfree(passphrase2); | ||
479 | printf("Passphrases do not match. Try again.\n"); | ||
480 | goto passphrase_again; | ||
481 | } | ||
482 | /* Clear the other copy of the passphrase. */ | ||
483 | memset(passphrase2, 0, strlen(passphrase2)); | ||
484 | xfree(passphrase2); | ||
485 | } | ||
486 | |||
487 | /* Create default commend field for the passphrase. The user can | ||
488 | later edit this field. */ | ||
489 | if (identity_comment) { | ||
490 | strlcpy(comment, identity_comment, sizeof(comment)); | ||
491 | } else { | ||
492 | if (gethostname(hostname, sizeof(hostname)) < 0) { | ||
493 | perror("gethostname"); | ||
494 | exit(1); | ||
495 | } | ||
496 | snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, hostname); | ||
497 | } | ||
498 | |||
499 | /* Save the key with the given passphrase and comment. */ | ||
500 | if (!save_private_key(identity_file, passphrase1, private_key, comment)) { | ||
501 | printf("Saving the key failed: %s: %s.\n", | ||
502 | identity_file, strerror(errno)); | ||
503 | memset(passphrase1, 0, strlen(passphrase1)); | ||
504 | xfree(passphrase1); | ||
505 | exit(1); | ||
506 | } | ||
507 | /* Clear the passphrase. */ | ||
508 | memset(passphrase1, 0, strlen(passphrase1)); | ||
509 | xfree(passphrase1); | ||
510 | |||
511 | /* Clear the private key and the random number generator. */ | ||
512 | RSA_free(private_key); | ||
513 | arc4random_stir(); | ||
514 | |||
515 | if (!quiet) | ||
516 | printf("Your identification has been saved in %s.\n", identity_file); | ||
517 | |||
518 | /* Save the public key in text format in a file with the same name | ||
519 | but .pub appended. */ | ||
520 | strlcat(identity_file, ".pub", sizeof(identity_file)); | ||
521 | f = fopen(identity_file, "w"); | ||
522 | if (!f) { | ||
523 | printf("Could not save your public key in %s\n", identity_file); | ||
524 | exit(1); | ||
525 | } | ||
526 | fprintf(f, "%d ", BN_num_bits(public_key->n)); | ||
527 | tmpbuf = BN_bn2dec(public_key->e); | ||
528 | fprintf(f, "%s ", tmpbuf); | ||
529 | free(tmpbuf); | ||
530 | tmpbuf = BN_bn2dec(public_key->n); | ||
531 | fprintf(f, "%s %s\n", tmpbuf, comment); | ||
532 | free(tmpbuf); | ||
533 | fclose(f); | ||
534 | |||
535 | if (!quiet) { | ||
536 | printf("Your public key has been saved in %s.\n", identity_file); | ||
537 | printf("The key fingerprint is:\n"); | ||
538 | printf("%d %s %s\n", BN_num_bits(public_key->n), | ||
539 | fingerprint(public_key->e, public_key->n), | ||
540 | comment); | ||
545 | } | 541 | } |
546 | snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, hostname); | 542 | exit(0); |
547 | } | ||
548 | |||
549 | /* Save the key with the given passphrase and comment. */ | ||
550 | if (!save_private_key(identity_file, passphrase1, private_key, comment)) | ||
551 | { | ||
552 | printf("Saving the key failed: %s: %s.\n", | ||
553 | identity_file, strerror(errno)); | ||
554 | memset(passphrase1, 0, strlen(passphrase1)); | ||
555 | xfree(passphrase1); | ||
556 | exit(1); | ||
557 | } | ||
558 | /* Clear the passphrase. */ | ||
559 | memset(passphrase1, 0, strlen(passphrase1)); | ||
560 | xfree(passphrase1); | ||
561 | |||
562 | /* Clear the private key and the random number generator. */ | ||
563 | RSA_free(private_key); | ||
564 | arc4random_stir(); | ||
565 | |||
566 | if (!quiet) | ||
567 | printf("Your identification has been saved in %s.\n", identity_file); | ||
568 | |||
569 | /* Save the public key in text format in a file with the same name but | ||
570 | .pub appended. */ | ||
571 | strlcat(identity_file, ".pub", sizeof(identity_file)); | ||
572 | f = fopen(identity_file, "w"); | ||
573 | if (!f) | ||
574 | { | ||
575 | printf("Could not save your public key in %s\n", identity_file); | ||
576 | exit(1); | ||
577 | } | ||
578 | fprintf(f, "%d ", BN_num_bits(public_key->n)); | ||
579 | tmpbuf = BN_bn2dec(public_key->e); | ||
580 | fprintf(f, "%s ", tmpbuf); | ||
581 | free(tmpbuf); | ||
582 | tmpbuf = BN_bn2dec(public_key->n); | ||
583 | fprintf(f, "%s %s\n", tmpbuf, comment); | ||
584 | free(tmpbuf); | ||
585 | fclose(f); | ||
586 | |||
587 | if (!quiet) { | ||
588 | printf("Your public key has been saved in %s.\n", identity_file); | ||
589 | printf("The key fingerprint is:\n"); | ||
590 | printf("%d %s %s\n", BN_num_bits(public_key->n), | ||
591 | fingerprint(public_key->e, public_key->n), | ||
592 | comment); | ||
593 | } | ||
594 | |||
595 | exit(0); | ||
596 | } | 543 | } |
@@ -9,7 +9,7 @@ | |||
9 | .\" | 9 | .\" |
10 | .\" Created: Sat Apr 22 21:55:14 1995 ylo | 10 | .\" Created: Sat Apr 22 21:55:14 1995 ylo |
11 | .\" | 11 | .\" |
12 | .\" $Id: ssh.1,v 1.8 1999/11/18 00:35:13 damien Exp $ | 12 | .\" $Id: ssh.1,v 1.9 1999/11/24 13:26:23 damien Exp $ |
13 | .\" | 13 | .\" |
14 | .Dd September 25, 1999 | 14 | .Dd September 25, 1999 |
15 | .Dt SSH 1 | 15 | .Dt SSH 1 |
@@ -662,6 +662,16 @@ or | |||
662 | RSA authentication will only be | 662 | RSA authentication will only be |
663 | attempted if the identity file exists, or an authentication agent is | 663 | attempted if the identity file exists, or an authentication agent is |
664 | running. | 664 | running. |
665 | .It Cm SkeyAuthentication | ||
666 | Specifies whether to use | ||
667 | .Xr skey 1 | ||
668 | authentication. The argument to | ||
669 | this keyword must be | ||
670 | .Dq yes | ||
671 | or | ||
672 | .Dq no . | ||
673 | The default is | ||
674 | .Dq no . | ||
665 | .It Cm CheckHostIP | 675 | .It Cm CheckHostIP |
666 | If this flag is set to | 676 | If this flag is set to |
667 | .Dq yes , | 677 | .Dq yes , |
@@ -1,24 +1,17 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | |
3 | ssh.c | 3 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
4 | 4 | * All rights reserved | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Created: Sat Mar 18 16:36:11 1995 ylo |
6 | 6 | * Ssh client program. This program can be used to log into a remote machine. | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * The software supports strong authentication, encryption, and forwarding |
8 | All rights reserved | 8 | * of X11, TCP/IP, and authentication connections. |
9 | 9 | * | |
10 | Created: Sat Mar 18 16:36:11 1995 ylo | 10 | * Modified to work with SSL by Niels Provos <provos@citi.umich.edu> in Canada. |
11 | 11 | */ | |
12 | Ssh client program. This program can be used to log into a remote machine. | ||
13 | The software supports strong authentication, encryption, and forwarding | ||
14 | of X11, TCP/IP, and authentication connections. | ||
15 | |||
16 | Modified to work with SSL by Niels Provos <provos@citi.umich.edu> in Canada. | ||
17 | |||
18 | */ | ||
19 | 12 | ||
20 | #include "includes.h" | 13 | #include "includes.h" |
21 | RCSID("$Id: ssh.c,v 1.10 1999/11/16 02:37:16 damien Exp $"); | 14 | RCSID("$Id: ssh.c,v 1.11 1999/11/24 13:26:23 damien Exp $"); |
22 | 15 | ||
23 | #include "xmalloc.h" | 16 | #include "xmalloc.h" |
24 | #include "ssh.h" | 17 | #include "ssh.h" |
@@ -34,13 +27,11 @@ extern char *__progname; | |||
34 | const char *__progname = "ssh"; | 27 | const char *__progname = "ssh"; |
35 | #endif /* HAVE___PROGNAME */ | 28 | #endif /* HAVE___PROGNAME */ |
36 | 29 | ||
37 | /* Flag indicating whether debug mode is on. This can be set on the | 30 | /* Flag indicating whether debug mode is on. This can be set on the command line. */ |
38 | command line. */ | ||
39 | int debug_flag = 0; | 31 | int debug_flag = 0; |
40 | 32 | ||
41 | /* Flag indicating whether to allocate a pseudo tty. This can be set on the | 33 | /* Flag indicating whether to allocate a pseudo tty. This can be set on the command |
42 | command line, and is automatically set if no command is given on the command | 34 | line, and is automatically set if no command is given on the command line. */ |
43 | line. */ | ||
44 | int tty_flag = 0; | 35 | int tty_flag = 0; |
45 | 36 | ||
46 | /* Flag indicating that nothing should be read from stdin. This can be set | 37 | /* Flag indicating that nothing should be read from stdin. This can be set |
@@ -87,730 +78,680 @@ uid_t original_real_uid; | |||
87 | void | 78 | void |
88 | usage() | 79 | usage() |
89 | { | 80 | { |
90 | fprintf(stderr, "Usage: %s [options] host [command]\n", av0); | 81 | fprintf(stderr, "Usage: %s [options] host [command]\n", av0); |
91 | fprintf(stderr, "Options:\n"); | 82 | fprintf(stderr, "Options:\n"); |
92 | fprintf(stderr, " -l user Log in using this user name.\n"); | 83 | fprintf(stderr, " -l user Log in using this user name.\n"); |
93 | fprintf(stderr, " -n Redirect input from /dev/null.\n"); | 84 | fprintf(stderr, " -n Redirect input from /dev/null.\n"); |
94 | fprintf(stderr, " -a Disable authentication agent forwarding.\n"); | 85 | fprintf(stderr, " -a Disable authentication agent forwarding.\n"); |
95 | #ifdef AFS | 86 | #ifdef AFS |
96 | fprintf(stderr, " -k Disable Kerberos ticket and AFS token forwarding.\n"); | 87 | fprintf(stderr, " -k Disable Kerberos ticket and AFS token forwarding.\n"); |
97 | #endif /* AFS */ | 88 | #endif /* AFS */ |
98 | fprintf(stderr, " -x Disable X11 connection forwarding.\n"); | 89 | fprintf(stderr, " -x Disable X11 connection forwarding.\n"); |
99 | fprintf(stderr, " -i file Identity for RSA authentication (default: ~/.ssh/identity).\n"); | 90 | fprintf(stderr, " -i file Identity for RSA authentication (default: ~/.ssh/identity).\n"); |
100 | fprintf(stderr, " -t Tty; allocate a tty even if command is given.\n"); | 91 | fprintf(stderr, " -t Tty; allocate a tty even if command is given.\n"); |
101 | fprintf(stderr, " -v Verbose; display verbose debugging messages.\n"); | 92 | fprintf(stderr, " -v Verbose; display verbose debugging messages.\n"); |
102 | fprintf(stderr, " -V Display version number only.\n"); | 93 | fprintf(stderr, " -V Display version number only.\n"); |
103 | fprintf(stderr, " -P Don't allocate a privileged port.\n"); | 94 | fprintf(stderr, " -P Don't allocate a privileged port.\n"); |
104 | fprintf(stderr, " -q Quiet; don't display any warning messages.\n"); | 95 | fprintf(stderr, " -q Quiet; don't display any warning messages.\n"); |
105 | fprintf(stderr, " -f Fork into background after authentication.\n"); | 96 | fprintf(stderr, " -f Fork into background after authentication.\n"); |
106 | fprintf(stderr, " -e char Set escape character; ``none'' = disable (default: ~).\n"); | 97 | fprintf(stderr, " -e char Set escape character; ``none'' = disable (default: ~).\n"); |
107 | 98 | ||
108 | fprintf(stderr, " -c cipher Select encryption algorithm: " | 99 | fprintf(stderr, " -c cipher Select encryption algorithm: " |
109 | "``3des'', " | 100 | "``3des'', " |
110 | "``blowfish''\n"); | 101 | "``blowfish''\n"); |
111 | fprintf(stderr, " -p port Connect to this port. Server must be on the same port.\n"); | 102 | fprintf(stderr, " -p port Connect to this port. Server must be on the same port.\n"); |
112 | fprintf(stderr, " -L listen-port:host:port Forward local port to remote address\n"); | 103 | fprintf(stderr, " -L listen-port:host:port Forward local port to remote address\n"); |
113 | fprintf(stderr, " -R listen-port:host:port Forward remote port to local address\n"); | 104 | fprintf(stderr, " -R listen-port:host:port Forward remote port to local address\n"); |
114 | fprintf(stderr, " These cause %s to listen for connections on a port, and\n", av0); | 105 | fprintf(stderr, " These cause %s to listen for connections on a port, and\n", av0); |
115 | fprintf(stderr, " forward them to the other side by connecting to host:port.\n"); | 106 | fprintf(stderr, " forward them to the other side by connecting to host:port.\n"); |
116 | fprintf(stderr, " -C Enable compression.\n"); | 107 | fprintf(stderr, " -C Enable compression.\n"); |
117 | fprintf(stderr, " -g Allow remote hosts to connect to forwarded ports.\n"); | 108 | fprintf(stderr, " -g Allow remote hosts to connect to forwarded ports.\n"); |
118 | fprintf(stderr, " -o 'option' Process the option as if it was read from a configuration file.\n"); | 109 | fprintf(stderr, " -o 'option' Process the option as if it was read from a configuration file.\n"); |
119 | exit(1); | 110 | exit(1); |
120 | } | 111 | } |
121 | 112 | ||
122 | /* Connects to the given host using rsh (or prints an error message and exits | 113 | /* |
123 | if rsh is not available). This function never returns. */ | 114 | * Connects to the given host using rsh (or prints an error message and exits |
124 | 115 | * if rsh is not available). This function never returns. | |
116 | */ | ||
125 | void | 117 | void |
126 | rsh_connect(char *host, char *user, Buffer *command) | 118 | rsh_connect(char *host, char *user, Buffer * command) |
127 | { | 119 | { |
128 | char *args[10]; | 120 | char *args[10]; |
129 | int i; | 121 | int i; |
130 | 122 | ||
131 | log("Using rsh. WARNING: Connection will not be encrypted."); | 123 | log("Using rsh. WARNING: Connection will not be encrypted."); |
132 | /* Build argument list for rsh. */ | 124 | /* Build argument list for rsh. */ |
133 | i = 0; | 125 | i = 0; |
134 | args[i++] = _PATH_RSH; | 126 | args[i++] = _PATH_RSH; |
135 | args[i++] = host; /* may have to come after user on some systems */ | 127 | /* host may have to come after user on some systems */ |
136 | if (user) | 128 | args[i++] = host; |
137 | { | 129 | if (user) { |
138 | args[i++] = "-l"; | 130 | args[i++] = "-l"; |
139 | args[i++] = user; | 131 | args[i++] = user; |
140 | } | 132 | } |
141 | if (buffer_len(command) > 0) | 133 | if (buffer_len(command) > 0) { |
142 | { | 134 | buffer_append(command, "\0", 1); |
143 | buffer_append(command, "\0", 1); | 135 | args[i++] = buffer_ptr(command); |
144 | args[i++] = buffer_ptr(command); | 136 | } |
145 | } | 137 | args[i++] = NULL; |
146 | args[i++] = NULL; | 138 | if (debug_flag) { |
147 | if (debug_flag) | 139 | for (i = 0; args[i]; i++) { |
148 | { | 140 | if (i != 0) |
149 | for (i = 0; args[i]; i++) | 141 | fprintf(stderr, " "); |
150 | { | 142 | fprintf(stderr, "%s", args[i]); |
151 | if (i != 0) | 143 | } |
152 | fprintf(stderr, " "); | 144 | fprintf(stderr, "\n"); |
153 | fprintf(stderr, "%s", args[i]); | ||
154 | } | 145 | } |
155 | fprintf(stderr, "\n"); | 146 | execv(_PATH_RSH, args); |
156 | } | 147 | perror(_PATH_RSH); |
157 | execv(_PATH_RSH, args); | 148 | exit(1); |
158 | perror(_PATH_RSH); | ||
159 | exit(1); | ||
160 | } | 149 | } |
161 | 150 | ||
162 | /* Main program for the ssh client. */ | 151 | /* |
163 | 152 | * Main program for the ssh client. | |
153 | */ | ||
164 | int | 154 | int |
165 | main(int ac, char **av) | 155 | main(int ac, char **av) |
166 | { | 156 | { |
167 | int i, opt, optind, type, exit_status, ok, fwd_port, fwd_host_port, authfd; | 157 | int i, opt, optind, type, exit_status, ok, fwd_port, fwd_host_port, |
168 | char *optarg, *cp, buf[256]; | 158 | authfd; |
169 | Buffer command; | 159 | char *optarg, *cp, buf[256]; |
170 | struct winsize ws; | 160 | Buffer command; |
171 | struct stat st; | 161 | struct winsize ws; |
172 | struct passwd *pw, pwcopy; | 162 | struct stat st; |
173 | int interactive = 0, dummy; | 163 | struct passwd *pw, pwcopy; |
174 | uid_t original_effective_uid; | 164 | int interactive = 0, dummy; |
175 | int plen; | 165 | uid_t original_effective_uid; |
176 | 166 | int plen; | |
177 | /* Save the original real uid. It will be needed later (uid-swapping may | 167 | |
178 | clobber the real uid). */ | 168 | /* Save the original real uid. It will be needed later |
179 | original_real_uid = getuid(); | 169 | (uid-swapping may clobber the real uid). */ |
180 | original_effective_uid = geteuid(); | 170 | original_real_uid = getuid(); |
181 | 171 | original_effective_uid = geteuid(); | |
182 | /* If we are installed setuid root be careful to not drop core. */ | 172 | |
183 | if (original_real_uid != original_effective_uid) | 173 | /* If we are installed setuid root be careful to not drop core. */ |
184 | { | 174 | if (original_real_uid != original_effective_uid) { |
185 | struct rlimit rlim; | 175 | struct rlimit rlim; |
186 | rlim.rlim_cur = rlim.rlim_max = 0; | 176 | rlim.rlim_cur = rlim.rlim_max = 0; |
187 | if (setrlimit(RLIMIT_CORE, &rlim) < 0) | 177 | if (setrlimit(RLIMIT_CORE, &rlim) < 0) |
188 | fatal("setrlimit failed: %.100s", strerror(errno)); | 178 | fatal("setrlimit failed: %.100s", strerror(errno)); |
189 | } | ||
190 | |||
191 | /* Use uid-swapping to give up root privileges for the duration of option | ||
192 | processing. We will re-instantiate the rights when we are ready to | ||
193 | create the privileged port, and will permanently drop them when the | ||
194 | port has been created (actually, when the connection has been made, as | ||
195 | we may need to create the port several times). */ | ||
196 | temporarily_use_uid(original_real_uid); | ||
197 | |||
198 | /* Set our umask to something reasonable, as some files are created with | ||
199 | the default umask. This will make them world-readable but writable | ||
200 | only by the owner, which is ok for all files for which we don't set | ||
201 | the modes explicitly. */ | ||
202 | umask(022); | ||
203 | |||
204 | /* Save our own name. */ | ||
205 | av0 = av[0]; | ||
206 | |||
207 | /* Initialize option structure to indicate that no values have been set. */ | ||
208 | initialize_options(&options); | ||
209 | |||
210 | /* Parse command-line arguments. */ | ||
211 | host = NULL; | ||
212 | |||
213 | /* If program name is not one of the standard names, use it as host name. */ | ||
214 | if (strchr(av0, '/')) | ||
215 | cp = strrchr(av0, '/') + 1; | ||
216 | else | ||
217 | cp = av0; | ||
218 | if (strcmp(cp, "rsh") != 0 && strcmp(cp, "ssh") != 0 && | ||
219 | strcmp(cp, "rlogin") != 0 && strcmp(cp, "slogin") != 0) | ||
220 | host = cp; | ||
221 | |||
222 | for (optind = 1; optind < ac; optind++) | ||
223 | { | ||
224 | if (av[optind][0] != '-') | ||
225 | { | ||
226 | if (host) | ||
227 | break; | ||
228 | if ((cp = strchr(av[optind], '@'))) { | ||
229 | options.user = av[optind]; | ||
230 | *cp = '\0'; | ||
231 | host = ++cp; | ||
232 | } | ||
233 | else | ||
234 | host = av[optind]; | ||
235 | continue; | ||
236 | } | 179 | } |
237 | opt = av[optind][1]; | 180 | /* Use uid-swapping to give up root privileges for the duration of |
238 | if (!opt) | 181 | option processing. We will re-instantiate the rights when we |
239 | usage(); | 182 | are ready to create the privileged port, and will permanently |
240 | if (strchr("eilcpLRo", opt)) /* options with arguments */ | 183 | drop them when the port has been created (actually, when the |
241 | { | 184 | connection has been made, as we may need to create the port |
242 | optarg = av[optind] + 2; | 185 | several times). */ |
243 | if (strcmp(optarg, "") == 0) | 186 | temporarily_use_uid(original_real_uid); |
244 | { | 187 | |
245 | if (optind >= ac - 1) | 188 | /* Set our umask to something reasonable, as some files are |
189 | created with the default umask. This will make them | ||
190 | world-readable but writable only by the owner, which is ok for | ||
191 | all files for which we don't set the modes explicitly. */ | ||
192 | umask(022); | ||
193 | |||
194 | /* Save our own name. */ | ||
195 | av0 = av[0]; | ||
196 | |||
197 | /* Initialize option structure to indicate that no values have been set. */ | ||
198 | initialize_options(&options); | ||
199 | |||
200 | /* Parse command-line arguments. */ | ||
201 | host = NULL; | ||
202 | |||
203 | /* If program name is not one of the standard names, use it as host name. */ | ||
204 | if (strchr(av0, '/')) | ||
205 | cp = strrchr(av0, '/') + 1; | ||
206 | else | ||
207 | cp = av0; | ||
208 | if (strcmp(cp, "rsh") != 0 && strcmp(cp, "ssh") != 0 && | ||
209 | strcmp(cp, "rlogin") != 0 && strcmp(cp, "slogin") != 0) | ||
210 | host = cp; | ||
211 | |||
212 | for (optind = 1; optind < ac; optind++) { | ||
213 | if (av[optind][0] != '-') { | ||
214 | if (host) | ||
215 | break; | ||
216 | if ((cp = strchr(av[optind], '@'))) { | ||
217 | options.user = av[optind]; | ||
218 | *cp = '\0'; | ||
219 | host = ++cp; | ||
220 | } else | ||
221 | host = av[optind]; | ||
222 | continue; | ||
223 | } | ||
224 | opt = av[optind][1]; | ||
225 | if (!opt) | ||
226 | usage(); | ||
227 | if (strchr("eilcpLRo", opt)) { /* options with arguments */ | ||
228 | optarg = av[optind] + 2; | ||
229 | if (strcmp(optarg, "") == 0) { | ||
230 | if (optind >= ac - 1) | ||
231 | usage(); | ||
232 | optarg = av[++optind]; | ||
233 | } | ||
234 | } else { | ||
235 | if (av[optind][2]) | ||
236 | usage(); | ||
237 | optarg = NULL; | ||
238 | } | ||
239 | switch (opt) { | ||
240 | case 'n': | ||
241 | stdin_null_flag = 1; | ||
242 | break; | ||
243 | |||
244 | case 'f': | ||
245 | fork_after_authentication_flag = 1; | ||
246 | stdin_null_flag = 1; | ||
247 | break; | ||
248 | |||
249 | case 'x': | ||
250 | options.forward_x11 = 0; | ||
251 | break; | ||
252 | |||
253 | case 'X': | ||
254 | options.forward_x11 = 1; | ||
255 | break; | ||
256 | |||
257 | case 'g': | ||
258 | options.gateway_ports = 1; | ||
259 | break; | ||
260 | |||
261 | case 'P': | ||
262 | options.use_privileged_port = 0; | ||
263 | break; | ||
264 | |||
265 | case 'a': | ||
266 | options.forward_agent = 0; | ||
267 | break; | ||
268 | #ifdef AFS | ||
269 | case 'k': | ||
270 | options.kerberos_tgt_passing = 0; | ||
271 | options.afs_token_passing = 0; | ||
272 | break; | ||
273 | #endif | ||
274 | case 'i': | ||
275 | if (stat(optarg, &st) < 0) { | ||
276 | fprintf(stderr, "Warning: Identity file %s does not exist.\n", | ||
277 | optarg); | ||
278 | break; | ||
279 | } | ||
280 | if (options.num_identity_files >= SSH_MAX_IDENTITY_FILES) | ||
281 | fatal("Too many identity files specified (max %d)", | ||
282 | SSH_MAX_IDENTITY_FILES); | ||
283 | options.identity_files[options.num_identity_files++] = | ||
284 | xstrdup(optarg); | ||
285 | break; | ||
286 | |||
287 | case 't': | ||
288 | tty_flag = 1; | ||
289 | break; | ||
290 | |||
291 | case 'v': | ||
292 | case 'V': | ||
293 | fprintf(stderr, "SSH Version %s, protocol version %d.%d.\n", | ||
294 | SSH_VERSION, PROTOCOL_MAJOR, PROTOCOL_MINOR); | ||
295 | fprintf(stderr, "Compiled with SSL.\n"); | ||
296 | if (opt == 'V') | ||
297 | exit(0); | ||
298 | debug_flag = 1; | ||
299 | options.log_level = SYSLOG_LEVEL_DEBUG; | ||
300 | break; | ||
301 | |||
302 | case 'q': | ||
303 | options.log_level = SYSLOG_LEVEL_QUIET; | ||
304 | break; | ||
305 | |||
306 | case 'e': | ||
307 | if (optarg[0] == '^' && optarg[2] == 0 && | ||
308 | (unsigned char) optarg[1] >= 64 && (unsigned char) optarg[1] < 128) | ||
309 | options.escape_char = (unsigned char) optarg[1] & 31; | ||
310 | else if (strlen(optarg) == 1) | ||
311 | options.escape_char = (unsigned char) optarg[0]; | ||
312 | else if (strcmp(optarg, "none") == 0) | ||
313 | options.escape_char = -2; | ||
314 | else { | ||
315 | fprintf(stderr, "Bad escape character '%s'.\n", optarg); | ||
316 | exit(1); | ||
317 | } | ||
318 | break; | ||
319 | |||
320 | case 'c': | ||
321 | options.cipher = cipher_number(optarg); | ||
322 | if (options.cipher == -1) { | ||
323 | fprintf(stderr, "Unknown cipher type '%s'\n", optarg); | ||
324 | exit(1); | ||
325 | } | ||
326 | break; | ||
327 | |||
328 | case 'p': | ||
329 | options.port = atoi(optarg); | ||
330 | if (options.port < 1 || options.port > 65535) { | ||
331 | fprintf(stderr, "Bad port %s.\n", optarg); | ||
332 | exit(1); | ||
333 | } | ||
334 | break; | ||
335 | |||
336 | case 'l': | ||
337 | options.user = optarg; | ||
338 | break; | ||
339 | |||
340 | case 'R': | ||
341 | if (sscanf(optarg, "%d:%255[^:]:%d", &fwd_port, buf, | ||
342 | &fwd_host_port) != 3) { | ||
343 | fprintf(stderr, "Bad forwarding specification '%s'.\n", optarg); | ||
344 | usage(); | ||
345 | /* NOTREACHED */ | ||
346 | } | ||
347 | add_remote_forward(&options, fwd_port, buf, fwd_host_port); | ||
348 | break; | ||
349 | |||
350 | case 'L': | ||
351 | if (sscanf(optarg, "%d:%255[^:]:%d", &fwd_port, buf, | ||
352 | &fwd_host_port) != 3) { | ||
353 | fprintf(stderr, "Bad forwarding specification '%s'.\n", optarg); | ||
354 | usage(); | ||
355 | /* NOTREACHED */ | ||
356 | } | ||
357 | add_local_forward(&options, fwd_port, buf, fwd_host_port); | ||
358 | break; | ||
359 | |||
360 | case 'C': | ||
361 | options.compression = 1; | ||
362 | break; | ||
363 | |||
364 | case 'o': | ||
365 | dummy = 1; | ||
366 | if (process_config_line(&options, host ? host : "", optarg, | ||
367 | "command-line", 0, &dummy) != 0) | ||
368 | exit(1); | ||
369 | break; | ||
370 | |||
371 | default: | ||
372 | usage(); | ||
373 | } | ||
374 | } | ||
375 | |||
376 | /* Check that we got a host name. */ | ||
377 | if (!host) | ||
246 | usage(); | 378 | usage(); |
247 | optarg = av[++optind]; | 379 | |
248 | } | 380 | /* check if RSA support exists */ |
381 | if (rsa_alive() == 0) { | ||
382 | fprintf(stderr, | ||
383 | "%s: no RSA support in libssl and libcrypto. See ssl(8).\n", | ||
384 | __progname); | ||
385 | exit(1); | ||
249 | } | 386 | } |
250 | else | 387 | /* Initialize the command to execute on remote host. */ |
251 | { | 388 | buffer_init(&command); |
252 | if (av[optind][2]) | 389 | |
253 | usage(); | 390 | /* Save the command to execute on the remote host in a buffer. |
254 | optarg = NULL; | 391 | There is no limit on the length of the command, except by the |
392 | maximum packet size. Also sets the tty flag if there is no | ||
393 | command. */ | ||
394 | if (optind == ac) { | ||
395 | /* No command specified - execute shell on a tty. */ | ||
396 | tty_flag = 1; | ||
397 | } else { | ||
398 | /* A command has been specified. Store it into the | ||
399 | buffer. */ | ||
400 | for (i = optind; i < ac; i++) { | ||
401 | if (i > optind) | ||
402 | buffer_append(&command, " ", 1); | ||
403 | buffer_append(&command, av[i], strlen(av[i])); | ||
404 | } | ||
255 | } | 405 | } |
256 | switch (opt) | 406 | |
257 | { | 407 | /* Cannot fork to background if no command. */ |
258 | case 'n': | 408 | if (fork_after_authentication_flag && buffer_len(&command) == 0) |
259 | stdin_null_flag = 1; | 409 | fatal("Cannot fork into background without a command to execute."); |
260 | break; | 410 | |
261 | 411 | /* Allocate a tty by default if no command specified. */ | |
262 | case 'f': | 412 | if (buffer_len(&command) == 0) |
263 | fork_after_authentication_flag = 1; | 413 | tty_flag = 1; |
264 | stdin_null_flag = 1; | 414 | |
265 | break; | 415 | /* Do not allocate a tty if stdin is not a tty. */ |
266 | 416 | if (!isatty(fileno(stdin))) { | |
267 | case 'x': | 417 | if (tty_flag) |
268 | options.forward_x11 = 0; | 418 | fprintf(stderr, "Pseudo-terminal will not be allocated because stdin is not a terminal.\n"); |
269 | break; | 419 | tty_flag = 0; |
270 | 420 | } | |
271 | case 'X': | 421 | /* Get user data. */ |
272 | options.forward_x11 = 1; | 422 | pw = getpwuid(original_real_uid); |
273 | break; | 423 | if (!pw) { |
274 | 424 | fprintf(stderr, "You don't exist, go away!\n"); | |
275 | case 'g': | 425 | exit(1); |
276 | options.gateway_ports = 1; | 426 | } |
277 | break; | 427 | /* Take a copy of the returned structure. */ |
278 | 428 | memset(&pwcopy, 0, sizeof(pwcopy)); | |
279 | case 'P': | 429 | pwcopy.pw_name = xstrdup(pw->pw_name); |
280 | options.use_privileged_port = 0; | 430 | pwcopy.pw_passwd = xstrdup(pw->pw_passwd); |
281 | break; | 431 | pwcopy.pw_uid = pw->pw_uid; |
282 | 432 | pwcopy.pw_gid = pw->pw_gid; | |
283 | case 'a': | 433 | pwcopy.pw_dir = xstrdup(pw->pw_dir); |
284 | options.forward_agent = 0; | 434 | pwcopy.pw_shell = xstrdup(pw->pw_shell); |
285 | break; | 435 | pw = &pwcopy; |
286 | #ifdef AFS | 436 | |
287 | case 'k': | 437 | /* Initialize "log" output. Since we are the client all output |
288 | options.kerberos_tgt_passing = 0; | 438 | actually goes to the terminal. */ |
289 | options.afs_token_passing = 0; | 439 | log_init(av[0], options.log_level, SYSLOG_FACILITY_USER, 0); |
290 | break; | 440 | |
291 | #endif | 441 | /* Read per-user configuration file. */ |
292 | case 'i': | 442 | snprintf(buf, sizeof buf, "%.100s/%.100s", pw->pw_dir, SSH_USER_CONFFILE); |
293 | if (stat(optarg, &st) < 0) | 443 | read_config_file(buf, host, &options); |
294 | { | 444 | |
295 | fprintf(stderr, "Warning: Identity file %s does not exist.\n", | 445 | /* Read systemwide configuration file. */ |
296 | optarg); | 446 | read_config_file(HOST_CONFIG_FILE, host, &options); |
297 | break; | 447 | |
298 | } | 448 | /* Fill configuration defaults. */ |
299 | if (options.num_identity_files >= SSH_MAX_IDENTITY_FILES) | 449 | fill_default_options(&options); |
300 | fatal("Too many identity files specified (max %d)", | 450 | |
301 | SSH_MAX_IDENTITY_FILES); | 451 | /* reinit */ |
302 | options.identity_files[options.num_identity_files++] = | 452 | log_init(av[0], options.log_level, SYSLOG_FACILITY_USER, 0); |
303 | xstrdup(optarg); | 453 | |
304 | break; | 454 | if (options.user == NULL) |
305 | 455 | options.user = xstrdup(pw->pw_name); | |
306 | case 't': | 456 | |
307 | tty_flag = 1; | 457 | if (options.hostname != NULL) |
308 | break; | 458 | host = options.hostname; |
309 | 459 | ||
310 | case 'v': | 460 | /* Find canonic host name. */ |
311 | case 'V': | 461 | if (strchr(host, '.') == 0) { |
312 | fprintf(stderr, "SSH Version %s, protocol version %d.%d.\n", | 462 | struct hostent *hp = gethostbyname(host); |
313 | SSH_VERSION, PROTOCOL_MAJOR, PROTOCOL_MINOR); | 463 | if (hp != 0) { |
314 | fprintf(stderr, "Compiled with SSL.\n"); | 464 | if (strchr(hp->h_name, '.') != 0) |
315 | if (opt == 'V') | 465 | host = xstrdup(hp->h_name); |
316 | exit(0); | 466 | else if (hp->h_aliases != 0 |
317 | debug_flag = 1; | 467 | && hp->h_aliases[0] != 0 |
318 | options.log_level = SYSLOG_LEVEL_DEBUG; | 468 | && strchr(hp->h_aliases[0], '.') != 0) |
319 | break; | 469 | host = xstrdup(hp->h_aliases[0]); |
320 | |||
321 | case 'q': | ||
322 | options.log_level = SYSLOG_LEVEL_QUIET; | ||
323 | break; | ||
324 | |||
325 | case 'e': | ||
326 | if (optarg[0] == '^' && optarg[2] == 0 && | ||
327 | (unsigned char)optarg[1] >= 64 && (unsigned char)optarg[1] < 128) | ||
328 | options.escape_char = (unsigned char)optarg[1] & 31; | ||
329 | else | ||
330 | if (strlen(optarg) == 1) | ||
331 | options.escape_char = (unsigned char)optarg[0]; | ||
332 | else | ||
333 | if (strcmp(optarg, "none") == 0) | ||
334 | options.escape_char = -2; | ||
335 | else | ||
336 | { | ||
337 | fprintf(stderr, "Bad escape character '%s'.\n", optarg); | ||
338 | exit(1); | ||
339 | } | 470 | } |
340 | break; | ||
341 | |||
342 | case 'c': | ||
343 | options.cipher = cipher_number(optarg); | ||
344 | if (options.cipher == -1) | ||
345 | { | ||
346 | fprintf(stderr, "Unknown cipher type '%s'\n", optarg); | ||
347 | exit(1); | ||
348 | } | ||
349 | break; | ||
350 | |||
351 | case 'p': | ||
352 | options.port = atoi(optarg); | ||
353 | if (options.port < 1 || options.port > 65535) | ||
354 | { | ||
355 | fprintf(stderr, "Bad port %s.\n", optarg); | ||
356 | exit(1); | ||
357 | } | ||
358 | break; | ||
359 | |||
360 | case 'l': | ||
361 | options.user = optarg; | ||
362 | break; | ||
363 | |||
364 | case 'R': | ||
365 | if (sscanf(optarg, "%d:%255[^:]:%d", &fwd_port, buf, | ||
366 | &fwd_host_port) != 3) | ||
367 | { | ||
368 | fprintf(stderr, "Bad forwarding specification '%s'.\n", optarg); | ||
369 | usage(); | ||
370 | /*NOTREACHED*/ | ||
371 | } | ||
372 | add_remote_forward(&options, fwd_port, buf, fwd_host_port); | ||
373 | break; | ||
374 | |||
375 | case 'L': | ||
376 | if (sscanf(optarg, "%d:%255[^:]:%d", &fwd_port, buf, | ||
377 | &fwd_host_port) != 3) | ||
378 | { | ||
379 | fprintf(stderr, "Bad forwarding specification '%s'.\n", optarg); | ||
380 | usage(); | ||
381 | /*NOTREACHED*/ | ||
382 | } | ||
383 | add_local_forward(&options, fwd_port, buf, fwd_host_port); | ||
384 | break; | ||
385 | |||
386 | case 'C': | ||
387 | options.compression = 1; | ||
388 | break; | ||
389 | |||
390 | case 'o': | ||
391 | dummy = 1; | ||
392 | if (process_config_line(&options, host ? host : "", optarg, | ||
393 | "command-line", 0, &dummy) != 0) | ||
394 | exit(1); | ||
395 | break; | ||
396 | |||
397 | default: | ||
398 | usage(); | ||
399 | } | 471 | } |
400 | } | 472 | /* Disable rhosts authentication if not running as root. */ |
401 | 473 | if (original_effective_uid != 0 || !options.use_privileged_port) { | |
402 | /* Check that we got a host name. */ | 474 | options.rhosts_authentication = 0; |
403 | if (!host) | 475 | options.rhosts_rsa_authentication = 0; |
404 | usage(); | 476 | } |
405 | 477 | /* If using rsh has been selected, exec it now (without trying | |
406 | /* check if RSA support exists */ | 478 | anything else). Note that we must release privileges first. */ |
407 | if (rsa_alive() == 0) { | 479 | if (options.use_rsh) { |
408 | 480 | /* Restore our superuser privileges. This must be done | |
409 | fprintf(stderr, | 481 | before permanently setting the uid. */ |
410 | "%s: no RSA support in libssl and libcrypto. See ssl(8).\n", | 482 | restore_uid(); |
411 | __progname); | 483 | |
412 | exit(1); | 484 | /* Switch to the original uid permanently. */ |
413 | } | 485 | permanently_set_uid(original_real_uid); |
414 | 486 | ||
415 | /* Initialize the command to execute on remote host. */ | 487 | /* Execute rsh. */ |
416 | buffer_init(&command); | 488 | rsh_connect(host, options.user, &command); |
417 | 489 | fatal("rsh_connect returned"); | |
418 | /* Save the command to execute on the remote host in a buffer. There is | ||
419 | no limit on the length of the command, except by the maximum packet | ||
420 | size. Also sets the tty flag if there is no command. */ | ||
421 | if (optind == ac) | ||
422 | { | ||
423 | /* No command specified - execute shell on a tty. */ | ||
424 | tty_flag = 1; | ||
425 | } | ||
426 | else | ||
427 | { | ||
428 | /* A command has been specified. Store it into the buffer. */ | ||
429 | for (i = optind; i < ac; i++) | ||
430 | { | ||
431 | if (i > optind) | ||
432 | buffer_append(&command, " ", 1); | ||
433 | buffer_append(&command, av[i], strlen(av[i])); | ||
434 | } | 490 | } |
435 | } | 491 | /* Restore our superuser privileges. */ |
436 | 492 | restore_uid(); | |
437 | /* Cannot fork to background if no command. */ | 493 | |
438 | if (fork_after_authentication_flag && buffer_len(&command) == 0) | 494 | /* Open a connection to the remote host. This needs root |
439 | fatal("Cannot fork into background without a command to execute."); | 495 | privileges if rhosts_{rsa_}authentication is enabled. */ |
440 | 496 | ||
441 | /* Allocate a tty by default if no command specified. */ | 497 | ok = ssh_connect(host, &hostaddr, options.port, |
442 | if (buffer_len(&command) == 0) | 498 | options.connection_attempts, |
443 | tty_flag = 1; | 499 | !options.rhosts_authentication && |
444 | 500 | !options.rhosts_rsa_authentication, | |
445 | /* Do not allocate a tty if stdin is not a tty. */ | 501 | original_real_uid, |
446 | if (!isatty(fileno(stdin))) | 502 | options.proxy_command); |
447 | { | 503 | |
448 | if (tty_flag) | 504 | /* If we successfully made the connection, load the host private |
449 | fprintf(stderr, "Pseudo-terminal will not be allocated because stdin is not a terminal.\n"); | 505 | key in case we will need it later for combined rsa-rhosts |
450 | tty_flag = 0; | 506 | authentication. This must be done before releasing extra |
451 | } | 507 | privileges, because the file is only readable by root. */ |
452 | 508 | if (ok) { | |
453 | /* Get user data. */ | 509 | host_private_key = RSA_new(); |
454 | pw = getpwuid(original_real_uid); | 510 | if (load_private_key(HOST_KEY_FILE, "", host_private_key, NULL)) |
455 | if (!pw) | 511 | host_private_key_loaded = 1; |
456 | { | 512 | } |
457 | fprintf(stderr, "You don't exist, go away!\n"); | 513 | /* Get rid of any extra privileges that we may have. We will no |
458 | exit(1); | 514 | longer need them. Also, extra privileges could make it very |
459 | } | 515 | hard to read identity files and other non-world-readable files |
460 | 516 | from the user's home directory if it happens to be on a NFS | |
461 | /* Take a copy of the returned structure. */ | 517 | volume where root is mapped to nobody. */ |
462 | memset(&pwcopy, 0, sizeof(pwcopy)); | 518 | |
463 | pwcopy.pw_name = xstrdup(pw->pw_name); | 519 | /* Note that some legacy systems need to postpone the following |
464 | pwcopy.pw_passwd = xstrdup(pw->pw_passwd); | 520 | call to permanently_set_uid() until the private hostkey is |
465 | pwcopy.pw_uid = pw->pw_uid; | 521 | destroyed with RSA_free(). Otherwise the calling user could |
466 | pwcopy.pw_gid = pw->pw_gid; | 522 | ptrace() the process, read the private hostkey and impersonate |
467 | pwcopy.pw_dir = xstrdup(pw->pw_dir); | 523 | the host. OpenBSD does not allow ptracing of setuid processes. */ |
468 | pwcopy.pw_shell = xstrdup(pw->pw_shell); | 524 | |
469 | pw = &pwcopy; | 525 | permanently_set_uid(original_real_uid); |
470 | 526 | ||
471 | /* Initialize "log" output. Since we are the client all output actually | 527 | /* Now that we are back to our own permissions, create ~/.ssh |
472 | goes to the terminal. */ | 528 | directory if it doesn\'t already exist. */ |
473 | log_init(av[0], options.log_level, SYSLOG_FACILITY_USER, 0); | 529 | snprintf(buf, sizeof buf, "%.100s/%.100s", pw->pw_dir, SSH_USER_DIR); |
474 | 530 | if (stat(buf, &st) < 0) | |
475 | /* Read per-user configuration file. */ | 531 | if (mkdir(buf, 0755) < 0) |
476 | snprintf(buf, sizeof buf, "%.100s/%.100s", pw->pw_dir, SSH_USER_CONFFILE); | 532 | error("Could not create directory '%.200s'.", buf); |
477 | read_config_file(buf, host, &options); | 533 | |
478 | 534 | /* Check if the connection failed, and try "rsh" if appropriate. */ | |
479 | /* Read systemwide configuration file. */ | 535 | if (!ok) { |
480 | read_config_file(HOST_CONFIG_FILE, host, &options); | 536 | if (options.port != 0) |
481 | 537 | log("Secure connection to %.100s on port %d refused%.100s.", | |
482 | /* Fill configuration defaults. */ | 538 | host, options.port, |
483 | fill_default_options(&options); | 539 | options.fallback_to_rsh ? "; reverting to insecure method" : ""); |
484 | 540 | else | |
485 | /* reinit */ | 541 | log("Secure connection to %.100s refused%.100s.", host, |
486 | log_init(av[0], options.log_level, SYSLOG_FACILITY_USER, 0); | 542 | options.fallback_to_rsh ? "; reverting to insecure method" : ""); |
487 | 543 | ||
488 | if (options.user == NULL) | 544 | if (options.fallback_to_rsh) { |
489 | options.user = xstrdup(pw->pw_name); | 545 | rsh_connect(host, options.user, &command); |
490 | 546 | fatal("rsh_connect returned"); | |
491 | if (options.hostname != NULL) | 547 | } |
492 | host = options.hostname; | 548 | exit(1); |
493 | |||
494 | /* Find canonic host name. */ | ||
495 | if (strchr(host, '.') == 0) | ||
496 | { | ||
497 | struct hostent *hp = gethostbyname(host); | ||
498 | if (hp != 0) | ||
499 | { | ||
500 | if (strchr(hp->h_name, '.') != 0) | ||
501 | host = xstrdup(hp->h_name); | ||
502 | else if (hp->h_aliases != 0 | ||
503 | && hp->h_aliases[0] != 0 | ||
504 | && strchr(hp->h_aliases[0], '.') != 0) | ||
505 | host = xstrdup(hp->h_aliases[0]); | ||
506 | } | 549 | } |
507 | } | 550 | /* Expand ~ in options.identity_files. */ |
508 | 551 | for (i = 0; i < options.num_identity_files; i++) | |
509 | /* Disable rhosts authentication if not running as root. */ | 552 | options.identity_files[i] = |
510 | if (original_effective_uid != 0 || !options.use_privileged_port) | 553 | tilde_expand_filename(options.identity_files[i], original_real_uid); |
511 | { | 554 | |
512 | options.rhosts_authentication = 0; | 555 | /* Expand ~ in known host file names. */ |
513 | options.rhosts_rsa_authentication = 0; | 556 | options.system_hostfile = tilde_expand_filename(options.system_hostfile, |
514 | } | 557 | original_real_uid); |
515 | 558 | options.user_hostfile = tilde_expand_filename(options.user_hostfile, | |
516 | /* If using rsh has been selected, exec it now (without trying anything | 559 | original_real_uid); |
517 | else). Note that we must release privileges first. */ | 560 | |
518 | if (options.use_rsh) | 561 | /* Log into the remote system. This never returns if the login fails. */ |
519 | { | 562 | ssh_login(host_private_key_loaded, host_private_key, |
520 | /* Restore our superuser privileges. This must be done before | 563 | host, &hostaddr, original_real_uid); |
521 | permanently setting the uid. */ | 564 | |
522 | restore_uid(); | 565 | /* We no longer need the host private key. Clear it now. */ |
523 | 566 | if (host_private_key_loaded) | |
524 | /* Switch to the original uid permanently. */ | 567 | RSA_free(host_private_key); /* Destroys contents safely */ |
525 | permanently_set_uid(original_real_uid); | 568 | |
526 | 569 | /* Close connection cleanly after attack. */ | |
527 | /* Execute rsh. */ | 570 | cipher_attack_detected = packet_disconnect; |
528 | rsh_connect(host, options.user, &command); | 571 | |
529 | fatal("rsh_connect returned"); | 572 | /* If requested, fork and let ssh continue in the background. */ |
530 | } | 573 | if (fork_after_authentication_flag) { |
531 | 574 | int ret = fork(); | |
532 | /* Restore our superuser privileges. */ | 575 | if (ret == -1) |
533 | restore_uid(); | 576 | fatal("fork failed: %.100s", strerror(errno)); |
534 | 577 | if (ret != 0) | |
535 | /* Open a connection to the remote host. This needs root privileges if | 578 | exit(0); |
536 | rhosts_{rsa_}authentication is enabled. */ | 579 | setsid(); |
537 | |||
538 | ok = ssh_connect(host, &hostaddr, options.port, options.connection_attempts, | ||
539 | !options.rhosts_authentication && | ||
540 | !options.rhosts_rsa_authentication, | ||
541 | original_real_uid, options.proxy_command); | ||
542 | |||
543 | /* If we successfully made the connection, load the host private key in | ||
544 | case we will need it later for combined rsa-rhosts authentication. | ||
545 | This must be done before releasing extra privileges, because the file | ||
546 | is only readable by root. */ | ||
547 | if (ok) | ||
548 | { | ||
549 | host_private_key = RSA_new(); | ||
550 | if (load_private_key(HOST_KEY_FILE, "", host_private_key, NULL)) | ||
551 | host_private_key_loaded = 1; | ||
552 | } | ||
553 | |||
554 | /* Get rid of any extra privileges that we may have. We will no longer need | ||
555 | them. Also, extra privileges could make it very hard to read identity | ||
556 | files and other non-world-readable files from the user's home directory | ||
557 | if it happens to be on a NFS volume where root is mapped to nobody. */ | ||
558 | |||
559 | /* Note that some legacy systems need to postpone the following call to | ||
560 | permanently_set_uid() until the private hostkey is destroyed with | ||
561 | RSA_free(). Otherwise the calling user could ptrace() the process, | ||
562 | read the private hostkey and impersonate the host. OpenBSD does not | ||
563 | allow ptracing of setuid processes. */ | ||
564 | |||
565 | permanently_set_uid(original_real_uid); | ||
566 | |||
567 | /* Now that we are back to our own permissions, create ~/.ssh directory | ||
568 | if it doesn\'t already exist. */ | ||
569 | snprintf(buf, sizeof buf, "%.100s/%.100s", pw->pw_dir, SSH_USER_DIR); | ||
570 | if (stat(buf, &st) < 0) | ||
571 | if (mkdir(buf, 0755) < 0) | ||
572 | error("Could not create directory '%.200s'.", buf); | ||
573 | |||
574 | /* Check if the connection failed, and try "rsh" if appropriate. */ | ||
575 | if (!ok) | ||
576 | { | ||
577 | if (options.port != 0) | ||
578 | log("Secure connection to %.100s on port %d refused%.100s.", | ||
579 | host, options.port, | ||
580 | options.fallback_to_rsh ? "; reverting to insecure method" : ""); | ||
581 | else | ||
582 | log("Secure connection to %.100s refused%.100s.", host, | ||
583 | options.fallback_to_rsh ? "; reverting to insecure method" : ""); | ||
584 | |||
585 | if (options.fallback_to_rsh) | ||
586 | { | ||
587 | rsh_connect(host, options.user, &command); | ||
588 | fatal("rsh_connect returned"); | ||
589 | } | 580 | } |
590 | exit(1); | 581 | /* Enable compression if requested. */ |
591 | } | 582 | if (options.compression) { |
592 | 583 | debug("Requesting compression at level %d.", options.compression_level); | |
593 | /* Expand ~ in options.identity_files. */ | 584 | |
594 | for (i = 0; i < options.num_identity_files; i++) | 585 | if (options.compression_level < 1 || options.compression_level > 9) |
595 | options.identity_files[i] = | 586 | fatal("Compression level must be from 1 (fast) to 9 (slow, best)."); |
596 | tilde_expand_filename(options.identity_files[i], original_real_uid); | 587 | |
597 | 588 | /* Send the request. */ | |
598 | /* Expand ~ in known host file names. */ | 589 | packet_start(SSH_CMSG_REQUEST_COMPRESSION); |
599 | options.system_hostfile = tilde_expand_filename(options.system_hostfile, | 590 | packet_put_int(options.compression_level); |
600 | original_real_uid); | 591 | packet_send(); |
601 | options.user_hostfile = tilde_expand_filename(options.user_hostfile, | 592 | packet_write_wait(); |
602 | original_real_uid); | 593 | type = packet_read(&plen); |
603 | 594 | if (type == SSH_SMSG_SUCCESS) | |
604 | /* Log into the remote system. This never returns if the login fails. */ | 595 | packet_start_compression(options.compression_level); |
605 | ssh_login(host_private_key_loaded, host_private_key, | 596 | else if (type == SSH_SMSG_FAILURE) |
606 | host, &hostaddr, original_real_uid); | 597 | log("Warning: Remote host refused compression."); |
607 | 598 | else | |
608 | /* We no longer need the host private key. Clear it now. */ | 599 | packet_disconnect("Protocol error waiting for compression response."); |
609 | if (host_private_key_loaded) | 600 | } |
610 | RSA_free(host_private_key); /* Destroys contents safely */ | 601 | /* Allocate a pseudo tty if appropriate. */ |
611 | 602 | if (tty_flag) { | |
612 | /* Close connection cleanly after attack. */ | 603 | debug("Requesting pty."); |
613 | cipher_attack_detected = packet_disconnect; | 604 | |
614 | 605 | /* Start the packet. */ | |
615 | /* Enable compression if requested. */ | 606 | packet_start(SSH_CMSG_REQUEST_PTY); |
616 | if (options.compression) | 607 | |
617 | { | 608 | /* Store TERM in the packet. There is no limit on the |
618 | debug("Requesting compression at level %d.", options.compression_level); | 609 | length of the string. */ |
619 | 610 | cp = getenv("TERM"); | |
620 | if (options.compression_level < 1 || options.compression_level > 9) | 611 | if (!cp) |
621 | fatal("Compression level must be from 1 (fast) to 9 (slow, best)."); | 612 | cp = ""; |
622 | 613 | packet_put_string(cp, strlen(cp)); | |
623 | /* Send the request. */ | 614 | |
624 | packet_start(SSH_CMSG_REQUEST_COMPRESSION); | 615 | /* Store window size in the packet. */ |
625 | packet_put_int(options.compression_level); | 616 | if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0) |
626 | packet_send(); | 617 | memset(&ws, 0, sizeof(ws)); |
627 | packet_write_wait(); | 618 | packet_put_int(ws.ws_row); |
628 | type = packet_read(&plen); | 619 | packet_put_int(ws.ws_col); |
629 | if (type == SSH_SMSG_SUCCESS) | 620 | packet_put_int(ws.ws_xpixel); |
630 | packet_start_compression(options.compression_level); | 621 | packet_put_int(ws.ws_ypixel); |
631 | else if (type == SSH_SMSG_FAILURE) | 622 | |
632 | log("Warning: Remote host refused compression."); | 623 | /* Store tty modes in the packet. */ |
633 | else | 624 | tty_make_modes(fileno(stdin)); |
634 | packet_disconnect("Protocol error waiting for compression response."); | 625 | |
635 | } | 626 | /* Send the packet, and wait for it to leave. */ |
636 | 627 | packet_send(); | |
637 | /* Allocate a pseudo tty if appropriate. */ | 628 | packet_write_wait(); |
638 | if (tty_flag) | 629 | |
639 | { | 630 | /* Read response from the server. */ |
640 | debug("Requesting pty."); | 631 | type = packet_read(&plen); |
641 | 632 | if (type == SSH_SMSG_SUCCESS) | |
642 | /* Start the packet. */ | 633 | interactive = 1; |
643 | packet_start(SSH_CMSG_REQUEST_PTY); | 634 | else if (type == SSH_SMSG_FAILURE) |
644 | 635 | log("Warning: Remote host failed or refused to allocate a pseudo tty."); | |
645 | /* Store TERM in the packet. There is no limit on the length of the | 636 | else |
646 | string. */ | 637 | packet_disconnect("Protocol error waiting for pty request response."); |
647 | cp = getenv("TERM"); | 638 | } |
648 | if (!cp) | 639 | /* Request X11 forwarding if enabled and DISPLAY is set. */ |
649 | cp = ""; | 640 | if (options.forward_x11 && getenv("DISPLAY") != NULL) { |
650 | packet_put_string(cp, strlen(cp)); | 641 | char line[512], proto[512], data[512]; |
651 | 642 | FILE *f; | |
652 | /* Store window size in the packet. */ | 643 | int forwarded = 0, got_data = 0, i; |
653 | if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0) | ||
654 | memset(&ws, 0, sizeof(ws)); | ||
655 | packet_put_int(ws.ws_row); | ||
656 | packet_put_int(ws.ws_col); | ||
657 | packet_put_int(ws.ws_xpixel); | ||
658 | packet_put_int(ws.ws_ypixel); | ||
659 | |||
660 | /* Store tty modes in the packet. */ | ||
661 | tty_make_modes(fileno(stdin)); | ||
662 | |||
663 | /* Send the packet, and wait for it to leave. */ | ||
664 | packet_send(); | ||
665 | packet_write_wait(); | ||
666 | |||
667 | /* Read response from the server. */ | ||
668 | type = packet_read(&plen); | ||
669 | if (type == SSH_SMSG_SUCCESS) | ||
670 | interactive = 1; | ||
671 | else if (type == SSH_SMSG_FAILURE) | ||
672 | log("Warning: Remote host failed or refused to allocate a pseudo tty."); | ||
673 | else | ||
674 | packet_disconnect("Protocol error waiting for pty request response."); | ||
675 | } | ||
676 | |||
677 | /* Request X11 forwarding if enabled and DISPLAY is set. */ | ||
678 | if (options.forward_x11 && getenv("DISPLAY") != NULL) | ||
679 | { | ||
680 | char line[512], proto[512], data[512]; | ||
681 | FILE *f; | ||
682 | int forwarded = 0, got_data = 0, i; | ||
683 | 644 | ||
684 | #ifdef XAUTH_PATH | 645 | #ifdef XAUTH_PATH |
685 | /* Try to get Xauthority information for the display. */ | 646 | /* Try to get Xauthority information for the display. */ |
686 | snprintf(line, sizeof line, "%.100s list %.200s 2>/dev/null", | 647 | snprintf(line, sizeof line, "%.100s list %.200s 2>/dev/null", |
687 | XAUTH_PATH, getenv("DISPLAY")); | 648 | XAUTH_PATH, getenv("DISPLAY")); |
688 | f = popen(line, "r"); | 649 | f = popen(line, "r"); |
689 | if (f && fgets(line, sizeof(line), f) && | 650 | if (f && fgets(line, sizeof(line), f) && |
690 | sscanf(line, "%*s %s %s", proto, data) == 2) | 651 | sscanf(line, "%*s %s %s", proto, data) == 2) |
691 | got_data = 1; | 652 | got_data = 1; |
692 | if (f) | 653 | if (f) |
693 | pclose(f); | 654 | pclose(f); |
694 | #endif /* XAUTH_PATH */ | 655 | #endif /* XAUTH_PATH */ |
695 | /* If we didn't get authentication data, just make up some data. The | 656 | /* If we didn't get authentication data, just make up some |
696 | forwarding code will check the validity of the response anyway, and | 657 | data. The forwarding code will check the validity of |
697 | substitute this data. The X11 server, however, will ignore this | 658 | the response anyway, and substitute this data. The X11 |
698 | fake data and use whatever authentication mechanisms it was using | 659 | server, however, will ignore this fake data and use |
699 | otherwise for the local connection. */ | 660 | whatever authentication mechanisms it was using |
700 | if (!got_data) | 661 | otherwise for the local connection. */ |
701 | { | 662 | if (!got_data) { |
702 | u_int32_t rand = 0; | 663 | u_int32_t rand = 0; |
703 | 664 | ||
704 | strlcpy(proto, "MIT-MAGIC-COOKIE-1", sizeof proto); | 665 | strlcpy(proto, "MIT-MAGIC-COOKIE-1", sizeof proto); |
705 | for (i = 0; i < 16; i++) { | 666 | for (i = 0; i < 16; i++) { |
706 | if (i % 4 == 0) | 667 | if (i % 4 == 0) |
707 | rand = arc4random(); | 668 | rand = arc4random(); |
708 | snprintf(data + 2 * i, sizeof data - 2 * i, "%02x", rand & 0xff); | 669 | snprintf(data + 2 * i, sizeof data - 2 * i, "%02x", rand & 0xff); |
709 | rand >>= 8; | 670 | rand >>= 8; |
710 | } | 671 | } |
672 | } | ||
673 | /* Got local authentication reasonable information. | ||
674 | Request forwarding with authentication spoofing. */ | ||
675 | debug("Requesting X11 forwarding with authentication spoofing."); | ||
676 | x11_request_forwarding_with_spoofing(proto, data); | ||
677 | |||
678 | /* Read response from the server. */ | ||
679 | type = packet_read(&plen); | ||
680 | if (type == SSH_SMSG_SUCCESS) { | ||
681 | forwarded = 1; | ||
682 | interactive = 1; | ||
683 | } else if (type == SSH_SMSG_FAILURE) | ||
684 | log("Warning: Remote host denied X11 forwarding."); | ||
685 | else | ||
686 | packet_disconnect("Protocol error waiting for X11 forwarding"); | ||
687 | } | ||
688 | /* Tell the packet module whether this is an interactive session. */ | ||
689 | packet_set_interactive(interactive, options.keepalives); | ||
690 | |||
691 | /* Clear agent forwarding if we don\'t have an agent. */ | ||
692 | authfd = ssh_get_authentication_socket(); | ||
693 | if (authfd < 0) | ||
694 | options.forward_agent = 0; | ||
695 | else | ||
696 | ssh_close_authentication_socket(authfd); | ||
697 | |||
698 | /* Request authentication agent forwarding if appropriate. */ | ||
699 | if (options.forward_agent) { | ||
700 | debug("Requesting authentication agent forwarding."); | ||
701 | auth_request_forwarding(); | ||
702 | |||
703 | /* Read response from the server. */ | ||
704 | type = packet_read(&plen); | ||
705 | packet_integrity_check(plen, 0, type); | ||
706 | if (type != SSH_SMSG_SUCCESS) | ||
707 | log("Warning: Remote host denied authentication agent forwarding."); | ||
708 | } | ||
709 | /* Initiate local TCP/IP port forwardings. */ | ||
710 | for (i = 0; i < options.num_local_forwards; i++) { | ||
711 | debug("Connections to local port %d forwarded to remote address %.200s:%d", | ||
712 | options.local_forwards[i].port, | ||
713 | options.local_forwards[i].host, | ||
714 | options.local_forwards[i].host_port); | ||
715 | channel_request_local_forwarding(options.local_forwards[i].port, | ||
716 | options.local_forwards[i].host, | ||
717 | options.local_forwards[i].host_port); | ||
711 | } | 718 | } |
712 | 719 | ||
713 | /* Got local authentication reasonable information. Request forwarding | 720 | /* Initiate remote TCP/IP port forwardings. */ |
714 | with authentication spoofing. */ | 721 | for (i = 0; i < options.num_remote_forwards; i++) { |
715 | debug("Requesting X11 forwarding with authentication spoofing."); | 722 | debug("Connections to remote port %d forwarded to local address %.200s:%d", |
716 | x11_request_forwarding_with_spoofing(proto, data); | 723 | options.remote_forwards[i].port, |
717 | 724 | options.remote_forwards[i].host, | |
718 | /* Read response from the server. */ | 725 | options.remote_forwards[i].host_port); |
719 | type = packet_read(&plen); | 726 | channel_request_remote_forwarding(options.remote_forwards[i].port, |
720 | if (type == SSH_SMSG_SUCCESS) | 727 | options.remote_forwards[i].host, |
721 | { | 728 | options.remote_forwards[i].host_port); |
722 | forwarded = 1; | ||
723 | interactive = 1; | ||
724 | } | 729 | } |
725 | else if (type == SSH_SMSG_FAILURE) | 730 | |
726 | log("Warning: Remote host denied X11 forwarding."); | 731 | /* If a command was specified on the command line, execute the |
727 | else | 732 | command now. Otherwise request the server to start a shell. */ |
728 | packet_disconnect("Protocol error waiting for X11 forwarding"); | 733 | if (buffer_len(&command) > 0) { |
729 | } | 734 | int len = buffer_len(&command); |
730 | 735 | if (len > 900) | |
731 | /* Tell the packet module whether this is an interactive session. */ | 736 | len = 900; |
732 | packet_set_interactive(interactive, options.keepalives); | 737 | debug("Sending command: %.*s", len, buffer_ptr(&command)); |
733 | 738 | packet_start(SSH_CMSG_EXEC_CMD); | |
734 | /* Clear agent forwarding if we don\'t have an agent. */ | 739 | packet_put_string(buffer_ptr(&command), buffer_len(&command)); |
735 | authfd = ssh_get_authentication_socket(); | 740 | packet_send(); |
736 | if (authfd < 0) | 741 | packet_write_wait(); |
737 | options.forward_agent = 0; | 742 | } else { |
738 | else | 743 | debug("Requesting shell."); |
739 | ssh_close_authentication_socket(authfd); | 744 | packet_start(SSH_CMSG_EXEC_SHELL); |
740 | 745 | packet_send(); | |
741 | /* Request authentication agent forwarding if appropriate. */ | 746 | packet_write_wait(); |
742 | if (options.forward_agent) | 747 | } |
743 | { | 748 | |
744 | debug("Requesting authentication agent forwarding."); | 749 | /* Enter the interactive session. */ |
745 | auth_request_forwarding(); | 750 | exit_status = client_loop(tty_flag, tty_flag ? options.escape_char : -1); |
746 | 751 | ||
747 | /* Read response from the server. */ | 752 | /* Close the connection to the remote host. */ |
748 | type = packet_read(&plen); | 753 | packet_close(); |
749 | packet_integrity_check(plen, 0, type); | 754 | |
750 | if (type != SSH_SMSG_SUCCESS) | 755 | /* Exit with the status returned by the program on the remote side. */ |
751 | log("Warning: Remote host denied authentication agent forwarding."); | 756 | exit(exit_status); |
752 | } | ||
753 | |||
754 | /* Initiate local TCP/IP port forwardings. */ | ||
755 | for (i = 0; i < options.num_local_forwards; i++) | ||
756 | { | ||
757 | debug("Connections to local port %d forwarded to remote address %.200s:%d", | ||
758 | options.local_forwards[i].port, options.local_forwards[i].host, | ||
759 | options.local_forwards[i].host_port); | ||
760 | channel_request_local_forwarding(options.local_forwards[i].port, | ||
761 | options.local_forwards[i].host, | ||
762 | options.local_forwards[i].host_port); | ||
763 | } | ||
764 | |||
765 | /* Initiate remote TCP/IP port forwardings. */ | ||
766 | for (i = 0; i < options.num_remote_forwards; i++) | ||
767 | { | ||
768 | debug("Connections to remote port %d forwarded to local address %.200s:%d", | ||
769 | options.remote_forwards[i].port, options.remote_forwards[i].host, | ||
770 | options.remote_forwards[i].host_port); | ||
771 | channel_request_remote_forwarding(options.remote_forwards[i].port, | ||
772 | options.remote_forwards[i].host, | ||
773 | options.remote_forwards[i].host_port); | ||
774 | } | ||
775 | |||
776 | /* If requested, fork and let ssh continue in the background. */ | ||
777 | if (fork_after_authentication_flag) | ||
778 | { | ||
779 | int ret = fork(); | ||
780 | if (ret == -1) | ||
781 | fatal("fork failed: %.100s", strerror(errno)); | ||
782 | if (ret != 0) | ||
783 | exit(0); | ||
784 | setsid(); | ||
785 | } | ||
786 | |||
787 | /* If a command was specified on the command line, execute the command now. | ||
788 | Otherwise request the server to start a shell. */ | ||
789 | if (buffer_len(&command) > 0) | ||
790 | { | ||
791 | int len = buffer_len(&command); | ||
792 | if (len > 900) | ||
793 | len = 900; | ||
794 | debug("Sending command: %.*s", len, buffer_ptr(&command)); | ||
795 | packet_start(SSH_CMSG_EXEC_CMD); | ||
796 | packet_put_string(buffer_ptr(&command), buffer_len(&command)); | ||
797 | packet_send(); | ||
798 | packet_write_wait(); | ||
799 | } | ||
800 | else | ||
801 | { | ||
802 | debug("Requesting shell."); | ||
803 | packet_start(SSH_CMSG_EXEC_SHELL); | ||
804 | packet_send(); | ||
805 | packet_write_wait(); | ||
806 | } | ||
807 | |||
808 | /* Enter the interactive session. */ | ||
809 | exit_status = client_loop(tty_flag, tty_flag ? options.escape_char : -1); | ||
810 | |||
811 | /* Close the connection to the remote host. */ | ||
812 | packet_close(); | ||
813 | |||
814 | /* Exit with the status returned by the program on the remote side. */ | ||
815 | exit(exit_status); | ||
816 | } | 757 | } |
@@ -1,19 +1,19 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * | |
3 | ssh.h | 3 | * ssh.h |
4 | 4 | * | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
6 | 6 | * | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
8 | All rights reserved | 8 | * All rights reserved |
9 | 9 | * | |
10 | Created: Fri Mar 17 17:09:37 1995 ylo | 10 | * Created: Fri Mar 17 17:09:37 1995 ylo |
11 | 11 | * | |
12 | Generic header file for ssh. | 12 | * Generic header file for ssh. |
13 | 13 | * | |
14 | */ | 14 | */ |
15 | 15 | ||
16 | /* RCSID("$Id: ssh.h,v 1.14 1999/11/21 02:23:53 damien Exp $"); */ | 16 | /* RCSID("$Id: ssh.h,v 1.15 1999/11/24 13:26:23 damien Exp $"); */ |
17 | 17 | ||
18 | #ifndef SSH_H | 18 | #ifndef SSH_H |
19 | #define SSH_H | 19 | #define SSH_H |
@@ -25,7 +25,7 @@ Generic header file for ssh. | |||
25 | #include "rsa.h" | 25 | #include "rsa.h" |
26 | #include "cipher.h" | 26 | #include "cipher.h" |
27 | 27 | ||
28 | /* The default cipher used if IDEA is not supported by the remote host. | 28 | /* The default cipher used if IDEA is not supported by the remote host. |
29 | It is recommended that this be one of the mandatory ciphers (DES, 3DES), | 29 | It is recommended that this be one of the mandatory ciphers (DES, 3DES), |
30 | though that is not required. */ | 30 | though that is not required. */ |
31 | #define SSH_FALLBACK_CIPHER SSH_CIPHER_3DES | 31 | #define SSH_FALLBACK_CIPHER SSH_CIPHER_3DES |
@@ -59,7 +59,9 @@ Generic header file for ssh. | |||
59 | #define ETCDIR "/etc" | 59 | #define ETCDIR "/etc" |
60 | #endif /* ETCDIR */ | 60 | #endif /* ETCDIR */ |
61 | 61 | ||
62 | #ifndef PIDDIR | ||
62 | #define PIDDIR "/var/run" | 63 | #define PIDDIR "/var/run" |
64 | #endif /* PIDDIR */ | ||
63 | 65 | ||
64 | /* System-wide file containing host keys of known hosts. This file should be | 66 | /* System-wide file containing host keys of known hosts. This file should be |
65 | world-readable. */ | 67 | world-readable. */ |
@@ -76,15 +78,15 @@ only by root, whereas ssh_config should be world-readable. */ | |||
76 | #define HOST_CONFIG_FILE ETCDIR "/ssh_config" | 78 | #define HOST_CONFIG_FILE ETCDIR "/ssh_config" |
77 | 79 | ||
78 | #ifndef SSH_PROGRAM | 80 | #ifndef SSH_PROGRAM |
79 | #define SSH_PROGRAM "/usr/bin/ssh" | 81 | #define SSH_PROGRAM "/usr/bin/ssh" |
80 | #endif /* SSH_PROGRAM */ | 82 | #endif /* SSH_PROGRAM */ |
81 | 83 | ||
82 | #ifndef LOGIN_PROGRAM | 84 | #ifndef LOGIN_PROGRAM |
83 | #define LOGIN_PROGRAM "/usr/bin/login" | 85 | #define LOGIN_PROGRAM "/usr/bin/login" |
84 | #endif /* LOGIN_PROGRAM */ | 86 | #endif /* LOGIN_PROGRAM */ |
85 | 87 | ||
86 | #ifndef ASKPASS_PROGRAM | 88 | #ifndef ASKPASS_PROGRAM |
87 | #define ASKPASS_PROGRAM "/usr/lib/ssh/ssh-askpass" | 89 | #define ASKPASS_PROGRAM "/usr/lib/ssh/ssh-askpass" |
88 | #endif /* ASKPASS_PROGRAM */ | 90 | #endif /* ASKPASS_PROGRAM */ |
89 | 91 | ||
90 | /* The process id of the daemon listening for connections is saved | 92 | /* The process id of the daemon listening for connections is saved |
@@ -100,7 +102,7 @@ only by root, whereas ssh_config should be world-readable. */ | |||
100 | not contain anything particularly secret. */ | 102 | not contain anything particularly secret. */ |
101 | #define SSH_USER_HOSTFILE "~/.ssh/known_hosts" | 103 | #define SSH_USER_HOSTFILE "~/.ssh/known_hosts" |
102 | 104 | ||
103 | /* Name of the default file containing client-side authentication key. | 105 | /* Name of the default file containing client-side authentication key. |
104 | This file should only be readable by the user him/herself. */ | 106 | This file should only be readable by the user him/herself. */ |
105 | #define SSH_CLIENT_IDENTITY ".ssh/identity" | 107 | #define SSH_CLIENT_IDENTITY ".ssh/identity" |
106 | 108 | ||
@@ -116,7 +118,7 @@ only by root, whereas ssh_config should be world-readable. */ | |||
116 | readable by anyone but the user him/herself, but does not contain | 118 | readable by anyone but the user him/herself, but does not contain |
117 | anything particularly secret. If the user\'s home directory resides | 119 | anything particularly secret. If the user\'s home directory resides |
118 | on an NFS volume where root is mapped to nobody, this may need to be | 120 | on an NFS volume where root is mapped to nobody, this may need to be |
119 | world-readable. (This file is read by the daemon which is running as | 121 | world-readable. (This file is read by the daemon which is running as |
120 | root.) */ | 122 | root.) */ |
121 | #define SSH_USER_PERMITTED_KEYS ".ssh/authorized_keys" | 123 | #define SSH_USER_PERMITTED_KEYS ".ssh/authorized_keys" |
122 | 124 | ||
@@ -130,7 +132,7 @@ only by root, whereas ssh_config should be world-readable. */ | |||
130 | /* Ssh-only version of /etc/hosts.equiv. */ | 132 | /* Ssh-only version of /etc/hosts.equiv. */ |
131 | #define SSH_HOSTS_EQUIV ETCDIR "/shosts.equiv" | 133 | #define SSH_HOSTS_EQUIV ETCDIR "/shosts.equiv" |
132 | 134 | ||
133 | /* Additionally, the daemon may use ~/.rhosts and /etc/hosts.equiv if | 135 | /* Additionally, the daemon may use ~/.rhosts and /etc/hosts.equiv if |
134 | rhosts authentication is enabled. */ | 136 | rhosts authentication is enabled. */ |
135 | 137 | ||
136 | /* Name of the environment variable containing the pathname of the | 138 | /* Name of the environment variable containing the pathname of the |
@@ -145,7 +147,7 @@ only by root, whereas ssh_config should be world-readable. */ | |||
145 | many bits. This is to make double encryption with rsaref work. */ | 147 | many bits. This is to make double encryption with rsaref work. */ |
146 | #define SSH_KEY_BITS_RESERVED 128 | 148 | #define SSH_KEY_BITS_RESERVED 128 |
147 | 149 | ||
148 | /* Length of the session key in bytes. (Specified as 256 bits in the | 150 | /* Length of the session key in bytes. (Specified as 256 bits in the |
149 | protocol.) */ | 151 | protocol.) */ |
150 | #define SSH_SESSION_KEY_LENGTH 32 | 152 | #define SSH_SESSION_KEY_LENGTH 32 |
151 | 153 | ||
@@ -158,21 +160,23 @@ only by root, whereas ssh_config should be world-readable. */ | |||
158 | #define SSH_AUTH_RSA 2 | 160 | #define SSH_AUTH_RSA 2 |
159 | #define SSH_AUTH_PASSWORD 3 | 161 | #define SSH_AUTH_PASSWORD 3 |
160 | #define SSH_AUTH_RHOSTS_RSA 4 | 162 | #define SSH_AUTH_RHOSTS_RSA 4 |
161 | /* 5 is TIS */ | 163 | #define SSH_AUTH_TIS 5 |
162 | #define SSH_AUTH_KERBEROS 6 | 164 | #define SSH_AUTH_KERBEROS 6 |
163 | #define SSH_PASS_KERBEROS_TGT 7 | 165 | #define SSH_PASS_KERBEROS_TGT 7 |
164 | /* 8 to 15 are reserved */ | 166 | /* 8 to 15 are reserved */ |
165 | #define SSH_PASS_AFS_TOKEN 21 | 167 | #define SSH_PASS_AFS_TOKEN 21 |
166 | 168 | ||
167 | /* Protocol flags. These are bit masks. */ | 169 | /* Protocol flags. These are bit masks. */ |
168 | #define SSH_PROTOFLAG_SCREEN_NUMBER 1 /* X11 forwarding includes screen */ | 170 | #define SSH_PROTOFLAG_SCREEN_NUMBER 1 /* X11 forwarding includes |
169 | #define SSH_PROTOFLAG_HOST_IN_FWD_OPEN 2 /* forwarding opens contain host */ | 171 | * screen */ |
172 | #define SSH_PROTOFLAG_HOST_IN_FWD_OPEN 2 /* forwarding opens contain | ||
173 | * host */ | ||
170 | 174 | ||
171 | /* Definition of message types. New values can be added, but old values | 175 | /* Definition of message types. New values can be added, but old values |
172 | should not be removed or without careful consideration of the consequences | 176 | should not be removed or without careful consideration of the consequences |
173 | for compatibility. The maximum value is 254; value 255 is reserved | 177 | for compatibility. The maximum value is 254; value 255 is reserved |
174 | for future extension. */ | 178 | for future extension. */ |
175 | /* Message name */ /* msg code */ /* arguments */ | 179 | /* Message name *//* msg code *//* arguments */ |
176 | #define SSH_MSG_NONE 0 /* no message */ | 180 | #define SSH_MSG_NONE 0 /* no message */ |
177 | #define SSH_MSG_DISCONNECT 1 /* cause (string) */ | 181 | #define SSH_MSG_DISCONNECT 1 /* cause (string) */ |
178 | #define SSH_SMSG_PUBLIC_KEY 2 /* ck,msk,srvk,hostk */ | 182 | #define SSH_SMSG_PUBLIC_KEY 2 /* ck,msk,srvk,hostk */ |
@@ -212,10 +216,9 @@ only by root, whereas ssh_config should be world-readable. */ | |||
212 | #define SSH_MSG_DEBUG 36 /* string */ | 216 | #define SSH_MSG_DEBUG 36 /* string */ |
213 | #define SSH_CMSG_REQUEST_COMPRESSION 37 /* level 1-9 (int) */ | 217 | #define SSH_CMSG_REQUEST_COMPRESSION 37 /* level 1-9 (int) */ |
214 | #define SSH_CMSG_MAX_PACKET_SIZE 38 /* size 4k-1024k (int) */ | 218 | #define SSH_CMSG_MAX_PACKET_SIZE 38 /* size 4k-1024k (int) */ |
215 | #define SSH_CMSG_AUTH_TIS 39 /* this is proto-1.5, but we ignore TIS */ | 219 | #define SSH_CMSG_AUTH_TIS 39 /* we use this for s/key */ |
216 | #define SSH_SMSG_AUTH_TIS_CHALLENGE 40 | 220 | #define SSH_SMSG_AUTH_TIS_CHALLENGE 40 /* challenge (string) */ |
217 | #define SSH_CMSG_AUTH_TIS_RESPONSE 41 | 221 | #define SSH_CMSG_AUTH_TIS_RESPONSE 41 /* response (string) */ |
218 | |||
219 | #define SSH_CMSG_AUTH_KERBEROS 42 /* (KTEXT) */ | 222 | #define SSH_CMSG_AUTH_KERBEROS 42 /* (KTEXT) */ |
220 | #define SSH_SMSG_AUTH_KERBEROS_RESPONSE 43 /* (KTEXT) */ | 223 | #define SSH_SMSG_AUTH_KERBEROS_RESPONSE 43 /* (KTEXT) */ |
221 | #define SSH_CMSG_HAVE_KERBEROS_TGT 44 /* credentials (s) */ | 224 | #define SSH_CMSG_HAVE_KERBEROS_TGT 44 /* credentials (s) */ |
@@ -223,74 +226,79 @@ only by root, whereas ssh_config should be world-readable. */ | |||
223 | 226 | ||
224 | /*------------ definitions for login.c -------------*/ | 227 | /*------------ definitions for login.c -------------*/ |
225 | 228 | ||
226 | /* Returns the time when the user last logged in. Returns 0 if the | 229 | /* Returns the time when the user last logged in. Returns 0 if the |
227 | information is not available. This must be called before record_login. | 230 | information is not available. This must be called before record_login. |
228 | The host from which the user logged in is stored in buf. */ | 231 | The host from which the user logged in is stored in buf. */ |
229 | unsigned long get_last_login_time(uid_t uid, const char *logname, | 232 | unsigned long |
230 | char *buf, unsigned int bufsize); | 233 | get_last_login_time(uid_t uid, const char *logname, |
234 | char *buf, unsigned int bufsize); | ||
231 | 235 | ||
232 | /* Records that the user has logged in. This does many things normally | 236 | /* Records that the user has logged in. This does many things normally |
233 | done by login(1). */ | 237 | done by login(1). */ |
234 | void record_login(int pid, const char *ttyname, const char *user, uid_t uid, | 238 | void |
235 | const char *host, struct sockaddr_in *addr); | 239 | record_login(int pid, const char *ttyname, const char *user, uid_t uid, |
240 | const char *host, struct sockaddr_in * addr); | ||
236 | 241 | ||
237 | /* Records that the user has logged out. This does many thigs normally | 242 | /* Records that the user has logged out. This does many thigs normally |
238 | done by login(1) or init. */ | 243 | done by login(1) or init. */ |
239 | void record_logout(int pid, const char *ttyname); | 244 | void record_logout(int pid, const char *ttyname); |
240 | 245 | ||
241 | /*------------ definitions for sshconnect.c ----------*/ | 246 | /*------------ definitions for sshconnect.c ----------*/ |
242 | 247 | ||
243 | /* Opens a TCP/IP connection to the remote server on the given host. If | 248 | /* Opens a TCP/IP connection to the remote server on the given host. If |
244 | port is 0, the default port will be used. If anonymous is zero, | 249 | port is 0, the default port will be used. If anonymous is zero, |
245 | a privileged port will be allocated to make the connection. | 250 | a privileged port will be allocated to make the connection. |
246 | This requires super-user privileges if anonymous is false. | 251 | This requires super-user privileges if anonymous is false. |
247 | Connection_attempts specifies the maximum number of tries, one per | 252 | Connection_attempts specifies the maximum number of tries, one per |
248 | second. This returns true on success, and zero on failure. If the | 253 | second. This returns true on success, and zero on failure. If the |
249 | connection is successful, this calls packet_set_connection for the | 254 | connection is successful, this calls packet_set_connection for the |
250 | connection. */ | 255 | connection. */ |
251 | int ssh_connect(const char *host, struct sockaddr_in *hostaddr, | 256 | int |
252 | int port, int connection_attempts, | 257 | ssh_connect(const char *host, struct sockaddr_in * hostaddr, |
253 | int anonymous, uid_t original_real_uid, | 258 | int port, int connection_attempts, |
254 | const char *proxy_command); | 259 | int anonymous, uid_t original_real_uid, |
260 | const char *proxy_command); | ||
255 | 261 | ||
256 | /* Starts a dialog with the server, and authenticates the current user on the | 262 | /* Starts a dialog with the server, and authenticates the current user on the |
257 | server. This does not need any extra privileges. The basic connection | 263 | server. This does not need any extra privileges. The basic connection |
258 | to the server must already have been established before this is called. | 264 | to the server must already have been established before this is called. |
259 | If login fails, this function prints an error and never returns. | 265 | If login fails, this function prints an error and never returns. |
260 | This initializes the random state, and leaves it initialized (it will also | 266 | This initializes the random state, and leaves it initialized (it will also |
261 | have references from the packet module). */ | 267 | have references from the packet module). */ |
262 | 268 | ||
263 | void ssh_login(int host_key_valid, RSA *host_key, const char *host, | 269 | void |
264 | struct sockaddr_in *hostaddr, uid_t original_real_uid); | 270 | ssh_login(int host_key_valid, RSA * host_key, const char *host, |
271 | struct sockaddr_in * hostaddr, uid_t original_real_uid); | ||
265 | 272 | ||
266 | /*------------ Definitions for various authentication methods. -------*/ | 273 | /*------------ Definitions for various authentication methods. -------*/ |
267 | 274 | ||
268 | /* Tries to authenticate the user using the .rhosts file. Returns true if | 275 | /* Tries to authenticate the user using the .rhosts file. Returns true if |
269 | authentication succeeds. If ignore_rhosts is non-zero, this will not | 276 | authentication succeeds. If ignore_rhosts is non-zero, this will not |
270 | consider .rhosts and .shosts (/etc/hosts.equiv will still be used). */ | 277 | consider .rhosts and .shosts (/etc/hosts.equiv will still be used). */ |
271 | int auth_rhosts(struct passwd *pw, const char *client_user); | 278 | int auth_rhosts(struct passwd * pw, const char *client_user); |
272 | 279 | ||
273 | /* Tries to authenticate the user using the .rhosts file and the host using | 280 | /* Tries to authenticate the user using the .rhosts file and the host using |
274 | its host key. Returns true if authentication succeeds. */ | 281 | its host key. Returns true if authentication succeeds. */ |
275 | int auth_rhosts_rsa(struct passwd *pw, const char *client_user, | 282 | int |
276 | BIGNUM *client_host_key_e, BIGNUM *client_host_key_n); | 283 | auth_rhosts_rsa(struct passwd * pw, const char *client_user, |
284 | BIGNUM * client_host_key_e, BIGNUM * client_host_key_n); | ||
277 | 285 | ||
278 | /* Tries to authenticate the user using password. Returns true if | 286 | /* Tries to authenticate the user using password. Returns true if |
279 | authentication succeeds. */ | 287 | authentication succeeds. */ |
280 | int auth_password(struct passwd *pw, const char *password); | 288 | int auth_password(struct passwd * pw, const char *password); |
281 | 289 | ||
282 | /* Performs the RSA authentication dialog with the client. This returns | 290 | /* Performs the RSA authentication dialog with the client. This returns |
283 | 0 if the client could not be authenticated, and 1 if authentication was | 291 | 0 if the client could not be authenticated, and 1 if authentication was |
284 | successful. This may exit if there is a serious protocol violation. */ | 292 | successful. This may exit if there is a serious protocol violation. */ |
285 | int auth_rsa(struct passwd *pw, BIGNUM *client_n); | 293 | int auth_rsa(struct passwd * pw, BIGNUM * client_n); |
286 | 294 | ||
287 | /* Parses an RSA key (number of bits, e, n) from a string. Moves the pointer | 295 | /* Parses an RSA key (number of bits, e, n) from a string. Moves the pointer |
288 | over the key. Skips any whitespace at the beginning and at end. */ | 296 | over the key. Skips any whitespace at the beginning and at end. */ |
289 | int auth_rsa_read_key(char **cpp, unsigned int *bitsp, BIGNUM *e, BIGNUM *n); | 297 | int auth_rsa_read_key(char **cpp, unsigned int *bitsp, BIGNUM * e, BIGNUM * n); |
290 | 298 | ||
291 | /* Returns the name of the machine at the other end of the socket. The | 299 | /* Returns the name of the machine at the other end of the socket. The |
292 | returned string should be freed by the caller. */ | 300 | returned string should be freed by the caller. */ |
293 | char *get_remote_hostname(int socket); | 301 | char *get_remote_hostname(int socket); |
294 | 302 | ||
295 | /* Return the canonical name of the host in the other side of the current | 303 | /* Return the canonical name of the host in the other side of the current |
296 | connection (as returned by packet_get_connection). The host name is | 304 | connection (as returned by packet_get_connection). The host name is |
@@ -302,296 +310,301 @@ const char *get_canonical_hostname(void); | |||
302 | const char *get_remote_ipaddr(void); | 310 | const char *get_remote_ipaddr(void); |
303 | 311 | ||
304 | /* Returns the port number of the peer of the socket. */ | 312 | /* Returns the port number of the peer of the socket. */ |
305 | int get_peer_port(int sock); | 313 | int get_peer_port(int sock); |
306 | 314 | ||
307 | /* Returns the port number of the remote host. */ | 315 | /* Returns the port number of the remote host. */ |
308 | int get_remote_port(void); | 316 | int get_remote_port(void); |
309 | 317 | ||
310 | /* Tries to match the host name (which must be in all lowercase) against the | 318 | /* Tries to match the host name (which must be in all lowercase) against the |
311 | comma-separated sequence of subpatterns (each possibly preceded by ! to | 319 | comma-separated sequence of subpatterns (each possibly preceded by ! to |
312 | indicate negation). Returns true if there is a positive match; zero | 320 | indicate negation). Returns true if there is a positive match; zero |
313 | otherwise. */ | 321 | otherwise. */ |
314 | int match_hostname(const char *host, const char *pattern, unsigned int len); | 322 | int match_hostname(const char *host, const char *pattern, unsigned int len); |
315 | 323 | ||
316 | /* Checks whether the given host is already in the list of our known hosts. | 324 | /* Checks whether the given host is already in the list of our known hosts. |
317 | Returns HOST_OK if the host is known and has the specified key, | 325 | Returns HOST_OK if the host is known and has the specified key, |
318 | HOST_NEW if the host is not known, and HOST_CHANGED if the host is known | 326 | HOST_NEW if the host is not known, and HOST_CHANGED if the host is known |
319 | but used to have a different host key. The host must be in all lowercase. */ | 327 | but used to have a different host key. The host must be in all lowercase. */ |
320 | typedef enum { HOST_OK, HOST_NEW, HOST_CHANGED } HostStatus; | 328 | typedef enum { |
321 | HostStatus check_host_in_hostfile(const char *filename, const char *host, | 329 | HOST_OK, HOST_NEW, HOST_CHANGED |
322 | BIGNUM *e, BIGNUM *n, BIGNUM *ke, BIGNUM *kn); | 330 | } HostStatus; |
331 | HostStatus | ||
332 | check_host_in_hostfile(const char *filename, const char *host, | ||
333 | BIGNUM * e, BIGNUM * n, BIGNUM * ke, BIGNUM * kn); | ||
323 | 334 | ||
324 | /* Appends an entry to the host file. Returns false if the entry | 335 | /* Appends an entry to the host file. Returns false if the entry |
325 | could not be appended. */ | 336 | could not be appended. */ |
326 | int add_host_to_hostfile(const char *filename, const char *host, | 337 | int |
327 | BIGNUM *e, BIGNUM *n); | 338 | add_host_to_hostfile(const char *filename, const char *host, |
339 | BIGNUM * e, BIGNUM * n); | ||
328 | 340 | ||
329 | /* Performs the RSA authentication challenge-response dialog with the client, | 341 | /* Performs the RSA authentication challenge-response dialog with the client, |
330 | and returns true (non-zero) if the client gave the correct answer to | 342 | and returns true (non-zero) if the client gave the correct answer to |
331 | our challenge; returns zero if the client gives a wrong answer. */ | 343 | our challenge; returns zero if the client gives a wrong answer. */ |
332 | int auth_rsa_challenge_dialog(BIGNUM *e, BIGNUM *n); | 344 | int auth_rsa_challenge_dialog(BIGNUM * e, BIGNUM * n); |
333 | 345 | ||
334 | /* Reads a passphrase from /dev/tty with echo turned off. Returns the | 346 | /* Reads a passphrase from /dev/tty with echo turned off. Returns the |
335 | passphrase (allocated with xmalloc). Exits if EOF is encountered. | 347 | passphrase (allocated with xmalloc). Exits if EOF is encountered. |
336 | If from_stdin is true, the passphrase will be read from stdin instead. */ | 348 | If from_stdin is true, the passphrase will be read from stdin instead. */ |
337 | char *read_passphrase(const char *prompt, int from_stdin); | 349 | char *read_passphrase(const char *prompt, int from_stdin); |
338 | 350 | ||
339 | /* Saves the authentication (private) key in a file, encrypting it with | 351 | /* Saves the authentication (private) key in a file, encrypting it with |
340 | passphrase. The identification of the file (lowest 64 bits of n) | 352 | passphrase. The identification of the file (lowest 64 bits of n) |
341 | will precede the key to provide identification of the key without | 353 | will precede the key to provide identification of the key without |
342 | needing a passphrase. */ | 354 | needing a passphrase. */ |
343 | int save_private_key(const char *filename, const char *passphrase, | 355 | int |
344 | RSA *private_key, const char *comment); | 356 | save_private_key(const char *filename, const char *passphrase, |
357 | RSA * private_key, const char *comment); | ||
345 | 358 | ||
346 | /* Loads the public part of the key file (public key and comment). | 359 | /* Loads the public part of the key file (public key and comment). |
347 | Returns 0 if an error occurred; zero if the public key was successfully | 360 | Returns 0 if an error occurred; zero if the public key was successfully |
348 | read. The comment of the key is returned in comment_return if it is | 361 | read. The comment of the key is returned in comment_return if it is |
349 | non-NULL; the caller must free the value with xfree. */ | 362 | non-NULL; the caller must free the value with xfree. */ |
350 | int load_public_key(const char *filename, RSA *pub, | 363 | int |
351 | char **comment_return); | 364 | load_public_key(const char *filename, RSA * pub, |
365 | char **comment_return); | ||
352 | 366 | ||
353 | /* Loads the private key from the file. Returns 0 if an error is encountered | 367 | /* Loads the private key from the file. Returns 0 if an error is encountered |
354 | (file does not exist or is not readable, or passphrase is bad). | 368 | (file does not exist or is not readable, or passphrase is bad). |
355 | This initializes the private key. The comment of the key is returned | 369 | This initializes the private key. The comment of the key is returned |
356 | in comment_return if it is non-NULL; the caller must free the value | 370 | in comment_return if it is non-NULL; the caller must free the value |
357 | with xfree. */ | 371 | with xfree. */ |
358 | int load_private_key(const char *filename, const char *passphrase, | 372 | int |
359 | RSA *private_key, char **comment_return); | 373 | load_private_key(const char *filename, const char *passphrase, |
374 | RSA * private_key, char **comment_return); | ||
360 | 375 | ||
361 | /*------------ Definitions for logging. -----------------------*/ | 376 | /*------------ Definitions for logging. -----------------------*/ |
362 | 377 | ||
363 | /* Supported syslog facilities and levels. */ | 378 | /* Supported syslog facilities and levels. */ |
364 | typedef enum | 379 | typedef enum { |
365 | { | 380 | SYSLOG_FACILITY_DAEMON, |
366 | SYSLOG_FACILITY_DAEMON, | 381 | SYSLOG_FACILITY_USER, |
367 | SYSLOG_FACILITY_USER, | 382 | SYSLOG_FACILITY_AUTH, |
368 | SYSLOG_FACILITY_AUTH, | 383 | SYSLOG_FACILITY_LOCAL0, |
369 | SYSLOG_FACILITY_LOCAL0, | 384 | SYSLOG_FACILITY_LOCAL1, |
370 | SYSLOG_FACILITY_LOCAL1, | 385 | SYSLOG_FACILITY_LOCAL2, |
371 | SYSLOG_FACILITY_LOCAL2, | 386 | SYSLOG_FACILITY_LOCAL3, |
372 | SYSLOG_FACILITY_LOCAL3, | 387 | SYSLOG_FACILITY_LOCAL4, |
373 | SYSLOG_FACILITY_LOCAL4, | 388 | SYSLOG_FACILITY_LOCAL5, |
374 | SYSLOG_FACILITY_LOCAL5, | 389 | SYSLOG_FACILITY_LOCAL6, |
375 | SYSLOG_FACILITY_LOCAL6, | 390 | SYSLOG_FACILITY_LOCAL7 |
376 | SYSLOG_FACILITY_LOCAL7 | 391 | } SyslogFacility; |
377 | } SyslogFacility; | 392 | |
378 | 393 | typedef enum { | |
379 | typedef enum | 394 | SYSLOG_LEVEL_QUIET, |
380 | { | 395 | SYSLOG_LEVEL_FATAL, |
381 | SYSLOG_LEVEL_QUIET, | 396 | SYSLOG_LEVEL_ERROR, |
382 | SYSLOG_LEVEL_FATAL, | 397 | SYSLOG_LEVEL_INFO, |
383 | SYSLOG_LEVEL_ERROR, | 398 | SYSLOG_LEVEL_VERBOSE, |
384 | SYSLOG_LEVEL_INFO, | 399 | SYSLOG_LEVEL_DEBUG |
385 | SYSLOG_LEVEL_CHAT, | 400 | } LogLevel; |
386 | SYSLOG_LEVEL_DEBUG | ||
387 | } LogLevel; | ||
388 | |||
389 | /* Initializes logging. */ | 401 | /* Initializes logging. */ |
390 | void log_init(char *av0, LogLevel level, SyslogFacility facility, int on_stderr); | 402 | void log_init(char *av0, LogLevel level, SyslogFacility facility, int on_stderr); |
391 | 403 | ||
392 | /* Logging implementation, depending on server or client */ | 404 | /* Logging implementation, depending on server or client */ |
393 | void do_log(LogLevel level, const char *fmt, va_list args); | 405 | void do_log(LogLevel level, const char *fmt, va_list args); |
394 | 406 | ||
395 | /* name to facility/level */ | 407 | /* name to facility/level */ |
396 | SyslogFacility log_facility_number(char *name); | 408 | SyslogFacility log_facility_number(char *name); |
397 | LogLevel log_level_number(char *name); | 409 | LogLevel log_level_number(char *name); |
398 | 410 | ||
399 | /* Output a message to syslog or stderr */ | 411 | /* Output a message to syslog or stderr */ |
400 | void fatal(const char *fmt, ...); | 412 | void fatal(const char *fmt,...) __attribute__((format(printf, 1, 2))); |
401 | void error(const char *fmt, ...); | 413 | void error(const char *fmt,...) __attribute__((format(printf, 1, 2))); |
402 | void log(const char *fmt, ...); | 414 | void log(const char *fmt,...) __attribute__((format(printf, 1, 2))); |
403 | void chat(const char *fmt, ...); | 415 | void verbose(const char *fmt,...) __attribute__((format(printf, 1, 2))); |
404 | void debug(const char *fmt, ...); | 416 | void debug(const char *fmt,...) __attribute__((format(printf, 1, 2))); |
405 | 417 | ||
406 | /* same as fatal() but w/o logging */ | 418 | /* same as fatal() but w/o logging */ |
407 | void fatal_cleanup(void); | 419 | void fatal_cleanup(void); |
408 | 420 | ||
409 | /* Registers a cleanup function to be called by fatal()/fatal_cleanup() before exiting. | 421 | /* Registers a cleanup function to be called by fatal()/fatal_cleanup() before exiting. |
410 | It is permissible to call fatal_remove_cleanup for the function itself | 422 | It is permissible to call fatal_remove_cleanup for the function itself |
411 | from the function. */ | 423 | from the function. */ |
412 | void fatal_add_cleanup(void (*proc)(void *context), void *context); | 424 | void fatal_add_cleanup(void (*proc) (void *context), void *context); |
413 | 425 | ||
414 | /* Removes a cleanup function to be called at fatal(). */ | 426 | /* Removes a cleanup function to be called at fatal(). */ |
415 | void fatal_remove_cleanup(void (*proc)(void *context), void *context); | 427 | void fatal_remove_cleanup(void (*proc) (void *context), void *context); |
416 | 428 | ||
417 | /*---------------- definitions for channels ------------------*/ | 429 | /*---------------- definitions for channels ------------------*/ |
418 | 430 | ||
419 | /* Sets specific protocol options. */ | 431 | /* Sets specific protocol options. */ |
420 | void channel_set_options(int hostname_in_open); | 432 | void channel_set_options(int hostname_in_open); |
421 | 433 | ||
422 | /* Allocate a new channel object and set its type and socket. Remote_name | 434 | /* Allocate a new channel object and set its type and socket. Remote_name |
423 | must have been allocated with xmalloc; this will free it when the channel | 435 | must have been allocated with xmalloc; this will free it when the channel |
424 | is freed. */ | 436 | is freed. */ |
425 | int channel_allocate(int type, int sock, char *remote_name); | 437 | int channel_allocate(int type, int sock, char *remote_name); |
426 | 438 | ||
427 | /* Free the channel and close its socket. */ | 439 | /* Free the channel and close its socket. */ |
428 | void channel_free(int channel); | 440 | void channel_free(int channel); |
429 | 441 | ||
430 | /* Add any bits relevant to channels in select bitmasks. */ | 442 | /* Add any bits relevant to channels in select bitmasks. */ |
431 | void channel_prepare_select(fd_set *readset, fd_set *writeset); | 443 | void channel_prepare_select(fd_set * readset, fd_set * writeset); |
432 | 444 | ||
433 | /* After select, perform any appropriate operations for channels which | 445 | /* After select, perform any appropriate operations for channels which |
434 | have events pending. */ | 446 | have events pending. */ |
435 | void channel_after_select(fd_set *readset, fd_set *writeset); | 447 | void channel_after_select(fd_set * readset, fd_set * writeset); |
436 | 448 | ||
437 | /* If there is data to send to the connection, send some of it now. */ | 449 | /* If there is data to send to the connection, send some of it now. */ |
438 | void channel_output_poll(void); | 450 | void channel_output_poll(void); |
439 | 451 | ||
440 | /* This is called when a packet of type CHANNEL_DATA has just been received. | 452 | /* This is called when a packet of type CHANNEL_DATA has just been received. |
441 | The message type has already been consumed, but channel number and data | 453 | The message type has already been consumed, but channel number and data |
442 | is still there. */ | 454 | is still there. */ |
443 | void channel_input_data(int payload_len); | 455 | void channel_input_data(int payload_len); |
444 | 456 | ||
445 | /* Returns true if no channel has too much buffered data. */ | 457 | /* Returns true if no channel has too much buffered data. */ |
446 | int channel_not_very_much_buffered_data(void); | 458 | int channel_not_very_much_buffered_data(void); |
447 | 459 | ||
448 | /* This is called after receiving CHANNEL_CLOSE. */ | 460 | /* This is called after receiving CHANNEL_CLOSE. */ |
449 | void channel_input_close(void); | 461 | void channel_input_close(void); |
450 | 462 | ||
451 | /* This is called after receiving CHANNEL_CLOSE_CONFIRMATION. */ | 463 | /* This is called after receiving CHANNEL_CLOSE_CONFIRMATION. */ |
452 | void channel_input_close_confirmation(void); | 464 | void channel_input_close_confirmation(void); |
453 | 465 | ||
454 | /* This is called after receiving CHANNEL_OPEN_CONFIRMATION. */ | 466 | /* This is called after receiving CHANNEL_OPEN_CONFIRMATION. */ |
455 | void channel_input_open_confirmation(void); | 467 | void channel_input_open_confirmation(void); |
456 | 468 | ||
457 | /* This is called after receiving CHANNEL_OPEN_FAILURE from the other side. */ | 469 | /* This is called after receiving CHANNEL_OPEN_FAILURE from the other side. */ |
458 | void channel_input_open_failure(void); | 470 | void channel_input_open_failure(void); |
459 | 471 | ||
460 | /* This closes any sockets that are listening for connections; this removes | 472 | /* This closes any sockets that are listening for connections; this removes |
461 | any unix domain sockets. */ | 473 | any unix domain sockets. */ |
462 | void channel_stop_listening(void); | 474 | void channel_stop_listening(void); |
463 | 475 | ||
464 | /* Closes the sockets of all channels. This is used to close extra file | 476 | /* Closes the sockets of all channels. This is used to close extra file |
465 | descriptors after a fork. */ | 477 | descriptors after a fork. */ |
466 | void channel_close_all(void); | 478 | void channel_close_all(void); |
467 | 479 | ||
468 | /* Returns the maximum file descriptor number used by the channels. */ | 480 | /* Returns the maximum file descriptor number used by the channels. */ |
469 | int channel_max_fd(void); | 481 | int channel_max_fd(void); |
470 | 482 | ||
471 | /* Returns true if there is still an open channel over the connection. */ | 483 | /* Returns true if there is still an open channel over the connection. */ |
472 | int channel_still_open(void); | 484 | int channel_still_open(void); |
473 | 485 | ||
474 | /* Returns a string containing a list of all open channels. The list is | 486 | /* Returns a string containing a list of all open channels. The list is |
475 | suitable for displaying to the user. It uses crlf instead of newlines. | 487 | suitable for displaying to the user. It uses crlf instead of newlines. |
476 | The caller should free the string with xfree. */ | 488 | The caller should free the string with xfree. */ |
477 | char *channel_open_message(void); | 489 | char *channel_open_message(void); |
478 | 490 | ||
479 | /* Initiate forwarding of connections to local port "port" through the secure | 491 | /* Initiate forwarding of connections to local port "port" through the secure |
480 | channel to host:port from remote side. This never returns if there | 492 | channel to host:port from remote side. This never returns if there |
481 | was an error. */ | 493 | was an error. */ |
482 | void channel_request_local_forwarding(int port, const char *host, | 494 | void |
483 | int remote_port); | 495 | channel_request_local_forwarding(int port, const char *host, |
496 | int remote_port); | ||
484 | 497 | ||
485 | /* Initiate forwarding of connections to port "port" on remote host through | 498 | /* Initiate forwarding of connections to port "port" on remote host through |
486 | the secure channel to host:port from local side. This never returns | 499 | the secure channel to host:port from local side. This never returns |
487 | if there was an error. This registers that open requests for that | 500 | if there was an error. This registers that open requests for that |
488 | port are permitted. */ | 501 | port are permitted. */ |
489 | void channel_request_remote_forwarding(int port, const char *host, | 502 | void |
490 | int remote_port); | 503 | channel_request_remote_forwarding(int port, const char *host, |
504 | int remote_port); | ||
491 | 505 | ||
492 | /* Permits opening to any host/port in SSH_MSG_PORT_OPEN. This is usually | 506 | /* Permits opening to any host/port in SSH_MSG_PORT_OPEN. This is usually |
493 | called by the server, because the user could connect to any port anyway, | 507 | called by the server, because the user could connect to any port anyway, |
494 | and the server has no way to know but to trust the client anyway. */ | 508 | and the server has no way to know but to trust the client anyway. */ |
495 | void channel_permit_all_opens(void); | 509 | void channel_permit_all_opens(void); |
496 | 510 | ||
497 | /* This is called after receiving CHANNEL_FORWARDING_REQUEST. This initates | 511 | /* This is called after receiving CHANNEL_FORWARDING_REQUEST. This initates |
498 | listening for the port, and sends back a success reply (or disconnect | 512 | listening for the port, and sends back a success reply (or disconnect |
499 | message if there was an error). This never returns if there was an | 513 | message if there was an error). This never returns if there was an |
500 | error. */ | 514 | error. */ |
501 | void channel_input_port_forward_request(int is_root); | 515 | void channel_input_port_forward_request(int is_root); |
502 | 516 | ||
503 | /* This is called after receiving PORT_OPEN message. This attempts to connect | 517 | /* This is called after receiving PORT_OPEN message. This attempts to connect |
504 | to the given host:port, and sends back CHANNEL_OPEN_CONFIRMATION or | 518 | to the given host:port, and sends back CHANNEL_OPEN_CONFIRMATION or |
505 | CHANNEL_OPEN_FAILURE. */ | 519 | CHANNEL_OPEN_FAILURE. */ |
506 | void channel_input_port_open(int payload_len); | 520 | void channel_input_port_open(int payload_len); |
507 | 521 | ||
508 | /* Creates a port for X11 connections, and starts listening for it. | 522 | /* Creates a port for X11 connections, and starts listening for it. |
509 | Returns the display name, or NULL if an error was encountered. */ | 523 | Returns the display name, or NULL if an error was encountered. */ |
510 | char *x11_create_display(int screen); | 524 | char *x11_create_display(int screen); |
511 | 525 | ||
512 | /* Creates an internet domain socket for listening for X11 connections. | 526 | /* Creates an internet domain socket for listening for X11 connections. |
513 | Returns a suitable value for the DISPLAY variable, or NULL if an error | 527 | Returns a suitable value for the DISPLAY variable, or NULL if an error |
514 | occurs. */ | 528 | occurs. */ |
515 | char *x11_create_display_inet(int screen); | 529 | char *x11_create_display_inet(int screen); |
516 | 530 | ||
517 | /* This is called when SSH_SMSG_X11_OPEN is received. The packet contains | 531 | /* This is called when SSH_SMSG_X11_OPEN is received. The packet contains |
518 | the remote channel number. We should do whatever we want, and respond | 532 | the remote channel number. We should do whatever we want, and respond |
519 | with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE. */ | 533 | with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE. */ |
520 | void x11_input_open(int payload_len); | 534 | void x11_input_open(int payload_len); |
521 | 535 | ||
522 | /* Requests forwarding of X11 connections. This should be called on the | 536 | /* Requests forwarding of X11 connections. This should be called on the |
523 | client only. */ | 537 | client only. */ |
524 | void x11_request_forwarding(void); | 538 | void x11_request_forwarding(void); |
525 | 539 | ||
526 | /* Requests forwarding for X11 connections, with authentication spoofing. | 540 | /* Requests forwarding for X11 connections, with authentication spoofing. |
527 | This should be called in the client only. */ | 541 | This should be called in the client only. */ |
528 | void x11_request_forwarding_with_spoofing(const char *proto, const char *data); | 542 | void x11_request_forwarding_with_spoofing(const char *proto, const char *data); |
529 | 543 | ||
530 | /* Sends a message to the server to request authentication fd forwarding. */ | 544 | /* Sends a message to the server to request authentication fd forwarding. */ |
531 | void auth_request_forwarding(void); | 545 | void auth_request_forwarding(void); |
532 | 546 | ||
533 | /* Returns the name of the forwarded authentication socket. Returns NULL | 547 | /* Returns the name of the forwarded authentication socket. Returns NULL |
534 | if there is no forwarded authentication socket. The returned value points | 548 | if there is no forwarded authentication socket. The returned value points |
535 | to a static buffer. */ | 549 | to a static buffer. */ |
536 | char *auth_get_socket_name(void); | 550 | char *auth_get_socket_name(void); |
537 | 551 | ||
538 | /* This if called to process SSH_CMSG_AGENT_REQUEST_FORWARDING on the server. | 552 | /* This if called to process SSH_CMSG_AGENT_REQUEST_FORWARDING on the server. |
539 | This starts forwarding authentication requests. */ | 553 | This starts forwarding authentication requests. */ |
540 | void auth_input_request_forwarding(struct passwd *pw); | 554 | void auth_input_request_forwarding(struct passwd * pw); |
541 | 555 | ||
542 | /* This is called to process an SSH_SMSG_AGENT_OPEN message. */ | 556 | /* This is called to process an SSH_SMSG_AGENT_OPEN message. */ |
543 | void auth_input_open_request(void); | 557 | void auth_input_open_request(void); |
544 | 558 | ||
545 | /* Returns true if the given string matches the pattern (which may contain | 559 | /* Returns true if the given string matches the pattern (which may contain |
546 | ? and * as wildcards), and zero if it does not match. */ | 560 | ? and * as wildcards), and zero if it does not match. */ |
547 | int match_pattern(const char *s, const char *pattern); | 561 | int match_pattern(const char *s, const char *pattern); |
548 | 562 | ||
549 | /* Expands tildes in the file name. Returns data allocated by xmalloc. | 563 | /* Expands tildes in the file name. Returns data allocated by xmalloc. |
550 | Warning: this calls getpw*. */ | 564 | Warning: this calls getpw*. */ |
551 | char *tilde_expand_filename(const char *filename, uid_t my_uid); | 565 | char *tilde_expand_filename(const char *filename, uid_t my_uid); |
552 | 566 | ||
553 | /* Performs the interactive session. This handles data transmission between | 567 | /* Performs the interactive session. This handles data transmission between |
554 | the client and the program. Note that the notion of stdin, stdout, and | 568 | the client and the program. Note that the notion of stdin, stdout, and |
555 | stderr in this function is sort of reversed: this function writes to | 569 | stderr in this function is sort of reversed: this function writes to |
556 | stdin (of the child program), and reads from stdout and stderr (of the | 570 | stdin (of the child program), and reads from stdout and stderr (of the |
557 | child program). */ | 571 | child program). */ |
558 | void server_loop(int pid, int fdin, int fdout, int fderr); | 572 | void server_loop(int pid, int fdin, int fdout, int fderr); |
559 | 573 | ||
560 | /* Client side main loop for the interactive session. */ | 574 | /* Client side main loop for the interactive session. */ |
561 | int client_loop(int have_pty, int escape_char); | 575 | int client_loop(int have_pty, int escape_char); |
562 | 576 | ||
563 | /* Linked list of custom environment strings (see auth-rsa.c). */ | 577 | /* Linked list of custom environment strings (see auth-rsa.c). */ |
564 | struct envstring { | 578 | struct envstring { |
565 | struct envstring *next; | 579 | struct envstring *next; |
566 | char *s; | 580 | char *s; |
567 | }; | 581 | }; |
568 | |||
569 | #ifdef KRB4 | 582 | #ifdef KRB4 |
570 | #include <krb.h> | 583 | #include <krb.h> |
571 | 584 | ||
572 | /* Performs Kerberos v4 mutual authentication with the client. This returns | 585 | /* Performs Kerberos v4 mutual authentication with the client. This returns |
573 | 0 if the client could not be authenticated, and 1 if authentication was | 586 | 0 if the client could not be authenticated, and 1 if authentication was |
574 | successful. This may exit if there is a serious protocol violation. */ | 587 | successful. This may exit if there is a serious protocol violation. */ |
575 | int auth_krb4(const char *server_user, KTEXT auth, char **client); | 588 | int auth_krb4(const char *server_user, KTEXT auth, char **client); |
576 | int krb4_init(uid_t uid); | 589 | int krb4_init(uid_t uid); |
577 | void krb4_cleanup_proc(void *ignore); | 590 | void krb4_cleanup_proc(void *ignore); |
578 | 591 | ||
579 | #ifdef AFS | 592 | #ifdef AFS |
580 | #include <kafs.h> | 593 | #include <kafs.h> |
581 | 594 | ||
582 | /* Accept passed Kerberos v4 ticket-granting ticket and AFS tokens. */ | 595 | /* Accept passed Kerberos v4 ticket-granting ticket and AFS tokens. */ |
583 | int auth_kerberos_tgt(struct passwd *pw, const char *string); | 596 | int auth_kerberos_tgt(struct passwd * pw, const char *string); |
584 | int auth_afs_token(struct passwd *pw, const char *token_string); | 597 | int auth_afs_token(struct passwd * pw, const char *token_string); |
585 | 598 | ||
586 | int creds_to_radix(CREDENTIALS *creds, unsigned char *buf); | 599 | int creds_to_radix(CREDENTIALS * creds, unsigned char *buf); |
587 | int radix_to_creds(const char *buf, CREDENTIALS *creds); | 600 | int radix_to_creds(const char *buf, CREDENTIALS * creds); |
588 | #endif /* AFS */ | 601 | #endif /* AFS */ |
589 | 602 | ||
590 | #endif /* KRB4 */ | 603 | #endif /* KRB4 */ |
591 | 604 | ||
592 | #ifdef SKEY | 605 | #ifdef SKEY |
593 | #include <skey.h> | 606 | #include <skey.h> |
594 | char *skey_fake_keyinfo(char *username); | 607 | char *skey_fake_keyinfo(char *username); |
595 | #endif /* SKEY */ | 608 | #endif /* SKEY */ |
596 | 609 | ||
597 | #endif /* SSH_H */ | 610 | #endif /* SSH_H */ |
diff --git a/sshconnect.c b/sshconnect.c index fba389d8b..0657c37e8 100644 --- a/sshconnect.c +++ b/sshconnect.c | |||
@@ -1,21 +1,14 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | |
3 | sshconnect.c | 3 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
4 | 4 | * All rights reserved | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Created: Sat Mar 18 22:15:47 1995 ylo |
6 | 6 | * Code to connect to a remote host, and to perform the client side of the | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * login (authentication) dialog. |
8 | All rights reserved | 8 | */ |
9 | |||
10 | Created: Sat Mar 18 22:15:47 1995 ylo | ||
11 | |||
12 | Code to connect to a remote host, and to perform the client side of the | ||
13 | login (authentication) dialog. | ||
14 | |||
15 | */ | ||
16 | 9 | ||
17 | #include "includes.h" | 10 | #include "includes.h" |
18 | RCSID("$Id: sshconnect.c,v 1.13 1999/11/21 02:23:53 damien Exp $"); | 11 | RCSID("$Id: sshconnect.c,v 1.14 1999/11/24 13:26:23 damien Exp $"); |
19 | 12 | ||
20 | #ifdef HAVE_OPENSSL | 13 | #ifdef HAVE_OPENSSL |
21 | #include <openssl/bn.h> | 14 | #include <openssl/bn.h> |
@@ -41,1497 +34,1495 @@ RCSID("$Id: sshconnect.c,v 1.13 1999/11/21 02:23:53 damien Exp $"); | |||
41 | /* Session id for the current session. */ | 34 | /* Session id for the current session. */ |
42 | unsigned char session_id[16]; | 35 | unsigned char session_id[16]; |
43 | 36 | ||
44 | /* Connect to the given ssh server using a proxy command. */ | 37 | /* |
45 | 38 | * Connect to the given ssh server using a proxy command. | |
39 | */ | ||
46 | int | 40 | int |
47 | ssh_proxy_connect(const char *host, int port, uid_t original_real_uid, | 41 | ssh_proxy_connect(const char *host, int port, uid_t original_real_uid, |
48 | const char *proxy_command) | 42 | const char *proxy_command) |
49 | { | 43 | { |
50 | Buffer command; | 44 | Buffer command; |
51 | const char *cp; | 45 | const char *cp; |
52 | char *command_string; | 46 | char *command_string; |
53 | int pin[2], pout[2]; | 47 | int pin[2], pout[2]; |
54 | int pid; | 48 | int pid; |
55 | char portstring[100]; | 49 | char portstring[100]; |
56 | 50 | ||
57 | /* Convert the port number into a string. */ | 51 | /* Convert the port number into a string. */ |
58 | snprintf(portstring, sizeof portstring, "%d", port); | 52 | snprintf(portstring, sizeof portstring, "%d", port); |
59 | 53 | ||
60 | /* Build the final command string in the buffer by making the appropriate | 54 | /* Build the final command string in the buffer by making the |
61 | substitutions to the given proxy command. */ | 55 | appropriate substitutions to the given proxy command. */ |
62 | buffer_init(&command); | 56 | buffer_init(&command); |
63 | for (cp = proxy_command; *cp; cp++) | 57 | for (cp = proxy_command; *cp; cp++) { |
64 | { | 58 | if (cp[0] == '%' && cp[1] == '%') { |
65 | if (cp[0] == '%' && cp[1] == '%') | 59 | buffer_append(&command, "%", 1); |
66 | { | 60 | cp++; |
67 | buffer_append(&command, "%", 1); | 61 | continue; |
68 | cp++; | 62 | } |
69 | continue; | 63 | if (cp[0] == '%' && cp[1] == 'h') { |
70 | } | 64 | buffer_append(&command, host, strlen(host)); |
71 | if (cp[0] == '%' && cp[1] == 'h') | 65 | cp++; |
72 | { | 66 | continue; |
73 | buffer_append(&command, host, strlen(host)); | 67 | } |
74 | cp++; | 68 | if (cp[0] == '%' && cp[1] == 'p') { |
75 | continue; | 69 | buffer_append(&command, portstring, strlen(portstring)); |
76 | } | 70 | cp++; |
77 | if (cp[0] == '%' && cp[1] == 'p') | 71 | continue; |
78 | { | 72 | } |
79 | buffer_append(&command, portstring, strlen(portstring)); | 73 | buffer_append(&command, cp, 1); |
80 | cp++; | ||
81 | continue; | ||
82 | } | 74 | } |
83 | buffer_append(&command, cp, 1); | 75 | buffer_append(&command, "\0", 1); |
84 | } | 76 | |
85 | buffer_append(&command, "\0", 1); | 77 | /* Get the final command string. */ |
86 | 78 | command_string = buffer_ptr(&command); | |
87 | /* Get the final command string. */ | 79 | |
88 | command_string = buffer_ptr(&command); | 80 | /* Create pipes for communicating with the proxy. */ |
89 | 81 | if (pipe(pin) < 0 || pipe(pout) < 0) | |
90 | /* Create pipes for communicating with the proxy. */ | 82 | fatal("Could not create pipes to communicate with the proxy: %.100s", |
91 | if (pipe(pin) < 0 || pipe(pout) < 0) | 83 | strerror(errno)); |
92 | fatal("Could not create pipes to communicate with the proxy: %.100s", | 84 | |
93 | strerror(errno)); | 85 | debug("Executing proxy command: %.500s", command_string); |
94 | 86 | ||
95 | debug("Executing proxy command: %.500s", command_string); | 87 | /* Fork and execute the proxy command. */ |
96 | 88 | if ((pid = fork()) == 0) { | |
97 | /* Fork and execute the proxy command. */ | 89 | char *argv[10]; |
98 | if ((pid = fork()) == 0) | 90 | |
99 | { | 91 | /* Child. Permanently give up superuser privileges. */ |
100 | char *argv[10]; | 92 | permanently_set_uid(original_real_uid); |
101 | 93 | ||
102 | /* Child. Permanently give up superuser privileges. */ | 94 | /* Redirect stdin and stdout. */ |
103 | permanently_set_uid(original_real_uid); | 95 | close(pin[1]); |
104 | 96 | if (pin[0] != 0) { | |
105 | /* Redirect stdin and stdout. */ | 97 | if (dup2(pin[0], 0) < 0) |
106 | close(pin[1]); | 98 | perror("dup2 stdin"); |
107 | if (pin[0] != 0) | 99 | close(pin[0]); |
108 | { | 100 | } |
109 | if (dup2(pin[0], 0) < 0) | 101 | close(pout[0]); |
110 | perror("dup2 stdin"); | 102 | if (dup2(pout[1], 1) < 0) |
111 | close(pin[0]); | 103 | perror("dup2 stdout"); |
104 | /* Cannot be 1 because pin allocated two descriptors. */ | ||
105 | close(pout[1]); | ||
106 | |||
107 | /* Stderr is left as it is so that error messages get | ||
108 | printed on the user's terminal. */ | ||
109 | argv[0] = "/bin/sh"; | ||
110 | argv[1] = "-c"; | ||
111 | argv[2] = command_string; | ||
112 | argv[3] = NULL; | ||
113 | |||
114 | /* Execute the proxy command. Note that we gave up any | ||
115 | extra privileges above. */ | ||
116 | execv("/bin/sh", argv); | ||
117 | perror("/bin/sh"); | ||
118 | exit(1); | ||
112 | } | 119 | } |
113 | close(pout[0]); | 120 | /* Parent. */ |
114 | if (dup2(pout[1], 1) < 0) | 121 | if (pid < 0) |
115 | perror("dup2 stdout"); | 122 | fatal("fork failed: %.100s", strerror(errno)); |
116 | close(pout[1]); /* Cannot be 1 because pin allocated two descriptors. */ | 123 | |
117 | 124 | /* Close child side of the descriptors. */ | |
118 | /* Stderr is left as it is so that error messages get printed on | 125 | close(pin[0]); |
119 | the user's terminal. */ | 126 | close(pout[1]); |
120 | argv[0] = "/bin/sh"; | 127 | |
121 | argv[1] = "-c"; | 128 | /* Free the command name. */ |
122 | argv[2] = command_string; | 129 | buffer_free(&command); |
123 | argv[3] = NULL; | 130 | |
124 | 131 | /* Set the connection file descriptors. */ | |
125 | /* Execute the proxy command. Note that we gave up any extra | 132 | packet_set_connection(pout[0], pin[1]); |
126 | privileges above. */ | ||
127 | execv("/bin/sh", argv); | ||
128 | perror("/bin/sh"); | ||
129 | exit(1); | ||
130 | } | ||
131 | /* Parent. */ | ||
132 | if (pid < 0) | ||
133 | fatal("fork failed: %.100s", strerror(errno)); | ||
134 | |||
135 | /* Close child side of the descriptors. */ | ||
136 | close(pin[0]); | ||
137 | close(pout[1]); | ||
138 | |||
139 | /* Free the command name. */ | ||
140 | buffer_free(&command); | ||
141 | |||
142 | /* Set the connection file descriptors. */ | ||
143 | packet_set_connection(pout[0], pin[1]); | ||
144 | |||
145 | return 1; | ||
146 | } | ||
147 | 133 | ||
148 | /* Creates a (possibly privileged) socket for use as the ssh connection. */ | 134 | return 1; |
135 | } | ||
149 | 136 | ||
150 | int ssh_create_socket(uid_t original_real_uid, int privileged) | 137 | /* |
138 | * Creates a (possibly privileged) socket for use as the ssh connection. | ||
139 | */ | ||
140 | int | ||
141 | ssh_create_socket(uid_t original_real_uid, int privileged) | ||
151 | { | 142 | { |
152 | int sock; | 143 | int sock; |
153 | 144 | ||
154 | /* If we are running as root and want to connect to a privileged port, | 145 | /* If we are running as root and want to connect to a privileged |
155 | bind our own socket to a privileged port. */ | 146 | port, bind our own socket to a privileged port. */ |
156 | if (privileged) | 147 | if (privileged) { |
157 | { | 148 | int p = IPPORT_RESERVED - 1; |
158 | int p = IPPORT_RESERVED - 1; | 149 | |
159 | 150 | sock = rresvport(&p); | |
160 | sock = rresvport(&p); | 151 | if (sock < 0) |
161 | if (sock < 0) | 152 | fatal("rresvport: %.100s", strerror(errno)); |
162 | fatal("rresvport: %.100s", strerror(errno)); | 153 | debug("Allocated local port %d.", p); |
163 | debug("Allocated local port %d.", p); | 154 | } else { |
164 | } | 155 | /* Just create an ordinary socket on arbitrary port. We |
165 | else | 156 | use the user's uid to create the socket. */ |
166 | { | 157 | temporarily_use_uid(original_real_uid); |
167 | /* Just create an ordinary socket on arbitrary port. We use the | 158 | sock = socket(AF_INET, SOCK_STREAM, 0); |
168 | user's uid to create the socket. */ | 159 | if (sock < 0) |
169 | temporarily_use_uid(original_real_uid); | 160 | fatal("socket: %.100s", strerror(errno)); |
170 | sock = socket(AF_INET, SOCK_STREAM, 0); | 161 | restore_uid(); |
171 | if (sock < 0) | 162 | } |
172 | fatal("socket: %.100s", strerror(errno)); | 163 | return sock; |
173 | restore_uid(); | ||
174 | } | ||
175 | return sock; | ||
176 | } | 164 | } |
177 | 165 | ||
178 | /* Opens a TCP/IP connection to the remote server on the given host. If | 166 | /* |
179 | port is 0, the default port will be used. If anonymous is zero, | 167 | * Opens a TCP/IP connection to the remote server on the given host. If |
180 | a privileged port will be allocated to make the connection. | 168 | * port is 0, the default port will be used. If anonymous is zero, |
181 | This requires super-user privileges if anonymous is false. | 169 | * a privileged port will be allocated to make the connection. |
182 | Connection_attempts specifies the maximum number of tries (one per | 170 | * This requires super-user privileges if anonymous is false. |
183 | second). If proxy_command is non-NULL, it specifies the command (with %h | 171 | * Connection_attempts specifies the maximum number of tries (one per |
184 | and %p substituted for host and port, respectively) to use to contact | 172 | * second). If proxy_command is non-NULL, it specifies the command (with %h |
185 | the daemon. */ | 173 | * and %p substituted for host and port, respectively) to use to contact |
186 | 174 | * the daemon. | |
187 | int ssh_connect(const char *host, struct sockaddr_in *hostaddr, | 175 | */ |
188 | int port, int connection_attempts, | 176 | int |
189 | int anonymous, uid_t original_real_uid, | 177 | ssh_connect(const char *host, struct sockaddr_in * hostaddr, |
190 | const char *proxy_command) | 178 | int port, int connection_attempts, |
179 | int anonymous, uid_t original_real_uid, | ||
180 | const char *proxy_command) | ||
191 | { | 181 | { |
192 | int sock = -1, attempt, i; | 182 | int sock = -1, attempt, i; |
193 | int on = 1; | 183 | int on = 1; |
194 | struct servent *sp; | 184 | struct servent *sp; |
195 | struct hostent *hp; | 185 | struct hostent *hp; |
196 | struct linger linger; | 186 | struct linger linger; |
197 | 187 | ||
198 | debug("ssh_connect: getuid %d geteuid %d anon %d", | 188 | debug("ssh_connect: getuid %d geteuid %d anon %d", |
199 | (int)getuid(), (int)geteuid(), anonymous); | 189 | (int) getuid(), (int) geteuid(), anonymous); |
200 | 190 | ||
201 | /* Get default port if port has not been set. */ | 191 | /* Get default port if port has not been set. */ |
202 | if (port == 0) | 192 | if (port == 0) { |
203 | { | 193 | sp = getservbyname(SSH_SERVICE_NAME, "tcp"); |
204 | sp = getservbyname(SSH_SERVICE_NAME, "tcp"); | 194 | if (sp) |
205 | if (sp) | 195 | port = ntohs(sp->s_port); |
206 | port = ntohs(sp->s_port); | 196 | else |
207 | else | 197 | port = SSH_DEFAULT_PORT; |
208 | port = SSH_DEFAULT_PORT; | ||
209 | } | ||
210 | |||
211 | /* If a proxy command is given, connect using it. */ | ||
212 | if (proxy_command != NULL) | ||
213 | return ssh_proxy_connect(host, port, original_real_uid, proxy_command); | ||
214 | |||
215 | /* No proxy command. */ | ||
216 | |||
217 | /* No host lookup made yet. */ | ||
218 | hp = NULL; | ||
219 | |||
220 | /* Try to connect several times. On some machines, the first time will | ||
221 | sometimes fail. In general socket code appears to behave quite | ||
222 | magically on many machines. */ | ||
223 | for (attempt = 0; attempt < connection_attempts; attempt++) | ||
224 | { | ||
225 | if (attempt > 0) | ||
226 | debug("Trying again..."); | ||
227 | |||
228 | /* Try to parse the host name as a numeric inet address. */ | ||
229 | memset(hostaddr, 0, sizeof(hostaddr)); | ||
230 | hostaddr->sin_family = AF_INET; | ||
231 | hostaddr->sin_port = htons(port); | ||
232 | hostaddr->sin_addr.s_addr = inet_addr(host); | ||
233 | if ((hostaddr->sin_addr.s_addr & 0xffffffff) != 0xffffffff) | ||
234 | { | ||
235 | /* Valid numeric IP address */ | ||
236 | debug("Connecting to %.100s port %d.", | ||
237 | inet_ntoa(hostaddr->sin_addr), port); | ||
238 | |||
239 | /* Create a socket. */ | ||
240 | sock = ssh_create_socket(original_real_uid, | ||
241 | !anonymous && geteuid() == 0 && | ||
242 | port < IPPORT_RESERVED); | ||
243 | |||
244 | /* Connect to the host. We use the user's uid in the hope that | ||
245 | it will help with the problems of tcp_wrappers showing the | ||
246 | remote uid as root. */ | ||
247 | temporarily_use_uid(original_real_uid); | ||
248 | if (connect(sock, (struct sockaddr *)hostaddr, sizeof(*hostaddr)) | ||
249 | >= 0) | ||
250 | { | ||
251 | /* Successful connect. */ | ||
252 | restore_uid(); | ||
253 | break; | ||
254 | } | ||
255 | debug("connect: %.100s", strerror(errno)); | ||
256 | restore_uid(); | ||
257 | |||
258 | /* Destroy the failed socket. */ | ||
259 | shutdown(sock, SHUT_RDWR); | ||
260 | close(sock); | ||
261 | } | 198 | } |
262 | else | 199 | /* If a proxy command is given, connect using it. */ |
263 | { | 200 | if (proxy_command != NULL) |
264 | /* Not a valid numeric inet address. */ | 201 | return ssh_proxy_connect(host, port, original_real_uid, proxy_command); |
265 | /* Map host name to an address. */ | 202 | |
266 | if (!hp) | 203 | /* No proxy command. */ |
267 | hp = gethostbyname(host); | 204 | |
268 | if (!hp) | 205 | /* No host lookup made yet. */ |
269 | fatal("Bad host name: %.100s", host); | 206 | hp = NULL; |
270 | if (!hp->h_addr_list[0]) | 207 | |
271 | fatal("Host does not have an IP address: %.100s", host); | 208 | /* Try to connect several times. On some machines, the first time |
272 | 209 | will sometimes fail. In general socket code appears to behave | |
273 | /* Loop through addresses for this host, and try each one in | 210 | quite magically on many machines. */ |
274 | sequence until the connection succeeds. */ | 211 | for (attempt = 0; attempt < connection_attempts; attempt++) { |
275 | for (i = 0; hp->h_addr_list[i]; i++) | 212 | if (attempt > 0) |
276 | { | 213 | debug("Trying again..."); |
277 | /* Set the address to connect to. */ | 214 | |
278 | hostaddr->sin_family = hp->h_addrtype; | 215 | /* Try to parse the host name as a numeric inet address. */ |
279 | memcpy(&hostaddr->sin_addr, hp->h_addr_list[i], | 216 | memset(hostaddr, 0, sizeof(hostaddr)); |
280 | sizeof(hostaddr->sin_addr)); | 217 | hostaddr->sin_family = AF_INET; |
281 | 218 | hostaddr->sin_port = htons(port); | |
282 | debug("Connecting to %.200s [%.100s] port %d.", | 219 | hostaddr->sin_addr.s_addr = inet_addr(host); |
283 | host, inet_ntoa(hostaddr->sin_addr), port); | 220 | if ((hostaddr->sin_addr.s_addr & 0xffffffff) != 0xffffffff) { |
284 | 221 | /* Valid numeric IP address */ | |
285 | /* Create a socket for connecting. */ | 222 | debug("Connecting to %.100s port %d.", |
286 | sock = ssh_create_socket(original_real_uid, | 223 | inet_ntoa(hostaddr->sin_addr), port); |
287 | !anonymous && geteuid() == 0 && | 224 | |
288 | port < IPPORT_RESERVED); | 225 | /* Create a socket. */ |
289 | 226 | sock = ssh_create_socket(original_real_uid, | |
290 | /* Connect to the host. We use the user's uid in the hope that | 227 | !anonymous && geteuid() == 0 && |
291 | it will help with tcp_wrappers showing the remote uid as | 228 | port < IPPORT_RESERVED); |
292 | root. */ | 229 | |
293 | temporarily_use_uid(original_real_uid); | 230 | /* Connect to the host. We use the user's uid in |
294 | if (connect(sock, (struct sockaddr *)hostaddr, | 231 | the hope that it will help with the problems of |
295 | sizeof(*hostaddr)) >= 0) | 232 | tcp_wrappers showing the remote uid as root. */ |
296 | { | 233 | temporarily_use_uid(original_real_uid); |
297 | /* Successful connection. */ | 234 | if (connect(sock, (struct sockaddr *) hostaddr, sizeof(*hostaddr)) |
298 | restore_uid(); | 235 | >= 0) { |
299 | break; | 236 | /* Successful connect. */ |
237 | restore_uid(); | ||
238 | break; | ||
239 | } | ||
240 | debug("connect: %.100s", strerror(errno)); | ||
241 | restore_uid(); | ||
242 | |||
243 | /* Destroy the failed socket. */ | ||
244 | shutdown(sock, SHUT_RDWR); | ||
245 | close(sock); | ||
246 | } else { | ||
247 | /* Not a valid numeric inet address. */ | ||
248 | /* Map host name to an address. */ | ||
249 | if (!hp) | ||
250 | hp = gethostbyname(host); | ||
251 | if (!hp) | ||
252 | fatal("Bad host name: %.100s", host); | ||
253 | if (!hp->h_addr_list[0]) | ||
254 | fatal("Host does not have an IP address: %.100s", host); | ||
255 | |||
256 | /* Loop through addresses for this host, and try | ||
257 | each one in sequence until the connection | ||
258 | succeeds. */ | ||
259 | for (i = 0; hp->h_addr_list[i]; i++) { | ||
260 | /* Set the address to connect to. */ | ||
261 | hostaddr->sin_family = hp->h_addrtype; | ||
262 | memcpy(&hostaddr->sin_addr, hp->h_addr_list[i], | ||
263 | sizeof(hostaddr->sin_addr)); | ||
264 | |||
265 | debug("Connecting to %.200s [%.100s] port %d.", | ||
266 | host, inet_ntoa(hostaddr->sin_addr), port); | ||
267 | |||
268 | /* Create a socket for connecting. */ | ||
269 | sock = ssh_create_socket(original_real_uid, | ||
270 | !anonymous && geteuid() == 0 && | ||
271 | port < IPPORT_RESERVED); | ||
272 | |||
273 | /* Connect to the host. We use the user's uid in the hope that | ||
274 | it will help with tcp_wrappers showing the remote uid as root. */ | ||
275 | temporarily_use_uid(original_real_uid); | ||
276 | if (connect(sock, (struct sockaddr *) hostaddr, | ||
277 | sizeof(*hostaddr)) >= 0) { | ||
278 | /* Successful connection. */ | ||
279 | restore_uid(); | ||
280 | break; | ||
281 | } | ||
282 | debug("connect: %.100s", strerror(errno)); | ||
283 | restore_uid(); | ||
284 | |||
285 | /* Close the failed socket; there appear to be some problems when | ||
286 | reusing a socket for which connect() has already returned an error. */ | ||
287 | shutdown(sock, SHUT_RDWR); | ||
288 | close(sock); | ||
289 | } | ||
290 | if (hp->h_addr_list[i]) | ||
291 | break; /* Successful connection. */ | ||
300 | } | 292 | } |
301 | debug("connect: %.100s", strerror(errno)); | ||
302 | restore_uid(); | ||
303 | |||
304 | /* Close the failed socket; there appear to be some problems | ||
305 | when reusing a socket for which connect() has already | ||
306 | returned an error. */ | ||
307 | shutdown(sock, SHUT_RDWR); | ||
308 | close(sock); | ||
309 | } | ||
310 | if (hp->h_addr_list[i]) | ||
311 | break; /* Successful connection. */ | ||
312 | } | ||
313 | 293 | ||
314 | /* Sleep a moment before retrying. */ | 294 | /* Sleep a moment before retrying. */ |
315 | sleep(1); | 295 | sleep(1); |
316 | } | 296 | } |
317 | /* Return failure if we didn't get a successful connection. */ | 297 | /* Return failure if we didn't get a successful connection. */ |
318 | if (attempt >= connection_attempts) | 298 | if (attempt >= connection_attempts) |
319 | return 0; | 299 | return 0; |
320 | 300 | ||
321 | debug("Connection established."); | 301 | debug("Connection established."); |
322 | 302 | ||
323 | /* Set socket options. We would like the socket to disappear as soon as | 303 | /* Set socket options. We would like the socket to disappear as |
324 | it has been closed for whatever reason. */ | 304 | soon as it has been closed for whatever reason. */ |
325 | /* setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); */ | 305 | /* setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, |
326 | setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void *)&on, sizeof(on)); | 306 | sizeof(on)); */ |
327 | linger.l_onoff = 1; | 307 | setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void *) &on, sizeof(on)); |
328 | linger.l_linger = 5; | 308 | linger.l_onoff = 1; |
329 | setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *)&linger, sizeof(linger)); | 309 | linger.l_linger = 5; |
310 | setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *) &linger, sizeof(linger)); | ||
330 | 311 | ||
331 | /* Set the connection. */ | 312 | /* Set the connection. */ |
332 | packet_set_connection(sock, sock); | 313 | packet_set_connection(sock, sock); |
333 | 314 | ||
334 | return 1; | 315 | return 1; |
335 | } | 316 | } |
336 | 317 | ||
337 | /* Checks if the user has an authentication agent, and if so, tries to | 318 | /* |
338 | authenticate using the agent. */ | 319 | * Checks if the user has an authentication agent, and if so, tries to |
339 | 320 | * authenticate using the agent. | |
321 | */ | ||
340 | int | 322 | int |
341 | try_agent_authentication() | 323 | try_agent_authentication() |
342 | { | 324 | { |
343 | int status, type; | 325 | int status, type; |
344 | char *comment; | 326 | char *comment; |
345 | AuthenticationConnection *auth; | 327 | AuthenticationConnection *auth; |
346 | unsigned char response[16]; | 328 | unsigned char response[16]; |
347 | unsigned int i; | 329 | unsigned int i; |
348 | BIGNUM *e, *n, *challenge; | 330 | BIGNUM *e, *n, *challenge; |
349 | 331 | ||
350 | /* Get connection to the agent. */ | 332 | /* Get connection to the agent. */ |
351 | auth = ssh_get_authentication_connection(); | 333 | auth = ssh_get_authentication_connection(); |
352 | if (!auth) | 334 | if (!auth) |
353 | return 0; | 335 | return 0; |
354 | 336 | ||
355 | e = BN_new(); | 337 | e = BN_new(); |
356 | n = BN_new(); | 338 | n = BN_new(); |
357 | challenge = BN_new(); | 339 | challenge = BN_new(); |
358 | 340 | ||
359 | /* Loop through identities served by the agent. */ | 341 | /* Loop through identities served by the agent. */ |
360 | for (status = ssh_get_first_identity(auth, e, n, &comment); | 342 | for (status = ssh_get_first_identity(auth, e, n, &comment); |
361 | status; | 343 | status; |
362 | status = ssh_get_next_identity(auth, e, n, &comment)) | 344 | status = ssh_get_next_identity(auth, e, n, &comment)) { |
363 | { | 345 | int plen, clen; |
364 | int plen, clen; | 346 | |
365 | 347 | /* Try this identity. */ | |
366 | /* Try this identity. */ | 348 | debug("Trying RSA authentication via agent with '%.100s'", comment); |
367 | debug("Trying RSA authentication via agent with '%.100s'", comment); | 349 | xfree(comment); |
368 | xfree(comment); | 350 | |
369 | 351 | /* Tell the server that we are willing to authenticate using this key. */ | |
370 | /* Tell the server that we are willing to authenticate using this key. */ | 352 | packet_start(SSH_CMSG_AUTH_RSA); |
371 | packet_start(SSH_CMSG_AUTH_RSA); | 353 | packet_put_bignum(n); |
372 | packet_put_bignum(n); | 354 | packet_send(); |
373 | packet_send(); | 355 | packet_write_wait(); |
374 | packet_write_wait(); | 356 | |
375 | 357 | /* Wait for server's response. */ | |
376 | /* Wait for server's response. */ | 358 | type = packet_read(&plen); |
377 | type = packet_read(&plen); | 359 | |
378 | 360 | /* The server sends failure if it doesn\'t like our key or | |
379 | /* The server sends failure if it doesn\'t like our key or does not | 361 | does not support RSA authentication. */ |
380 | support RSA authentication. */ | 362 | if (type == SSH_SMSG_FAILURE) { |
381 | if (type == SSH_SMSG_FAILURE) | 363 | debug("Server refused our key."); |
382 | { | 364 | continue; |
383 | debug("Server refused our key."); | 365 | } |
384 | continue; | 366 | /* Otherwise it should have sent a challenge. */ |
385 | } | 367 | if (type != SSH_SMSG_AUTH_RSA_CHALLENGE) |
386 | 368 | packet_disconnect("Protocol error during RSA authentication: %d", | |
387 | /* Otherwise it should have sent a challenge. */ | 369 | type); |
388 | if (type != SSH_SMSG_AUTH_RSA_CHALLENGE) | ||
389 | packet_disconnect("Protocol error during RSA authentication: %d", | ||
390 | type); | ||
391 | |||
392 | packet_get_bignum(challenge, &clen); | ||
393 | |||
394 | packet_integrity_check(plen, clen, type); | ||
395 | |||
396 | debug("Received RSA challenge from server."); | ||
397 | |||
398 | /* Ask the agent to decrypt the challenge. */ | ||
399 | if (!ssh_decrypt_challenge(auth, e, n, challenge, | ||
400 | session_id, 1, response)) | ||
401 | { | ||
402 | /* The agent failed to authenticate this identifier although it | ||
403 | advertised it supports this. Just return a wrong value. */ | ||
404 | log("Authentication agent failed to decrypt challenge."); | ||
405 | memset(response, 0, sizeof(response)); | ||
406 | } | ||
407 | |||
408 | debug("Sending response to RSA challenge."); | ||
409 | |||
410 | /* Send the decrypted challenge back to the server. */ | ||
411 | packet_start(SSH_CMSG_AUTH_RSA_RESPONSE); | ||
412 | for (i = 0; i < 16; i++) | ||
413 | packet_put_char(response[i]); | ||
414 | packet_send(); | ||
415 | packet_write_wait(); | ||
416 | |||
417 | /* Wait for response from the server. */ | ||
418 | type = packet_read(&plen); | ||
419 | |||
420 | /* The server returns success if it accepted the authentication. */ | ||
421 | if (type == SSH_SMSG_SUCCESS) | ||
422 | { | ||
423 | debug("RSA authentication accepted by server."); | ||
424 | BN_clear_free(e); | ||
425 | BN_clear_free(n); | ||
426 | BN_clear_free(challenge); | ||
427 | return 1; | ||
428 | } | ||
429 | 370 | ||
430 | /* Otherwise it should return failure. */ | 371 | packet_get_bignum(challenge, &clen); |
431 | if (type != SSH_SMSG_FAILURE) | ||
432 | packet_disconnect("Protocol error waiting RSA auth response: %d", | ||
433 | type); | ||
434 | } | ||
435 | 372 | ||
436 | BN_clear_free(e); | 373 | packet_integrity_check(plen, clen, type); |
437 | BN_clear_free(n); | ||
438 | BN_clear_free(challenge); | ||
439 | 374 | ||
440 | debug("RSA authentication using agent refused."); | 375 | debug("Received RSA challenge from server."); |
441 | return 0; | 376 | |
442 | } | 377 | /* Ask the agent to decrypt the challenge. */ |
378 | if (!ssh_decrypt_challenge(auth, e, n, challenge, | ||
379 | session_id, 1, response)) { | ||
380 | /* The agent failed to authenticate this identifier although it | ||
381 | advertised it supports this. Just return a wrong value. */ | ||
382 | log("Authentication agent failed to decrypt challenge."); | ||
383 | memset(response, 0, sizeof(response)); | ||
384 | } | ||
385 | debug("Sending response to RSA challenge."); | ||
386 | |||
387 | /* Send the decrypted challenge back to the server. */ | ||
388 | packet_start(SSH_CMSG_AUTH_RSA_RESPONSE); | ||
389 | for (i = 0; i < 16; i++) | ||
390 | packet_put_char(response[i]); | ||
391 | packet_send(); | ||
392 | packet_write_wait(); | ||
393 | |||
394 | /* Wait for response from the server. */ | ||
395 | type = packet_read(&plen); | ||
396 | |||
397 | /* The server returns success if it accepted the authentication. */ | ||
398 | if (type == SSH_SMSG_SUCCESS) { | ||
399 | debug("RSA authentication accepted by server."); | ||
400 | BN_clear_free(e); | ||
401 | BN_clear_free(n); | ||
402 | BN_clear_free(challenge); | ||
403 | return 1; | ||
404 | } | ||
405 | /* Otherwise it should return failure. */ | ||
406 | if (type != SSH_SMSG_FAILURE) | ||
407 | packet_disconnect("Protocol error waiting RSA auth response: %d", | ||
408 | type); | ||
409 | } | ||
410 | |||
411 | BN_clear_free(e); | ||
412 | BN_clear_free(n); | ||
413 | BN_clear_free(challenge); | ||
443 | 414 | ||
444 | /* Computes the proper response to a RSA challenge, and sends the response to | 415 | debug("RSA authentication using agent refused."); |
445 | the server. */ | 416 | return 0; |
417 | } | ||
446 | 418 | ||
419 | /* | ||
420 | * Computes the proper response to a RSA challenge, and sends the response to | ||
421 | * the server. | ||
422 | */ | ||
447 | void | 423 | void |
448 | respond_to_rsa_challenge(BIGNUM *challenge, RSA *prv) | 424 | respond_to_rsa_challenge(BIGNUM * challenge, RSA * prv) |
449 | { | 425 | { |
450 | unsigned char buf[32], response[16]; | 426 | unsigned char buf[32], response[16]; |
451 | MD5_CTX md; | 427 | MD5_CTX md; |
452 | int i, len; | 428 | int i, len; |
453 | 429 | ||
454 | /* Decrypt the challenge using the private key. */ | 430 | /* Decrypt the challenge using the private key. */ |
455 | rsa_private_decrypt(challenge, challenge, prv); | 431 | rsa_private_decrypt(challenge, challenge, prv); |
456 | 432 | ||
457 | /* Compute the response. */ | 433 | /* Compute the response. */ |
458 | /* The response is MD5 of decrypted challenge plus session id. */ | 434 | /* The response is MD5 of decrypted challenge plus session id. */ |
459 | len = BN_num_bytes(challenge); | 435 | len = BN_num_bytes(challenge); |
460 | if (len <= 0 || len > sizeof(buf)) | 436 | if (len <= 0 || len > sizeof(buf)) |
461 | packet_disconnect("respond_to_rsa_challenge: bad challenge length %d", | 437 | packet_disconnect("respond_to_rsa_challenge: bad challenge length %d", |
462 | len); | 438 | len); |
463 | 439 | ||
464 | memset(buf, 0, sizeof(buf)); | 440 | memset(buf, 0, sizeof(buf)); |
465 | BN_bn2bin(challenge, buf + sizeof(buf) - len); | 441 | BN_bn2bin(challenge, buf + sizeof(buf) - len); |
466 | MD5_Init(&md); | 442 | MD5_Init(&md); |
467 | MD5_Update(&md, buf, 32); | 443 | MD5_Update(&md, buf, 32); |
468 | MD5_Update(&md, session_id, 16); | 444 | MD5_Update(&md, session_id, 16); |
469 | MD5_Final(response, &md); | 445 | MD5_Final(response, &md); |
470 | 446 | ||
471 | debug("Sending response to host key RSA challenge."); | 447 | debug("Sending response to host key RSA challenge."); |
472 | 448 | ||
473 | /* Send the response back to the server. */ | 449 | /* Send the response back to the server. */ |
474 | packet_start(SSH_CMSG_AUTH_RSA_RESPONSE); | 450 | packet_start(SSH_CMSG_AUTH_RSA_RESPONSE); |
475 | for (i = 0; i < 16; i++) | 451 | for (i = 0; i < 16; i++) |
476 | packet_put_char(response[i]); | 452 | packet_put_char(response[i]); |
477 | packet_send(); | 453 | packet_send(); |
478 | packet_write_wait(); | 454 | packet_write_wait(); |
479 | |||
480 | memset(buf, 0, sizeof(buf)); | ||
481 | memset(response, 0, sizeof(response)); | ||
482 | memset(&md, 0, sizeof(md)); | ||
483 | } | ||
484 | 455 | ||
485 | /* Checks if the user has authentication file, and if so, tries to authenticate | 456 | memset(buf, 0, sizeof(buf)); |
486 | the user using it. */ | 457 | memset(response, 0, sizeof(response)); |
458 | memset(&md, 0, sizeof(md)); | ||
459 | } | ||
487 | 460 | ||
461 | /* | ||
462 | * Checks if the user has authentication file, and if so, tries to authenticate | ||
463 | * the user using it. | ||
464 | */ | ||
488 | int | 465 | int |
489 | try_rsa_authentication(struct passwd *pw, const char *authfile) | 466 | try_rsa_authentication(struct passwd * pw, const char *authfile) |
490 | { | 467 | { |
491 | extern Options options; | 468 | extern Options options; |
492 | BIGNUM *challenge; | 469 | BIGNUM *challenge; |
493 | RSA *private_key; | 470 | RSA *private_key; |
494 | RSA *public_key; | 471 | RSA *public_key; |
495 | char *passphrase, *comment; | 472 | char *passphrase, *comment; |
496 | int type, i; | 473 | int type, i; |
497 | int plen, clen; | 474 | int plen, clen; |
498 | 475 | ||
499 | /* Try to load identification for the authentication key. */ | 476 | /* Try to load identification for the authentication key. */ |
500 | public_key = RSA_new(); | 477 | public_key = RSA_new(); |
501 | if (!load_public_key(authfile, public_key, &comment)) { | 478 | if (!load_public_key(authfile, public_key, &comment)) { |
502 | RSA_free(public_key); | 479 | RSA_free(public_key); |
503 | return 0; /* Could not load it. Fail. */ | 480 | return 0; /* Could not load it. Fail. */ |
504 | } | ||
505 | |||
506 | debug("Trying RSA authentication with key '%.100s'", comment); | ||
507 | |||
508 | /* Tell the server that we are willing to authenticate using this key. */ | ||
509 | packet_start(SSH_CMSG_AUTH_RSA); | ||
510 | packet_put_bignum(public_key->n); | ||
511 | packet_send(); | ||
512 | packet_write_wait(); | ||
513 | |||
514 | /* We no longer need the public key. */ | ||
515 | RSA_free(public_key); | ||
516 | |||
517 | /* Wait for server's response. */ | ||
518 | type = packet_read(&plen); | ||
519 | |||
520 | /* The server responds with failure if it doesn\'t like our key or doesn\'t | ||
521 | support RSA authentication. */ | ||
522 | if (type == SSH_SMSG_FAILURE) | ||
523 | { | ||
524 | debug("Server refused our key."); | ||
525 | xfree(comment); | ||
526 | return 0; /* Server refuses to authenticate with this key. */ | ||
527 | } | ||
528 | |||
529 | /* Otherwise, the server should respond with a challenge. */ | ||
530 | if (type != SSH_SMSG_AUTH_RSA_CHALLENGE) | ||
531 | packet_disconnect("Protocol error during RSA authentication: %d", type); | ||
532 | |||
533 | /* Get the challenge from the packet. */ | ||
534 | challenge = BN_new(); | ||
535 | packet_get_bignum(challenge, &clen); | ||
536 | |||
537 | packet_integrity_check(plen, clen, type); | ||
538 | |||
539 | debug("Received RSA challenge from server."); | ||
540 | |||
541 | private_key = RSA_new(); | ||
542 | /* Load the private key. Try first with empty passphrase; if it fails, | ||
543 | ask for a passphrase. */ | ||
544 | if (!load_private_key(authfile, "", private_key, NULL)) | ||
545 | { | ||
546 | char buf[300]; | ||
547 | /* Request passphrase from the user. We read from /dev/tty to make | ||
548 | this work even if stdin has been redirected. If running in | ||
549 | batch mode, we just use the empty passphrase, which will fail and | ||
550 | return. */ | ||
551 | snprintf(buf, sizeof buf, | ||
552 | "Enter passphrase for RSA key '%.100s': ", comment); | ||
553 | if (!options.batch_mode) | ||
554 | passphrase = read_passphrase(buf, 0); | ||
555 | else | ||
556 | { | ||
557 | debug("Will not query passphrase for %.100s in batch mode.", | ||
558 | comment); | ||
559 | passphrase = xstrdup(""); | ||
560 | } | 481 | } |
561 | 482 | debug("Trying RSA authentication with key '%.100s'", comment); | |
562 | /* Load the authentication file using the pasphrase. */ | 483 | |
563 | if (!load_private_key(authfile, passphrase, private_key, NULL)) | 484 | /* Tell the server that we are willing to authenticate using this key. */ |
564 | { | 485 | packet_start(SSH_CMSG_AUTH_RSA); |
565 | memset(passphrase, 0, strlen(passphrase)); | 486 | packet_put_bignum(public_key->n); |
566 | xfree(passphrase); | 487 | packet_send(); |
567 | error("Bad passphrase."); | 488 | packet_write_wait(); |
568 | 489 | ||
569 | /* Send a dummy response packet to avoid protocol error. */ | 490 | /* We no longer need the public key. */ |
570 | packet_start(SSH_CMSG_AUTH_RSA_RESPONSE); | 491 | RSA_free(public_key); |
571 | for (i = 0; i < 16; i++) | 492 | |
572 | packet_put_char(0); | 493 | /* Wait for server's response. */ |
573 | packet_send(); | 494 | type = packet_read(&plen); |
574 | packet_write_wait(); | 495 | |
575 | 496 | /* The server responds with failure if it doesn\'t like our key or | |
576 | /* Expect the server to reject it... */ | 497 | doesn\'t support RSA authentication. */ |
577 | packet_read_expect(&plen, SSH_SMSG_FAILURE); | 498 | if (type == SSH_SMSG_FAILURE) { |
578 | xfree(comment); | 499 | debug("Server refused our key."); |
579 | return 0; | 500 | xfree(comment); |
501 | return 0; /* Server refuses to authenticate with | ||
502 | this key. */ | ||
580 | } | 503 | } |
504 | /* Otherwise, the server should respond with a challenge. */ | ||
505 | if (type != SSH_SMSG_AUTH_RSA_CHALLENGE) | ||
506 | packet_disconnect("Protocol error during RSA authentication: %d", type); | ||
507 | |||
508 | /* Get the challenge from the packet. */ | ||
509 | challenge = BN_new(); | ||
510 | packet_get_bignum(challenge, &clen); | ||
511 | |||
512 | packet_integrity_check(plen, clen, type); | ||
513 | |||
514 | debug("Received RSA challenge from server."); | ||
515 | |||
516 | private_key = RSA_new(); | ||
517 | /* Load the private key. Try first with empty passphrase; if it | ||
518 | fails, ask for a passphrase. */ | ||
519 | if (!load_private_key(authfile, "", private_key, NULL)) { | ||
520 | char buf[300]; | ||
521 | snprintf(buf, sizeof buf, "Enter passphrase for RSA key '%.100s': ", | ||
522 | comment); | ||
523 | if (!options.batch_mode) | ||
524 | passphrase = read_passphrase(buf, 0); | ||
525 | else { | ||
526 | debug("Will not query passphrase for %.100s in batch mode.", | ||
527 | comment); | ||
528 | passphrase = xstrdup(""); | ||
529 | } | ||
581 | 530 | ||
582 | /* Destroy the passphrase. */ | 531 | /* Load the authentication file using the pasphrase. */ |
583 | memset(passphrase, 0, strlen(passphrase)); | 532 | if (!load_private_key(authfile, passphrase, private_key, NULL)) { |
584 | xfree(passphrase); | 533 | memset(passphrase, 0, strlen(passphrase)); |
585 | } | 534 | xfree(passphrase); |
586 | 535 | error("Bad passphrase."); | |
587 | /* We no longer need the comment. */ | 536 | |
588 | xfree(comment); | 537 | /* Send a dummy response packet to avoid protocol error. */ |
589 | 538 | packet_start(SSH_CMSG_AUTH_RSA_RESPONSE); | |
590 | /* Compute and send a response to the challenge. */ | 539 | for (i = 0; i < 16; i++) |
591 | respond_to_rsa_challenge(challenge, private_key); | 540 | packet_put_char(0); |
592 | 541 | packet_send(); | |
593 | /* Destroy the private key. */ | 542 | packet_write_wait(); |
594 | RSA_free(private_key); | 543 | |
595 | 544 | /* Expect the server to reject it... */ | |
596 | /* We no longer need the challenge. */ | 545 | packet_read_expect(&plen, SSH_SMSG_FAILURE); |
597 | BN_clear_free(challenge); | 546 | xfree(comment); |
598 | 547 | return 0; | |
599 | /* Wait for response from the server. */ | 548 | } |
600 | type = packet_read(&plen); | 549 | /* Destroy the passphrase. */ |
601 | if (type == SSH_SMSG_SUCCESS) | 550 | memset(passphrase, 0, strlen(passphrase)); |
602 | { | 551 | xfree(passphrase); |
603 | debug("RSA authentication accepted by server."); | 552 | } |
604 | return 1; | 553 | /* We no longer need the comment. */ |
605 | } | 554 | xfree(comment); |
606 | if (type != SSH_SMSG_FAILURE) | 555 | |
607 | packet_disconnect("Protocol error waiting RSA auth response: %d", type); | 556 | /* Compute and send a response to the challenge. */ |
608 | debug("RSA authentication refused."); | 557 | respond_to_rsa_challenge(challenge, private_key); |
609 | return 0; | 558 | |
610 | } | 559 | /* Destroy the private key. */ |
560 | RSA_free(private_key); | ||
611 | 561 | ||
612 | /* Tries to authenticate the user using combined rhosts or /etc/hosts.equiv | 562 | /* We no longer need the challenge. */ |
613 | authentication and RSA host authentication. */ | 563 | BN_clear_free(challenge); |
564 | |||
565 | /* Wait for response from the server. */ | ||
566 | type = packet_read(&plen); | ||
567 | if (type == SSH_SMSG_SUCCESS) { | ||
568 | debug("RSA authentication accepted by server."); | ||
569 | return 1; | ||
570 | } | ||
571 | if (type != SSH_SMSG_FAILURE) | ||
572 | packet_disconnect("Protocol error waiting RSA auth response: %d", type); | ||
573 | debug("RSA authentication refused."); | ||
574 | return 0; | ||
575 | } | ||
614 | 576 | ||
577 | /* | ||
578 | * Tries to authenticate the user using combined rhosts or /etc/hosts.equiv | ||
579 | * authentication and RSA host authentication. | ||
580 | */ | ||
615 | int | 581 | int |
616 | try_rhosts_rsa_authentication(const char *local_user, RSA *host_key) | 582 | try_rhosts_rsa_authentication(const char *local_user, RSA * host_key) |
617 | { | 583 | { |
618 | int type; | 584 | int type; |
619 | BIGNUM *challenge; | 585 | BIGNUM *challenge; |
620 | int plen, clen; | 586 | int plen, clen; |
621 | 587 | ||
622 | debug("Trying rhosts or /etc/hosts.equiv with RSA host authentication."); | 588 | debug("Trying rhosts or /etc/hosts.equiv with RSA host authentication."); |
623 | 589 | ||
624 | /* Tell the server that we are willing to authenticate using this key. */ | 590 | /* Tell the server that we are willing to authenticate using this key. */ |
625 | packet_start(SSH_CMSG_AUTH_RHOSTS_RSA); | 591 | packet_start(SSH_CMSG_AUTH_RHOSTS_RSA); |
626 | packet_put_string(local_user, strlen(local_user)); | 592 | packet_put_string(local_user, strlen(local_user)); |
627 | packet_put_int(BN_num_bits(host_key->n)); | 593 | packet_put_int(BN_num_bits(host_key->n)); |
628 | packet_put_bignum(host_key->e); | 594 | packet_put_bignum(host_key->e); |
629 | packet_put_bignum(host_key->n); | 595 | packet_put_bignum(host_key->n); |
630 | packet_send(); | 596 | packet_send(); |
631 | packet_write_wait(); | 597 | packet_write_wait(); |
632 | 598 | ||
633 | /* Wait for server's response. */ | 599 | /* Wait for server's response. */ |
634 | type = packet_read(&plen); | 600 | type = packet_read(&plen); |
635 | 601 | ||
636 | /* The server responds with failure if it doesn't admit our .rhosts | 602 | /* The server responds with failure if it doesn't admit our |
637 | authentication or doesn't know our host key. */ | 603 | .rhosts authentication or doesn't know our host key. */ |
638 | if (type == SSH_SMSG_FAILURE) | 604 | if (type == SSH_SMSG_FAILURE) { |
639 | { | 605 | debug("Server refused our rhosts authentication or host key."); |
640 | debug("Server refused our rhosts authentication or host key."); | 606 | return 0; |
641 | return 0; /* Server refuses to authenticate us with this method. */ | 607 | } |
642 | } | 608 | /* Otherwise, the server should respond with a challenge. */ |
643 | 609 | if (type != SSH_SMSG_AUTH_RSA_CHALLENGE) | |
644 | /* Otherwise, the server should respond with a challenge. */ | 610 | packet_disconnect("Protocol error during RSA authentication: %d", type); |
645 | if (type != SSH_SMSG_AUTH_RSA_CHALLENGE) | 611 | |
646 | packet_disconnect("Protocol error during RSA authentication: %d", type); | 612 | /* Get the challenge from the packet. */ |
647 | 613 | challenge = BN_new(); | |
648 | /* Get the challenge from the packet. */ | 614 | packet_get_bignum(challenge, &clen); |
649 | challenge = BN_new(); | 615 | |
650 | packet_get_bignum(challenge, &clen); | 616 | packet_integrity_check(plen, clen, type); |
651 | 617 | ||
652 | packet_integrity_check(plen, clen, type); | 618 | debug("Received RSA challenge for host key from server."); |
653 | 619 | ||
654 | debug("Received RSA challenge for host key from server."); | 620 | /* Compute a response to the challenge. */ |
655 | 621 | respond_to_rsa_challenge(challenge, host_key); | |
656 | /* Compute a response to the challenge. */ | 622 | |
657 | respond_to_rsa_challenge(challenge, host_key); | 623 | /* We no longer need the challenge. */ |
658 | 624 | BN_clear_free(challenge); | |
659 | /* We no longer need the challenge. */ | 625 | |
660 | BN_clear_free(challenge); | 626 | /* Wait for response from the server. */ |
661 | 627 | type = packet_read(&plen); | |
662 | /* Wait for response from the server. */ | 628 | if (type == SSH_SMSG_SUCCESS) { |
663 | type = packet_read(&plen); | 629 | debug("Rhosts or /etc/hosts.equiv with RSA host authentication accepted by server."); |
664 | if (type == SSH_SMSG_SUCCESS) | 630 | return 1; |
665 | { | 631 | } |
666 | debug("Rhosts or /etc/hosts.equiv with RSA host authentication accepted by server."); | 632 | if (type != SSH_SMSG_FAILURE) |
667 | return 1; | 633 | packet_disconnect("Protocol error waiting RSA auth response: %d", type); |
668 | } | 634 | debug("Rhosts or /etc/hosts.equiv with RSA host authentication refused."); |
669 | if (type != SSH_SMSG_FAILURE) | 635 | return 0; |
670 | packet_disconnect("Protocol error waiting RSA auth response: %d", type); | ||
671 | debug("Rhosts or /etc/hosts.equiv with RSA host authentication refused."); | ||
672 | return 0; | ||
673 | } | 636 | } |
674 | 637 | ||
675 | #ifdef KRB4 | 638 | #ifdef KRB4 |
676 | int try_kerberos_authentication() | 639 | int |
640 | try_kerberos_authentication() | ||
677 | { | 641 | { |
678 | KTEXT_ST auth; /* Kerberos data */ | 642 | KTEXT_ST auth; /* Kerberos data */ |
679 | char *reply; | 643 | char *reply; |
680 | char inst[INST_SZ]; | 644 | char inst[INST_SZ]; |
681 | char *realm; | 645 | char *realm; |
682 | CREDENTIALS cred; | 646 | CREDENTIALS cred; |
683 | int r, type, plen; | 647 | int r, type, plen; |
684 | Key_schedule schedule; | 648 | Key_schedule schedule; |
685 | u_long checksum, cksum; | 649 | u_long checksum, cksum; |
686 | MSG_DAT msg_data; | 650 | MSG_DAT msg_data; |
687 | struct sockaddr_in local, foreign; | 651 | struct sockaddr_in local, foreign; |
688 | struct stat st; | 652 | struct stat st; |
689 | 653 | ||
690 | /* Don't do anything if we don't have any tickets. */ | 654 | /* Don't do anything if we don't have any tickets. */ |
691 | if (stat(tkt_string(), &st) < 0) return 0; | 655 | if (stat(tkt_string(), &st) < 0) |
692 | 656 | return 0; | |
693 | strncpy(inst, (char *) krb_get_phost(get_canonical_hostname()), INST_SZ); | 657 | |
694 | 658 | strncpy(inst, (char *) krb_get_phost(get_canonical_hostname()), INST_SZ); | |
695 | realm = (char *)krb_realmofhost(get_canonical_hostname()); | 659 | |
696 | if (!realm) { | 660 | realm = (char *) krb_realmofhost(get_canonical_hostname()); |
697 | debug("Kerberos V4: no realm for %s", get_canonical_hostname()); | 661 | if (!realm) { |
698 | return 0; | 662 | debug("Kerberos V4: no realm for %s", get_canonical_hostname()); |
699 | } | 663 | return 0; |
700 | /* This can really be anything. */ | 664 | } |
701 | checksum = (u_long) getpid(); | 665 | /* This can really be anything. */ |
702 | 666 | checksum = (u_long) getpid(); | |
703 | r = krb_mk_req(&auth, KRB4_SERVICE_NAME, inst, realm, checksum); | 667 | |
704 | if (r != KSUCCESS) { | 668 | r = krb_mk_req(&auth, KRB4_SERVICE_NAME, inst, realm, checksum); |
705 | debug("Kerberos V4 krb_mk_req failed: %s", krb_err_txt[r]); | 669 | if (r != KSUCCESS) { |
706 | return 0; | 670 | debug("Kerberos V4 krb_mk_req failed: %s", krb_err_txt[r]); |
707 | } | 671 | return 0; |
708 | /* Get session key to decrypt the server's reply with. */ | 672 | } |
709 | r = krb_get_cred(KRB4_SERVICE_NAME, inst, realm, &cred); | 673 | /* Get session key to decrypt the server's reply with. */ |
710 | if (r != KSUCCESS) { | 674 | r = krb_get_cred(KRB4_SERVICE_NAME, inst, realm, &cred); |
711 | debug("get_cred failed: %s", krb_err_txt[r]); | 675 | if (r != KSUCCESS) { |
712 | return 0; | 676 | debug("get_cred failed: %s", krb_err_txt[r]); |
713 | } | 677 | return 0; |
714 | des_key_sched((des_cblock *)cred.session, schedule); | 678 | } |
715 | 679 | des_key_sched((des_cblock *) cred.session, schedule); | |
716 | /* Send authentication info to server. */ | 680 | |
717 | packet_start(SSH_CMSG_AUTH_KERBEROS); | 681 | /* Send authentication info to server. */ |
718 | packet_put_string((char *)auth.dat, auth.length); | 682 | packet_start(SSH_CMSG_AUTH_KERBEROS); |
719 | packet_send(); | 683 | packet_put_string((char *) auth.dat, auth.length); |
720 | packet_write_wait(); | 684 | packet_send(); |
721 | 685 | packet_write_wait(); | |
722 | /* Zero the buffer. */ | 686 | |
723 | (void) memset(auth.dat, 0, MAX_KTXT_LEN); | 687 | /* Zero the buffer. */ |
724 | 688 | (void) memset(auth.dat, 0, MAX_KTXT_LEN); | |
725 | r = sizeof(local); | 689 | |
726 | memset(&local, 0, sizeof(local)); | 690 | r = sizeof(local); |
727 | if (getsockname(packet_get_connection_in(), | 691 | memset(&local, 0, sizeof(local)); |
728 | (struct sockaddr *) &local, &r) < 0) | 692 | if (getsockname(packet_get_connection_in(), |
729 | debug("getsockname failed: %s", strerror(errno)); | 693 | (struct sockaddr *) & local, &r) < 0) |
730 | 694 | debug("getsockname failed: %s", strerror(errno)); | |
731 | r = sizeof(foreign); | 695 | |
732 | memset(&foreign, 0, sizeof(foreign)); | 696 | r = sizeof(foreign); |
733 | if (getpeername(packet_get_connection_in(), | 697 | memset(&foreign, 0, sizeof(foreign)); |
734 | (struct sockaddr *)&foreign, &r) < 0) { | 698 | if (getpeername(packet_get_connection_in(), |
735 | debug("getpeername failed: %s", strerror(errno)); | 699 | (struct sockaddr *) & foreign, &r) < 0) { |
736 | fatal_cleanup(); | 700 | debug("getpeername failed: %s", strerror(errno)); |
737 | } | 701 | fatal_cleanup(); |
738 | 702 | } | |
739 | /* Get server reply. */ | 703 | /* Get server reply. */ |
740 | type = packet_read(&plen); | 704 | type = packet_read(&plen); |
741 | switch(type) { | 705 | switch (type) { |
742 | 706 | case SSH_SMSG_FAILURE: | |
743 | case SSH_SMSG_FAILURE: /* Should really be SSH_SMSG_AUTH_KERBEROS_FAILURE */ | 707 | /* Should really be SSH_SMSG_AUTH_KERBEROS_FAILURE */ |
744 | debug("Kerberos V4 authentication failed."); | 708 | debug("Kerberos V4 authentication failed."); |
745 | return 0; | 709 | return 0; |
746 | break; | 710 | break; |
747 | 711 | ||
748 | case SSH_SMSG_AUTH_KERBEROS_RESPONSE: /* SSH_SMSG_AUTH_KERBEROS_SUCCESS */ | 712 | case SSH_SMSG_AUTH_KERBEROS_RESPONSE: |
749 | debug("Kerberos V4 authentication accepted."); | 713 | /* SSH_SMSG_AUTH_KERBEROS_SUCCESS */ |
750 | 714 | debug("Kerberos V4 authentication accepted."); | |
751 | /* Get server's response. */ | 715 | |
752 | reply = packet_get_string((unsigned int *)&auth.length); | 716 | /* Get server's response. */ |
753 | memcpy(auth.dat, reply, auth.length); | 717 | reply = packet_get_string((unsigned int *) &auth.length); |
754 | xfree(reply); | 718 | memcpy(auth.dat, reply, auth.length); |
755 | 719 | xfree(reply); | |
756 | packet_integrity_check(plen, 4 + auth.length, type); | 720 | |
757 | 721 | packet_integrity_check(plen, 4 + auth.length, type); | |
758 | /* If his response isn't properly encrypted with the session key, | 722 | |
759 | and the decrypted checksum fails to match, he's bogus. Bail out. */ | 723 | /* If his response isn't properly encrypted with the |
760 | r = krb_rd_priv(auth.dat, auth.length, schedule, &cred.session, | 724 | session key, and the decrypted checksum fails to match, |
761 | &foreign, &local, &msg_data); | 725 | he's bogus. Bail out. */ |
762 | if (r != KSUCCESS) { | 726 | r = krb_rd_priv(auth.dat, auth.length, schedule, &cred.session, |
763 | debug("Kerberos V4 krb_rd_priv failed: %s", krb_err_txt[r]); | 727 | &foreign, &local, &msg_data); |
764 | packet_disconnect("Kerberos V4 challenge failed!"); | 728 | if (r != KSUCCESS) { |
765 | } | 729 | debug("Kerberos V4 krb_rd_priv failed: %s", krb_err_txt[r]); |
766 | /* Fetch the (incremented) checksum that we supplied in the request. */ | 730 | packet_disconnect("Kerberos V4 challenge failed!"); |
767 | (void)memcpy((char *)&cksum, (char *)msg_data.app_data, sizeof(cksum)); | 731 | } |
768 | cksum = ntohl(cksum); | 732 | /* Fetch the (incremented) checksum that we supplied in the request. */ |
769 | 733 | (void) memcpy((char *) &cksum, (char *) msg_data.app_data, sizeof(cksum)); | |
770 | /* If it matches, we're golden. */ | 734 | cksum = ntohl(cksum); |
771 | if (cksum == checksum + 1) { | 735 | |
772 | debug("Kerberos V4 challenge successful."); | 736 | /* If it matches, we're golden. */ |
773 | return 1; | 737 | if (cksum == checksum + 1) { |
774 | } | 738 | debug("Kerberos V4 challenge successful."); |
775 | else | 739 | return 1; |
776 | packet_disconnect("Kerberos V4 challenge failed!"); | 740 | } else |
777 | break; | 741 | packet_disconnect("Kerberos V4 challenge failed!"); |
778 | 742 | break; | |
779 | default: | 743 | |
780 | packet_disconnect("Protocol error on Kerberos V4 response: %d", type); | 744 | default: |
781 | } | 745 | packet_disconnect("Protocol error on Kerberos V4 response: %d", type); |
782 | return 0; | 746 | } |
747 | return 0; | ||
783 | } | 748 | } |
749 | |||
784 | #endif /* KRB4 */ | 750 | #endif /* KRB4 */ |
785 | 751 | ||
786 | #ifdef AFS | 752 | #ifdef AFS |
787 | int send_kerberos_tgt() | 753 | int |
754 | send_kerberos_tgt() | ||
788 | { | 755 | { |
789 | CREDENTIALS *creds; | 756 | CREDENTIALS *creds; |
790 | char pname[ANAME_SZ], pinst[INST_SZ], prealm[REALM_SZ]; | 757 | char pname[ANAME_SZ], pinst[INST_SZ], prealm[REALM_SZ]; |
791 | int r, type, plen; | 758 | int r, type, plen; |
792 | unsigned char buffer[8192]; | 759 | unsigned char buffer[8192]; |
793 | struct stat st; | 760 | struct stat st; |
794 | 761 | ||
795 | /* Don't do anything if we don't have any tickets. */ | 762 | /* Don't do anything if we don't have any tickets. */ |
796 | if (stat(tkt_string(), &st) < 0) return 0; | 763 | if (stat(tkt_string(), &st) < 0) |
797 | 764 | return 0; | |
798 | creds = xmalloc(sizeof(*creds)); | 765 | |
799 | 766 | creds = xmalloc(sizeof(*creds)); | |
800 | if ((r = krb_get_tf_fullname(TKT_FILE, pname, pinst, prealm)) != KSUCCESS) { | 767 | |
801 | debug("Kerberos V4 tf_fullname failed: %s",krb_err_txt[r]); | 768 | if ((r = krb_get_tf_fullname(TKT_FILE, pname, pinst, prealm)) != KSUCCESS) { |
802 | return 0; | 769 | debug("Kerberos V4 tf_fullname failed: %s", krb_err_txt[r]); |
803 | } | 770 | return 0; |
804 | if ((r = krb_get_cred("krbtgt", prealm, prealm, creds)) != GC_OK) { | 771 | } |
805 | debug("Kerberos V4 get_cred failed: %s", krb_err_txt[r]); | 772 | if ((r = krb_get_cred("krbtgt", prealm, prealm, creds)) != GC_OK) { |
806 | return 0; | 773 | debug("Kerberos V4 get_cred failed: %s", krb_err_txt[r]); |
807 | } | 774 | return 0; |
808 | if (time(0) > krb_life_to_time(creds->issue_date, creds->lifetime)) { | 775 | } |
809 | debug("Kerberos V4 ticket expired: %s", TKT_FILE); | 776 | if (time(0) > krb_life_to_time(creds->issue_date, creds->lifetime)) { |
810 | return 0; | 777 | debug("Kerberos V4 ticket expired: %s", TKT_FILE); |
811 | } | 778 | return 0; |
812 | 779 | } | |
813 | creds_to_radix(creds, buffer); | 780 | creds_to_radix(creds, buffer); |
814 | xfree(creds); | 781 | xfree(creds); |
815 | 782 | ||
816 | packet_start(SSH_CMSG_HAVE_KERBEROS_TGT); | 783 | packet_start(SSH_CMSG_HAVE_KERBEROS_TGT); |
817 | packet_put_string((char *)buffer, strlen(buffer)); | 784 | packet_put_string((char *) buffer, strlen(buffer)); |
818 | packet_send(); | 785 | packet_send(); |
819 | packet_write_wait(); | 786 | packet_write_wait(); |
820 | 787 | ||
821 | type = packet_read(&plen); | 788 | type = packet_read(&plen); |
822 | 789 | ||
823 | if (type == SSH_SMSG_FAILURE) | 790 | if (type == SSH_SMSG_FAILURE) |
824 | debug("Kerberos TGT for realm %s rejected.", prealm); | 791 | debug("Kerberos TGT for realm %s rejected.", prealm); |
825 | else if (type != SSH_SMSG_SUCCESS) | 792 | else if (type != SSH_SMSG_SUCCESS) |
826 | packet_disconnect("Protocol error on Kerberos TGT response: %d", type); | 793 | packet_disconnect("Protocol error on Kerberos TGT response: %d", type); |
827 | 794 | ||
828 | return 1; | 795 | return 1; |
829 | } | 796 | } |
830 | 797 | ||
831 | void send_afs_tokens(void) | 798 | void |
799 | send_afs_tokens(void) | ||
832 | { | 800 | { |
833 | CREDENTIALS creds; | 801 | CREDENTIALS creds; |
834 | struct ViceIoctl parms; | 802 | struct ViceIoctl parms; |
835 | struct ClearToken ct; | 803 | struct ClearToken ct; |
836 | int i, type, len, plen; | 804 | int i, type, len, plen; |
837 | char buf[2048], *p, *server_cell; | 805 | char buf[2048], *p, *server_cell; |
838 | unsigned char buffer[8192]; | 806 | unsigned char buffer[8192]; |
839 | 807 | ||
840 | /* Move over ktc_GetToken, here's something leaner. */ | 808 | /* Move over ktc_GetToken, here's something leaner. */ |
841 | for (i = 0; i < 100; i++) { /* just in case */ | 809 | for (i = 0; i < 100; i++) { /* just in case */ |
842 | parms.in = (char *)&i; | 810 | parms.in = (char *) &i; |
843 | parms.in_size = sizeof(i); | 811 | parms.in_size = sizeof(i); |
844 | parms.out = buf; | 812 | parms.out = buf; |
845 | parms.out_size = sizeof(buf); | 813 | parms.out_size = sizeof(buf); |
846 | if (k_pioctl(0, VIOCGETTOK, &parms, 0) != 0) break; | 814 | if (k_pioctl(0, VIOCGETTOK, &parms, 0) != 0) |
847 | p = buf; | 815 | break; |
848 | 816 | p = buf; | |
849 | /* Get secret token. */ | 817 | |
850 | memcpy(&creds.ticket_st.length, p, sizeof(unsigned int)); | 818 | /* Get secret token. */ |
851 | if (creds.ticket_st.length > MAX_KTXT_LEN) break; | 819 | memcpy(&creds.ticket_st.length, p, sizeof(unsigned int)); |
852 | p += sizeof(unsigned int); | 820 | if (creds.ticket_st.length > MAX_KTXT_LEN) |
853 | memcpy(creds.ticket_st.dat, p, creds.ticket_st.length); | 821 | break; |
854 | p += creds.ticket_st.length; | 822 | p += sizeof(unsigned int); |
855 | 823 | memcpy(creds.ticket_st.dat, p, creds.ticket_st.length); | |
856 | /* Get clear token. */ | 824 | p += creds.ticket_st.length; |
857 | memcpy(&len, p, sizeof(len)); | 825 | |
858 | if (len != sizeof(struct ClearToken)) break; | 826 | /* Get clear token. */ |
859 | p += sizeof(len); | 827 | memcpy(&len, p, sizeof(len)); |
860 | memcpy(&ct, p, len); | 828 | if (len != sizeof(struct ClearToken)) |
861 | p += len; | 829 | break; |
862 | p += sizeof(len); /* primary flag */ | 830 | p += sizeof(len); |
863 | server_cell = p; | 831 | memcpy(&ct, p, len); |
864 | 832 | p += len; | |
865 | /* Flesh out our credentials. */ | 833 | p += sizeof(len); /* primary flag */ |
866 | strlcpy(creds.service, "afs", sizeof creds.service); | 834 | server_cell = p; |
867 | creds.instance[0] = '\0'; | 835 | |
868 | strlcpy(creds.realm, server_cell, REALM_SZ); | 836 | /* Flesh out our credentials. */ |
869 | memcpy(creds.session, ct.HandShakeKey, DES_KEY_SZ); | 837 | strlcpy(creds.service, "afs", sizeof creds.service); |
870 | creds.issue_date = ct.BeginTimestamp; | 838 | creds.instance[0] = '\0'; |
871 | creds.lifetime = krb_time_to_life(creds.issue_date, ct.EndTimestamp); | 839 | strlcpy(creds.realm, server_cell, REALM_SZ); |
872 | creds.kvno = ct.AuthHandle; | 840 | memcpy(creds.session, ct.HandShakeKey, DES_KEY_SZ); |
873 | snprintf(creds.pname, sizeof(creds.pname), "AFS ID %d", ct.ViceId); | 841 | creds.issue_date = ct.BeginTimestamp; |
874 | creds.pinst[0] = '\0'; | 842 | creds.lifetime = krb_time_to_life(creds.issue_date, ct.EndTimestamp); |
875 | 843 | creds.kvno = ct.AuthHandle; | |
876 | /* Encode token, ship it off. */ | 844 | snprintf(creds.pname, sizeof(creds.pname), "AFS ID %d", ct.ViceId); |
877 | if (!creds_to_radix(&creds, buffer)) break; | 845 | creds.pinst[0] = '\0'; |
878 | packet_start(SSH_CMSG_HAVE_AFS_TOKEN); | 846 | |
879 | packet_put_string((char *)buffer, strlen(buffer)); | 847 | /* Encode token, ship it off. */ |
880 | packet_send(); | 848 | if (!creds_to_radix(&creds, buffer)) |
881 | packet_write_wait(); | 849 | break; |
882 | 850 | packet_start(SSH_CMSG_HAVE_AFS_TOKEN); | |
883 | /* Roger, Roger. Clearance, Clarence. What's your vector, Victor? */ | 851 | packet_put_string((char *) buffer, strlen(buffer)); |
884 | type = packet_read(&plen); | 852 | packet_send(); |
885 | 853 | packet_write_wait(); | |
886 | if (type == SSH_SMSG_FAILURE) | 854 | |
887 | debug("AFS token for cell %s rejected.", server_cell); | 855 | /* Roger, Roger. Clearance, Clarence. What's your vector, |
888 | else if (type != SSH_SMSG_SUCCESS) | 856 | Victor? */ |
889 | packet_disconnect("Protocol error on AFS token response: %d", type); | 857 | type = packet_read(&plen); |
890 | } | 858 | |
859 | if (type == SSH_SMSG_FAILURE) | ||
860 | debug("AFS token for cell %s rejected.", server_cell); | ||
861 | else if (type != SSH_SMSG_SUCCESS) | ||
862 | packet_disconnect("Protocol error on AFS token response: %d", type); | ||
863 | } | ||
891 | } | 864 | } |
892 | #endif /* AFS */ | ||
893 | 865 | ||
894 | /* Waits for the server identification string, and sends our own identification | 866 | #endif /* AFS */ |
895 | string. */ | ||
896 | 867 | ||
897 | void ssh_exchange_identification() | 868 | /* |
869 | * Waits for the server identification string, and sends our own | ||
870 | * identification string. | ||
871 | */ | ||
872 | void | ||
873 | ssh_exchange_identification() | ||
898 | { | 874 | { |
899 | char buf[256], remote_version[256]; /* must be same size! */ | 875 | char buf[256], remote_version[256]; /* must be same size! */ |
900 | int remote_major, remote_minor, i; | 876 | int remote_major, remote_minor, i; |
901 | int connection_in = packet_get_connection_in(); | 877 | int connection_in = packet_get_connection_in(); |
902 | int connection_out = packet_get_connection_out(); | 878 | int connection_out = packet_get_connection_out(); |
903 | extern Options options; | 879 | extern Options options; |
904 | 880 | ||
905 | /* Read other side\'s version identification. */ | 881 | /* Read other side\'s version identification. */ |
906 | for (i = 0; i < sizeof(buf) - 1; i++) | 882 | for (i = 0; i < sizeof(buf) - 1; i++) { |
907 | { | 883 | if (read(connection_in, &buf[i], 1) != 1) |
908 | if (read(connection_in, &buf[i], 1) != 1) | 884 | fatal("ssh_exchange_identification: read: %.100s", strerror(errno)); |
909 | fatal("ssh_exchange_identification: read: %.100s", strerror(errno)); | 885 | if (buf[i] == '\r') { |
910 | if (buf[i] == '\r') | 886 | buf[i] = '\n'; |
911 | { | 887 | buf[i + 1] = 0; |
912 | buf[i] = '\n'; | 888 | break; |
913 | buf[i + 1] = 0; | 889 | } |
914 | break; | 890 | if (buf[i] == '\n') { |
891 | buf[i + 1] = 0; | ||
892 | break; | ||
893 | } | ||
915 | } | 894 | } |
916 | if (buf[i] == '\n') | 895 | buf[sizeof(buf) - 1] = 0; |
917 | { | 896 | |
918 | buf[i + 1] = 0; | 897 | /* Check that the versions match. In future this might accept |
919 | break; | 898 | several versions and set appropriate flags to handle them. */ |
899 | if (sscanf(buf, "SSH-%d.%d-%[^\n]\n", &remote_major, &remote_minor, | ||
900 | remote_version) != 3) | ||
901 | fatal("Bad remote protocol version identification: '%.100s'", buf); | ||
902 | debug("Remote protocol version %d.%d, remote software version %.100s", | ||
903 | remote_major, remote_minor, remote_version); | ||
904 | |||
905 | /* Check if the remote protocol version is too old. */ | ||
906 | if (remote_major == 1 && remote_minor < 3) | ||
907 | fatal("Remote machine has too old SSH software version."); | ||
908 | |||
909 | /* We speak 1.3, too. */ | ||
910 | if (remote_major == 1 && remote_minor == 3) { | ||
911 | enable_compat13(); | ||
912 | if (options.forward_agent && strcmp(remote_version, SSH_VERSION) != 0) { | ||
913 | log("Agent forwarding disabled, remote version '%s' is not compatible.", | ||
914 | remote_version); | ||
915 | options.forward_agent = 0; | ||
916 | } | ||
920 | } | 917 | } |
921 | } | ||
922 | buf[sizeof(buf) - 1] = 0; | ||
923 | |||
924 | /* Check that the versions match. In future this might accept several | ||
925 | versions and set appropriate flags to handle them. */ | ||
926 | if (sscanf(buf, "SSH-%d.%d-%[^\n]\n", &remote_major, &remote_minor, | ||
927 | remote_version) != 3) | ||
928 | fatal("Bad remote protocol version identification: '%.100s'", buf); | ||
929 | debug("Remote protocol version %d.%d, remote software version %.100s", | ||
930 | remote_major, remote_minor, remote_version); | ||
931 | |||
932 | /* Check if the remote protocol version is too old. */ | ||
933 | if (remote_major == 1 && remote_minor < 3) | ||
934 | fatal("Remote machine has too old SSH software version."); | ||
935 | |||
936 | /* We speak 1.3, too. */ | ||
937 | if (remote_major == 1 && remote_minor == 3) { | ||
938 | enable_compat13(); | ||
939 | if (options.forward_agent && strcmp(remote_version, SSH_VERSION) != 0) { | ||
940 | log("Agent forwarding disabled, remote version '%s' is not compatible.", | ||
941 | remote_version); | ||
942 | options.forward_agent = 0; | ||
943 | } | ||
944 | } | ||
945 | #if 0 | 918 | #if 0 |
946 | /* Removed for now, to permit compatibility with latter versions. The server | 919 | /* Removed for now, to permit compatibility with latter versions. |
947 | will reject our version and disconnect if it doesn't support it. */ | 920 | The server will reject our version and disconnect if it doesn't |
948 | if (remote_major != PROTOCOL_MAJOR) | 921 | support it. */ |
949 | fatal("Protocol major versions differ: %d vs. %d", | 922 | if (remote_major != PROTOCOL_MAJOR) |
950 | PROTOCOL_MAJOR, remote_major); | 923 | fatal("Protocol major versions differ: %d vs. %d", |
924 | PROTOCOL_MAJOR, remote_major); | ||
951 | #endif | 925 | #endif |
952 | 926 | ||
953 | /* Send our own protocol version identification. */ | 927 | /* Send our own protocol version identification. */ |
954 | snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", | 928 | snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", |
955 | PROTOCOL_MAJOR, PROTOCOL_MINOR, SSH_VERSION); | 929 | PROTOCOL_MAJOR, PROTOCOL_MINOR, SSH_VERSION); |
956 | if (write(connection_out, buf, strlen(buf)) != strlen(buf)) | 930 | if (write(connection_out, buf, strlen(buf)) != strlen(buf)) |
957 | fatal("write: %.100s", strerror(errno)); | 931 | fatal("write: %.100s", strerror(errno)); |
958 | } | 932 | } |
959 | 933 | ||
960 | int ssh_cipher_default = SSH_CIPHER_3DES; | 934 | int ssh_cipher_default = SSH_CIPHER_3DES; |
961 | 935 | ||
962 | int read_yes_or_no(const char *prompt, int defval) | 936 | int |
937 | read_yes_or_no(const char *prompt, int defval) | ||
963 | { | 938 | { |
964 | char buf[1024]; | 939 | char buf[1024]; |
965 | FILE *f; | 940 | FILE *f; |
966 | int retval = -1; | 941 | int retval = -1; |
967 | 942 | ||
968 | if (isatty(0)) | 943 | if (isatty(0)) |
969 | f = stdin; | 944 | f = stdin; |
970 | else | 945 | else |
971 | f = fopen("/dev/tty", "rw"); | 946 | f = fopen("/dev/tty", "rw"); |
972 | 947 | ||
973 | if (f == NULL) | 948 | if (f == NULL) |
974 | return 0; | 949 | return 0; |
975 | 950 | ||
976 | fflush(stdout); | 951 | fflush(stdout); |
977 | 952 | ||
978 | while (1) | 953 | while (1) { |
979 | { | 954 | fprintf(stderr, "%s", prompt); |
980 | fprintf(stderr, "%s", prompt); | 955 | if (fgets(buf, sizeof(buf), f) == NULL) { |
981 | if (fgets(buf, sizeof(buf), f) == NULL) | 956 | /* Print a newline (the prompt probably didn\'t have one). */ |
982 | { | 957 | fprintf(stderr, "\n"); |
983 | /* Print a newline (the prompt probably didn\'t have one). */ | 958 | strlcpy(buf, "no", sizeof buf); |
984 | fprintf(stderr, "\n"); | 959 | } |
985 | strlcpy(buf, "no", sizeof buf); | 960 | /* Remove newline from response. */ |
986 | } | 961 | if (strchr(buf, '\n')) |
987 | /* Remove newline from response. */ | 962 | *strchr(buf, '\n') = 0; |
988 | if (strchr(buf, '\n')) | 963 | |
989 | *strchr(buf, '\n') = 0; | 964 | if (buf[0] == 0) |
990 | 965 | retval = defval; | |
991 | if (buf[0] == 0) | 966 | if (strcmp(buf, "yes") == 0) |
992 | retval = defval; | 967 | retval = 1; |
993 | if (strcmp(buf, "yes") == 0) | 968 | if (strcmp(buf, "no") == 0) |
994 | retval = 1; | 969 | retval = 0; |
995 | if (strcmp(buf, "no") == 0) | 970 | |
996 | retval = 0; | 971 | if (retval != -1) { |
997 | 972 | if (f != stdin) | |
998 | if (retval != -1) | 973 | fclose(f); |
999 | { | 974 | return retval; |
1000 | if (f != stdin) | 975 | } |
1001 | fclose(f); | ||
1002 | return retval; | ||
1003 | } | 976 | } |
1004 | } | ||
1005 | } | 977 | } |
1006 | 978 | ||
1007 | /* Starts a dialog with the server, and authenticates the current user on the | 979 | /* |
1008 | server. This does not need any extra privileges. The basic connection | 980 | * Starts a dialog with the server, and authenticates the current user on the |
1009 | to the server must already have been established before this is called. | 981 | * server. This does not need any extra privileges. The basic connection |
1010 | User is the remote user; if it is NULL, the current local user name will | 982 | * to the server must already have been established before this is called. |
1011 | be used. Anonymous indicates that no rhosts authentication will be used. | 983 | * User is the remote user; if it is NULL, the current local user name will |
1012 | If login fails, this function prints an error and never returns. | 984 | * be used. Anonymous indicates that no rhosts authentication will be used. |
1013 | This function does not require super-user privileges. */ | 985 | * If login fails, this function prints an error and never returns. |
1014 | 986 | * This function does not require super-user privileges. | |
1015 | void ssh_login(int host_key_valid, | 987 | */ |
1016 | RSA *own_host_key, | 988 | void |
1017 | const char *orighost, | 989 | ssh_login(int host_key_valid, |
1018 | struct sockaddr_in *hostaddr, | 990 | RSA *own_host_key, |
1019 | uid_t original_real_uid) | 991 | const char *orighost, |
992 | struct sockaddr_in *hostaddr, | ||
993 | uid_t original_real_uid) | ||
1020 | { | 994 | { |
1021 | extern Options options; | 995 | extern Options options; |
1022 | int i, type; | 996 | int i, type; |
1023 | char *password; | 997 | char *password; |
1024 | struct passwd *pw; | 998 | struct passwd *pw; |
1025 | BIGNUM *key; | 999 | BIGNUM *key; |
1026 | RSA *host_key, *file_key; | 1000 | RSA *host_key, *file_key; |
1027 | RSA *public_key; | 1001 | RSA *public_key; |
1028 | int bits, rbits; | 1002 | int bits, rbits; |
1029 | unsigned char session_key[SSH_SESSION_KEY_LENGTH]; | 1003 | unsigned char session_key[SSH_SESSION_KEY_LENGTH]; |
1030 | const char *server_user, *local_user; | 1004 | const char *server_user, *local_user; |
1031 | char *cp, *host, *ip = NULL; | 1005 | char *cp, *host, *ip = NULL; |
1032 | unsigned char check_bytes[8]; | 1006 | char hostline[1000], *hostp; |
1033 | unsigned int supported_ciphers, supported_authentications, protocol_flags; | 1007 | unsigned char check_bytes[8]; |
1034 | HostStatus host_status; | 1008 | unsigned int supported_ciphers, supported_authentications, protocol_flags; |
1035 | HostStatus ip_status; | 1009 | HostStatus host_status; |
1036 | int host_ip_differ = 0; | 1010 | HostStatus ip_status; |
1037 | int local = (ntohl(hostaddr->sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; | 1011 | int host_ip_differ = 0; |
1038 | int payload_len, clen, sum_len = 0; | 1012 | int local = (ntohl(hostaddr->sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; |
1039 | u_int32_t rand = 0; | 1013 | int payload_len, clen, sum_len = 0; |
1040 | 1014 | u_int32_t rand = 0; | |
1041 | if (options.check_host_ip) | 1015 | |
1042 | ip = xstrdup(inet_ntoa(hostaddr->sin_addr)); | 1016 | if (options.check_host_ip) |
1043 | 1017 | ip = xstrdup(inet_ntoa(hostaddr->sin_addr)); | |
1044 | /* Convert the user-supplied hostname into all lowercase. */ | 1018 | |
1045 | host = xstrdup(orighost); | 1019 | /* Convert the user-supplied hostname into all lowercase. */ |
1046 | for (cp = host; *cp; cp++) | 1020 | host = xstrdup(orighost); |
1047 | if (isupper(*cp)) | 1021 | for (cp = host; *cp; cp++) |
1048 | *cp = tolower(*cp); | 1022 | if (isupper(*cp)) |
1049 | 1023 | *cp = tolower(*cp); | |
1050 | /* Exchange protocol version identification strings with the server. */ | 1024 | |
1051 | ssh_exchange_identification(); | 1025 | /* Exchange protocol version identification strings with the server. */ |
1052 | 1026 | ssh_exchange_identification(); | |
1053 | /* Put the connection into non-blocking mode. */ | 1027 | |
1054 | packet_set_nonblocking(); | 1028 | /* Put the connection into non-blocking mode. */ |
1055 | 1029 | packet_set_nonblocking(); | |
1056 | /* Get local user name. Use it as server user if no user name | 1030 | |
1057 | was given. */ | 1031 | /* Get local user name. Use it as server user if no user name was given. */ |
1058 | pw = getpwuid(original_real_uid); | 1032 | pw = getpwuid(original_real_uid); |
1059 | if (!pw) | 1033 | if (!pw) |
1060 | fatal("User id %d not found from user database.", original_real_uid); | 1034 | fatal("User id %d not found from user database.", original_real_uid); |
1061 | local_user = xstrdup(pw->pw_name); | 1035 | local_user = xstrdup(pw->pw_name); |
1062 | server_user = options.user ? options.user : local_user; | 1036 | server_user = options.user ? options.user : local_user; |
1063 | 1037 | ||
1064 | debug("Waiting for server public key."); | 1038 | debug("Waiting for server public key."); |
1065 | 1039 | ||
1066 | /* Wait for a public key packet from the server. */ | 1040 | /* Wait for a public key packet from the server. */ |
1067 | packet_read_expect(&payload_len, SSH_SMSG_PUBLIC_KEY); | 1041 | packet_read_expect(&payload_len, SSH_SMSG_PUBLIC_KEY); |
1068 | 1042 | ||
1069 | /* Get check bytes from the packet. */ | 1043 | /* Get check bytes from the packet. */ |
1070 | for (i = 0; i < 8; i++) | 1044 | for (i = 0; i < 8; i++) |
1071 | check_bytes[i] = packet_get_char(); | 1045 | check_bytes[i] = packet_get_char(); |
1072 | 1046 | ||
1073 | /* Get the public key. */ | 1047 | /* Get the public key. */ |
1074 | public_key = RSA_new(); | 1048 | public_key = RSA_new(); |
1075 | bits = packet_get_int(); /* bits */ | 1049 | bits = packet_get_int();/* bits */ |
1076 | public_key->e = BN_new(); | 1050 | public_key->e = BN_new(); |
1077 | packet_get_bignum(public_key->e, &clen); | 1051 | packet_get_bignum(public_key->e, &clen); |
1078 | sum_len += clen; | 1052 | sum_len += clen; |
1079 | public_key->n = BN_new(); | 1053 | public_key->n = BN_new(); |
1080 | packet_get_bignum(public_key->n, &clen); | 1054 | packet_get_bignum(public_key->n, &clen); |
1081 | sum_len += clen; | 1055 | sum_len += clen; |
1082 | 1056 | ||
1083 | rbits = BN_num_bits(public_key->n); | 1057 | rbits = BN_num_bits(public_key->n); |
1084 | if (bits != rbits) { | 1058 | if (bits != rbits) { |
1085 | log("Warning: Server lies about size of server public key: " | 1059 | log("Warning: Server lies about size of server public key: " |
1086 | "actual size is %d bits vs. announced %d.", rbits, bits); | 1060 | "actual size is %d bits vs. announced %d.", rbits, bits); |
1087 | log("Warning: This may be due to an old implementation of ssh."); | 1061 | log("Warning: This may be due to an old implementation of ssh."); |
1088 | } | 1062 | } |
1089 | 1063 | /* Get the host key. */ | |
1090 | /* Get the host key. */ | 1064 | host_key = RSA_new(); |
1091 | host_key = RSA_new(); | 1065 | bits = packet_get_int();/* bits */ |
1092 | bits = packet_get_int(); /* bits */ | 1066 | host_key->e = BN_new(); |
1093 | host_key->e = BN_new(); | 1067 | packet_get_bignum(host_key->e, &clen); |
1094 | packet_get_bignum(host_key->e, &clen); | 1068 | sum_len += clen; |
1095 | sum_len += clen; | 1069 | host_key->n = BN_new(); |
1096 | host_key->n = BN_new(); | 1070 | packet_get_bignum(host_key->n, &clen); |
1097 | packet_get_bignum(host_key->n, &clen); | 1071 | sum_len += clen; |
1098 | sum_len += clen; | 1072 | |
1099 | 1073 | rbits = BN_num_bits(host_key->n); | |
1100 | rbits = BN_num_bits(host_key->n); | 1074 | if (bits != rbits) { |
1101 | if (bits != rbits) { | 1075 | log("Warning: Server lies about size of server host key: " |
1102 | log("Warning: Server lies about size of server host key: " | 1076 | "actual size is %d bits vs. announced %d.", rbits, bits); |
1103 | "actual size is %d bits vs. announced %d.", rbits, bits); | 1077 | log("Warning: This may be due to an old implementation of ssh."); |
1104 | log("Warning: This may be due to an old implementation of ssh."); | 1078 | } |
1105 | } | 1079 | /* Store the host key from the known host file in here so that we |
1106 | 1080 | can compare it with the key for the IP address. */ | |
1107 | /* Store the host key from the known host file in here | 1081 | file_key = RSA_new(); |
1108 | * so that we can compare it with the key for the IP | 1082 | file_key->n = BN_new(); |
1109 | * address. */ | 1083 | file_key->e = BN_new(); |
1110 | file_key = RSA_new(); | 1084 | |
1111 | file_key->n = BN_new(); | 1085 | /* Get protocol flags. */ |
1112 | file_key->e = BN_new(); | 1086 | protocol_flags = packet_get_int(); |
1113 | 1087 | packet_set_protocol_flags(protocol_flags); | |
1114 | /* Get protocol flags. */ | 1088 | |
1115 | protocol_flags = packet_get_int(); | 1089 | /* Get supported cipher types. */ |
1116 | packet_set_protocol_flags(protocol_flags); | 1090 | supported_ciphers = packet_get_int(); |
1117 | 1091 | ||
1118 | /* Get supported cipher types. */ | 1092 | /* Get supported authentication types. */ |
1119 | supported_ciphers = packet_get_int(); | 1093 | supported_authentications = packet_get_int(); |
1120 | 1094 | ||
1121 | /* Get supported authentication types. */ | 1095 | debug("Received server public key (%d bits) and host key (%d bits).", |
1122 | supported_authentications = packet_get_int(); | 1096 | BN_num_bits(public_key->n), BN_num_bits(host_key->n)); |
1123 | 1097 | ||
1124 | debug("Received server public key (%d bits) and host key (%d bits).", | 1098 | packet_integrity_check(payload_len, |
1125 | BN_num_bits(public_key->n), BN_num_bits(host_key->n)); | 1099 | 8 + 4 + sum_len + 0 + 4 + 0 + 0 + 4 + 4 + 4, |
1126 | 1100 | SSH_SMSG_PUBLIC_KEY); | |
1127 | packet_integrity_check(payload_len, | 1101 | |
1128 | 8 + 4 + sum_len + 0 + 4 + 0 + 0 + 4 + 4 + 4, | 1102 | /* Compute the session id. */ |
1129 | SSH_SMSG_PUBLIC_KEY); | 1103 | compute_session_id(session_id, check_bytes, host_key->n, public_key->n); |
1130 | 1104 | ||
1131 | /* Compute the session id. */ | 1105 | /* Check if the host key is present in the user\'s list of known |
1132 | compute_session_id(session_id, check_bytes, host_key->n, public_key->n); | 1106 | hosts or in the systemwide list. */ |
1133 | 1107 | host_status = check_host_in_hostfile(options.user_hostfile, host, | |
1134 | /* Check if the host key is present in the user\'s list of known hosts | 1108 | host_key->e, host_key->n, |
1135 | or in the systemwide list. */ | 1109 | file_key->e, file_key->n); |
1136 | host_status = check_host_in_hostfile(options.user_hostfile, host, | 1110 | if (host_status == HOST_NEW) |
1137 | host_key->e, host_key->n, | 1111 | host_status = check_host_in_hostfile(options.system_hostfile, host, |
1138 | file_key->e, file_key->n); | 1112 | host_key->e, host_key->n, |
1139 | if (host_status == HOST_NEW) | 1113 | file_key->e, file_key->n); |
1140 | host_status = check_host_in_hostfile(options.system_hostfile, host, | 1114 | /* Force accepting of the host key for localhost and 127.0.0.1. |
1141 | host_key->e, host_key->n, | 1115 | The problem is that if the home directory is NFS-mounted to |
1142 | file_key->e, file_key->n); | 1116 | multiple machines, localhost will refer to a different machine |
1143 | /* Force accepting of the host key for localhost and 127.0.0.1. | 1117 | in each of them, and the user will get bogus HOST_CHANGED |
1144 | The problem is that if the home directory is NFS-mounted to multiple | 1118 | warnings. This essentially disables host authentication for |
1145 | machines, localhost will refer to a different machine in each of them, | 1119 | localhost; however, this is probably not a real problem. */ |
1146 | and the user will get bogus HOST_CHANGED warnings. This essentially | 1120 | if (local) { |
1147 | disables host authentication for localhost; however, this is probably | 1121 | debug("Forcing accepting of host key for localhost."); |
1148 | not a real problem. */ | 1122 | host_status = HOST_OK; |
1149 | if (local) { | 1123 | } |
1150 | debug("Forcing accepting of host key for localhost."); | 1124 | /* Also perform check for the ip address, skip the check if we are |
1151 | host_status = HOST_OK; | 1125 | localhost or the hostname was an ip address to begin with */ |
1152 | } | 1126 | if (options.check_host_ip && !local && strcmp(host, ip)) { |
1153 | 1127 | RSA *ip_key = RSA_new(); | |
1154 | /* Also perform check for the ip address, skip the check if we are | 1128 | ip_key->n = BN_new(); |
1155 | localhost or the hostname was an ip address to begin with */ | 1129 | ip_key->e = BN_new(); |
1156 | if (options.check_host_ip && !local && strcmp(host, ip)) { | 1130 | ip_status = check_host_in_hostfile(options.user_hostfile, ip, |
1157 | RSA *ip_key = RSA_new(); | 1131 | host_key->e, host_key->n, |
1158 | ip_key->n = BN_new(); | 1132 | ip_key->e, ip_key->n); |
1159 | ip_key->e = BN_new(); | 1133 | |
1160 | ip_status = check_host_in_hostfile(options.user_hostfile, ip, | 1134 | if (ip_status == HOST_NEW) |
1161 | host_key->e, host_key->n, | 1135 | ip_status = check_host_in_hostfile(options.system_hostfile, ip, |
1162 | ip_key->e, ip_key->n); | 1136 | host_key->e, host_key->n, |
1163 | 1137 | ip_key->e, ip_key->n); | |
1164 | if (ip_status == HOST_NEW) | 1138 | if (host_status == HOST_CHANGED && |
1165 | ip_status = check_host_in_hostfile(options.system_hostfile, ip, | 1139 | (ip_status != HOST_CHANGED || |
1166 | host_key->e, host_key->n, | 1140 | (BN_cmp(ip_key->e, file_key->e) || BN_cmp(ip_key->n, file_key->n)))) |
1167 | ip_key->e, ip_key->n); | 1141 | host_ip_differ = 1; |
1168 | if (host_status == HOST_CHANGED && | 1142 | |
1169 | (ip_status != HOST_CHANGED || | 1143 | RSA_free(ip_key); |
1170 | (BN_cmp(ip_key->e, file_key->e) || BN_cmp(ip_key->n, file_key->n)))) | 1144 | } else |
1171 | host_ip_differ = 1; | 1145 | ip_status = host_status; |
1172 | 1146 | ||
1173 | RSA_free(ip_key); | 1147 | RSA_free(file_key); |
1174 | } else | 1148 | |
1175 | ip_status = host_status; | 1149 | switch (host_status) { |
1176 | 1150 | case HOST_OK: | |
1177 | RSA_free(file_key); | 1151 | /* The host is known and the key matches. */ |
1178 | 1152 | debug("Host '%.200s' is known and matches the host key.", host); | |
1179 | switch (host_status) { | 1153 | if (options.check_host_ip) { |
1180 | case HOST_OK: | 1154 | if (ip_status == HOST_NEW) { |
1181 | /* The host is known and the key matches. */ | 1155 | if (!add_host_to_hostfile(options.user_hostfile, ip, |
1182 | debug("Host '%.200s' is known and matches the host key.", host); | 1156 | host_key->e, host_key->n)) |
1183 | if (options.check_host_ip) { | 1157 | log("Failed to add the host key for IP address '%.30s' to the list of known hosts (%.30s).", |
1184 | if (ip_status == HOST_NEW) { | 1158 | ip, options.user_hostfile); |
1185 | if (!add_host_to_hostfile(options.user_hostfile, ip, | 1159 | else |
1186 | host_key->e, host_key->n)) | 1160 | log("Warning: Permanently added host key for IP address '%.30s' to the list of known hosts.", |
1187 | log("Failed to add the host key for IP address '%.30s' to the list of known hosts (%.30s).", | 1161 | ip); |
1188 | ip, options.user_hostfile); | 1162 | } else if (ip_status != HOST_OK) |
1189 | else | 1163 | log("Warning: the host key for '%.200s' differs from the key for the IP address '%.30s'", |
1190 | log("Warning: Permanently added host key for IP address '%.30s' to the list of known hosts.", | 1164 | host, ip); |
1191 | ip); | 1165 | } |
1192 | } else if (ip_status != HOST_OK) | 1166 | break; |
1193 | log("Warning: the host key for '%.200s' differs from the key for the IP address '%.30s'", | 1167 | case HOST_NEW: |
1194 | host, ip); | 1168 | /* The host is new. */ |
1195 | } | 1169 | if (options.strict_host_key_checking == 1) { |
1196 | 1170 | /* User has requested strict host key checking. We will not add the host key | |
1197 | break; | 1171 | automatically. The only alternative left is to abort. */ |
1198 | case HOST_NEW: | 1172 | fatal("No host key is known for %.200s and you have requested strict checking.", host); |
1199 | { | 1173 | } else if (options.strict_host_key_checking == 2) { |
1200 | char hostline[1000], *hostp = hostline; | 1174 | /* The default */ |
1201 | /* The host is new. */ | 1175 | char prompt[1024]; |
1202 | if (options.strict_host_key_checking == 1) { | 1176 | char *fp = fingerprint(host_key->e, host_key->n); |
1203 | /* User has requested strict host key checking. We will not | 1177 | snprintf(prompt, sizeof(prompt), |
1204 | add the host key automatically. The only alternative left | 1178 | "The authenticity of host '%.200s' can't be established.\n" |
1205 | is to abort. */ | 1179 | "Key fingerprint is %d %s.\n" |
1206 | fatal("No host key is known for %.200s and you have requested strict checking.", host); | 1180 | "Are you sure you want to continue connecting (yes/no)? ", |
1207 | } else if (options.strict_host_key_checking == 2) { /* The default */ | 1181 | host, BN_num_bits(host_key->n), fp); |
1208 | char prompt[1024]; | 1182 | if (!read_yes_or_no(prompt, -1)) |
1209 | char *fp = fingerprint(host_key->e, host_key->n); | 1183 | fatal("Aborted by user!\n"); |
1210 | snprintf(prompt, sizeof(prompt), | 1184 | } |
1211 | "The authenticity of host '%.200s' can't be established.\n" | 1185 | if (options.check_host_ip && ip_status == HOST_NEW && strcmp(host, ip)) { |
1212 | "Key fingerprint is %d %s.\n" | 1186 | snprintf(hostline, sizeof(hostline), "%s,%s", host, ip); |
1213 | "Are you sure you want to continue connecting (yes/no)? ", | 1187 | hostp = hostline; |
1214 | host, BN_num_bits(host_key->n), fp); | 1188 | } else |
1215 | if (!read_yes_or_no(prompt, -1)) | 1189 | hostp = host; |
1216 | fatal("Aborted by user!\n"); | 1190 | |
1217 | } | 1191 | /* If not in strict mode, add the key automatically to the local known_hosts file. */ |
1218 | 1192 | if (!add_host_to_hostfile(options.user_hostfile, hostp, | |
1219 | if (options.check_host_ip && ip_status == HOST_NEW && strcmp(host, ip)) | 1193 | host_key->e, host_key->n)) |
1220 | snprintf(hostline, sizeof(hostline), "%s,%s", host, ip); | 1194 | log("Failed to add the host to the list of known hosts (%.500s).", |
1221 | else | 1195 | options.user_hostfile); |
1222 | hostp = host; | 1196 | else |
1223 | 1197 | log("Warning: Permanently added '%.200s' to the list of known hosts.", | |
1224 | /* If not in strict mode, add the key automatically to the local | 1198 | hostp); |
1225 | known_hosts file. */ | 1199 | break; |
1226 | if (!add_host_to_hostfile(options.user_hostfile, hostp, | 1200 | case HOST_CHANGED: |
1227 | host_key->e, host_key->n)) | 1201 | if (options.check_host_ip && host_ip_differ) { |
1228 | log("Failed to add the host to the list of known hosts (%.500s).", | 1202 | char *msg; |
1229 | options.user_hostfile); | 1203 | if (ip_status == HOST_NEW) |
1230 | else | 1204 | msg = "is unknown"; |
1231 | log("Warning: Permanently added '%.200s' to the list of known hosts.", | 1205 | else if (ip_status == HOST_OK) |
1232 | hostp); | 1206 | msg = "is unchanged"; |
1233 | break; | 1207 | else |
1234 | } | 1208 | msg = "has a different value"; |
1235 | case HOST_CHANGED: | 1209 | error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); |
1236 | if (options.check_host_ip) { | 1210 | error("@ WARNING: POSSIBLE DNS SPOOFING DETECTED! @"); |
1237 | if (host_ip_differ) { | 1211 | error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); |
1238 | char *msg; | 1212 | error("The host key for %s has changed,", host); |
1239 | if (ip_status == HOST_NEW) | 1213 | error("and the key for the according IP address %s", ip); |
1240 | msg = "is unknown"; | 1214 | error("%s. This could either mean that", msg); |
1241 | else if (ip_status == HOST_OK) | 1215 | error("DNS SPOOFING is happening or the IP address for the host"); |
1242 | msg = "is unchanged"; | 1216 | error("and its host key have changed at the same time"); |
1243 | else | 1217 | } |
1244 | msg = "has a different value"; | 1218 | /* The host key has changed. */ |
1245 | error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); | 1219 | error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); |
1246 | error("@ WARNING: POSSIBLE DNS SPOOFING DETECTED! @"); | 1220 | error("@ WARNING: HOST IDENTIFICATION HAS CHANGED! @"); |
1247 | error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); | 1221 | error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); |
1248 | error("The host key for %s has changed,", host); | 1222 | error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!"); |
1249 | error("and the key for the according IP address %s", ip); | 1223 | error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!"); |
1250 | error("%s. This could either mean that", msg); | 1224 | error("It is also possible that the host key has just been changed."); |
1251 | error("DNS SPOOFING is happening or the IP address for the host"); | 1225 | error("Please contact your system administrator."); |
1252 | error("and its host key have changed at the same time"); | 1226 | error("Add correct host key in %.100s to get rid of this message.", |
1253 | } | 1227 | options.user_hostfile); |
1254 | } | 1228 | |
1255 | 1229 | /* If strict host key checking is in use, the user will | |
1256 | /* The host key has changed. */ | 1230 | have to edit the key manually and we can only abort. */ |
1257 | error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); | 1231 | if (options.strict_host_key_checking) |
1258 | error("@ WARNING: HOST IDENTIFICATION HAS CHANGED! @"); | 1232 | fatal("Host key for %.200s has changed and you have requested strict checking.", host); |
1259 | error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); | 1233 | |
1260 | error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!"); | 1234 | /* If strict host key checking has not been requested, allow the connection |
1261 | error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!"); | 1235 | but without password authentication or agent forwarding. */ |
1262 | error("It is also possible that the host key has just been changed."); | 1236 | if (options.password_authentication) { |
1263 | error("Please contact your system administrator."); | 1237 | error("Password authentication is disabled to avoid trojan horses."); |
1264 | error("Add correct host key in %.100s to get rid of this message.", | 1238 | options.password_authentication = 0; |
1265 | options.user_hostfile); | 1239 | } |
1266 | 1240 | if (options.forward_agent) { | |
1267 | /* If strict host key checking is in use, the user will have to edit | 1241 | error("Agent forwarding is disabled to avoid trojan horses."); |
1268 | the key manually and we can only abort. */ | 1242 | options.forward_agent = 0; |
1269 | if (options.strict_host_key_checking) | 1243 | } |
1270 | fatal("Host key for %.200s has changed and you have requested strict checking.", host); | 1244 | /* XXX Should permit the user to change to use the new id. |
1271 | 1245 | This could be done by converting the host key to an | |
1272 | /* If strict host key checking has not been requested, allow the | 1246 | identifying sentence, tell that the host identifies |
1273 | connection but without password authentication or | 1247 | itself by that sentence, and ask the user if he/she |
1274 | agent forwarding. */ | 1248 | whishes to accept the authentication. */ |
1275 | if (options.password_authentication) { | 1249 | break; |
1276 | error("Password authentication is disabled to avoid trojan horses."); | 1250 | } |
1277 | options.password_authentication = 0; | 1251 | |
1278 | } | 1252 | if (options.check_host_ip) |
1279 | if (options.forward_agent) { | 1253 | xfree(ip); |
1280 | error("Agent forwarding is disabled to avoid trojan horses."); | 1254 | |
1281 | options.forward_agent = 0; | 1255 | /* Generate a session key. */ |
1282 | } | 1256 | arc4random_stir(); |
1283 | /* XXX Should permit the user to change to use the new id. This could | 1257 | |
1284 | be done by converting the host key to an identifying sentence, tell | 1258 | /* Generate an encryption key for the session. The key is a 256 |
1285 | that the host identifies itself by that sentence, and ask the user | 1259 | bit random number, interpreted as a 32-byte key, with the least |
1286 | if he/she whishes to accept the authentication. */ | 1260 | significant 8 bits being the first byte of the key. */ |
1287 | break; | 1261 | for (i = 0; i < 32; i++) { |
1288 | } | 1262 | if (i % 4 == 0) |
1289 | 1263 | rand = arc4random(); | |
1290 | if (options.check_host_ip) | 1264 | session_key[i] = rand & 0xff; |
1291 | xfree(ip); | 1265 | rand >>= 8; |
1292 | 1266 | } | |
1293 | /* Generate a session key. */ | 1267 | |
1294 | arc4random_stir(); | 1268 | /* According to the protocol spec, the first byte of the session |
1295 | 1269 | key is the highest byte of the integer. The session key is | |
1296 | /* Generate an encryption key for the session. The key is a 256 bit | 1270 | xored with the first 16 bytes of the session id. */ |
1297 | random number, interpreted as a 32-byte key, with the least significant | 1271 | key = BN_new(); |
1298 | 8 bits being the first byte of the key. */ | 1272 | BN_set_word(key, 0); |
1299 | for (i = 0; i < 32; i++) { | 1273 | for (i = 0; i < SSH_SESSION_KEY_LENGTH; i++) { |
1300 | if (i % 4 == 0) | 1274 | BN_lshift(key, key, 8); |
1301 | rand = arc4random(); | 1275 | if (i < 16) |
1302 | session_key[i] = rand & 0xff; | 1276 | BN_add_word(key, session_key[i] ^ session_id[i]); |
1303 | rand >>= 8; | 1277 | else |
1304 | } | 1278 | BN_add_word(key, session_key[i]); |
1305 | 1279 | } | |
1306 | /* According to the protocol spec, the first byte of the session key is | 1280 | |
1307 | the highest byte of the integer. The session key is xored with the | 1281 | /* Encrypt the integer using the public key and host key of the |
1308 | first 16 bytes of the session id. */ | 1282 | server (key with smaller modulus first). */ |
1309 | key = BN_new(); | 1283 | if (BN_cmp(public_key->n, host_key->n) < 0) { |
1310 | BN_set_word(key, 0); | 1284 | /* Public key has smaller modulus. */ |
1311 | for (i = 0; i < SSH_SESSION_KEY_LENGTH; i++) | 1285 | if (BN_num_bits(host_key->n) < |
1312 | { | 1286 | BN_num_bits(public_key->n) + SSH_KEY_BITS_RESERVED) { |
1313 | BN_lshift(key, key, 8); | 1287 | fatal("respond_to_rsa_challenge: host_key %d < public_key %d + " |
1314 | if (i < 16) | 1288 | "SSH_KEY_BITS_RESERVED %d", |
1315 | BN_add_word(key, session_key[i] ^ session_id[i]); | 1289 | BN_num_bits(host_key->n), |
1316 | else | 1290 | BN_num_bits(public_key->n), |
1317 | BN_add_word(key, session_key[i]); | 1291 | SSH_KEY_BITS_RESERVED); |
1318 | } | 1292 | } |
1319 | 1293 | rsa_public_encrypt(key, key, public_key); | |
1320 | /* Encrypt the integer using the public key and host key of the server | 1294 | rsa_public_encrypt(key, key, host_key); |
1321 | (key with smaller modulus first). */ | 1295 | } else { |
1322 | if (BN_cmp(public_key->n, host_key->n) < 0) | 1296 | /* Host key has smaller modulus (or they are equal). */ |
1323 | { | 1297 | if (BN_num_bits(public_key->n) < |
1324 | /* Public key has smaller modulus. */ | 1298 | BN_num_bits(host_key->n) + SSH_KEY_BITS_RESERVED) { |
1325 | if (BN_num_bits(host_key->n) < | 1299 | fatal("respond_to_rsa_challenge: public_key %d < host_key %d + " |
1326 | BN_num_bits(public_key->n) + SSH_KEY_BITS_RESERVED) { | 1300 | "SSH_KEY_BITS_RESERVED %d", |
1327 | fatal("respond_to_rsa_challenge: host_key %d < public_key %d + " | 1301 | BN_num_bits(public_key->n), |
1328 | "SSH_KEY_BITS_RESERVED %d", | 1302 | BN_num_bits(host_key->n), |
1329 | BN_num_bits(host_key->n), | 1303 | SSH_KEY_BITS_RESERVED); |
1330 | BN_num_bits(public_key->n), | 1304 | } |
1331 | SSH_KEY_BITS_RESERVED); | 1305 | rsa_public_encrypt(key, key, host_key); |
1332 | } | 1306 | rsa_public_encrypt(key, key, public_key); |
1333 | 1307 | } | |
1334 | rsa_public_encrypt(key, key, public_key); | 1308 | |
1335 | rsa_public_encrypt(key, key, host_key); | 1309 | if (options.cipher == SSH_CIPHER_NOT_SET) { |
1336 | } | 1310 | if (cipher_mask() & supported_ciphers & (1 << ssh_cipher_default)) |
1337 | else | 1311 | options.cipher = ssh_cipher_default; |
1338 | { | 1312 | else { |
1339 | /* Host key has smaller modulus (or they are equal). */ | 1313 | debug("Cipher %s not supported, using %.100s instead.", |
1340 | if (BN_num_bits(public_key->n) < | 1314 | cipher_name(ssh_cipher_default), |
1341 | BN_num_bits(host_key->n) + SSH_KEY_BITS_RESERVED) { | 1315 | cipher_name(SSH_FALLBACK_CIPHER)); |
1342 | fatal("respond_to_rsa_challenge: public_key %d < host_key %d + " | 1316 | options.cipher = SSH_FALLBACK_CIPHER; |
1343 | "SSH_KEY_BITS_RESERVED %d", | 1317 | } |
1344 | BN_num_bits(public_key->n), | 1318 | } |
1345 | BN_num_bits(host_key->n), | 1319 | /* Check that the selected cipher is supported. */ |
1346 | SSH_KEY_BITS_RESERVED); | 1320 | if (!(supported_ciphers & (1 << options.cipher))) |
1347 | } | 1321 | fatal("Selected cipher type %.100s not supported by server.", |
1348 | 1322 | cipher_name(options.cipher)); | |
1349 | rsa_public_encrypt(key, key, host_key); | 1323 | |
1350 | rsa_public_encrypt(key, key, public_key); | 1324 | debug("Encryption type: %.100s", cipher_name(options.cipher)); |
1351 | } | 1325 | |
1352 | 1326 | /* Send the encrypted session key to the server. */ | |
1353 | if (options.cipher == SSH_CIPHER_NOT_SET) { | 1327 | packet_start(SSH_CMSG_SESSION_KEY); |
1354 | if (cipher_mask() & supported_ciphers & (1 << ssh_cipher_default)) | 1328 | packet_put_char(options.cipher); |
1355 | options.cipher = ssh_cipher_default; | 1329 | |
1356 | else { | 1330 | /* Send the check bytes back to the server. */ |
1357 | debug("Cipher %s not supported, using %.100s instead.", | 1331 | for (i = 0; i < 8; i++) |
1358 | cipher_name(ssh_cipher_default), | 1332 | packet_put_char(check_bytes[i]); |
1359 | cipher_name(SSH_FALLBACK_CIPHER)); | 1333 | |
1360 | options.cipher = SSH_FALLBACK_CIPHER; | 1334 | /* Send the encrypted encryption key. */ |
1361 | } | 1335 | packet_put_bignum(key); |
1362 | } | 1336 | |
1363 | 1337 | /* Send protocol flags. */ | |
1364 | /* Check that the selected cipher is supported. */ | 1338 | packet_put_int(SSH_PROTOFLAG_SCREEN_NUMBER | SSH_PROTOFLAG_HOST_IN_FWD_OPEN); |
1365 | if (!(supported_ciphers & (1 << options.cipher))) | 1339 | |
1366 | fatal("Selected cipher type %.100s not supported by server.", | 1340 | /* Send the packet now. */ |
1367 | cipher_name(options.cipher)); | ||
1368 | |||
1369 | debug("Encryption type: %.100s", cipher_name(options.cipher)); | ||
1370 | |||
1371 | /* Send the encrypted session key to the server. */ | ||
1372 | packet_start(SSH_CMSG_SESSION_KEY); | ||
1373 | packet_put_char(options.cipher); | ||
1374 | |||
1375 | /* Send the check bytes back to the server. */ | ||
1376 | for (i = 0; i < 8; i++) | ||
1377 | packet_put_char(check_bytes[i]); | ||
1378 | |||
1379 | /* Send the encrypted encryption key. */ | ||
1380 | packet_put_bignum(key); | ||
1381 | |||
1382 | /* Send protocol flags. */ | ||
1383 | packet_put_int(SSH_PROTOFLAG_SCREEN_NUMBER | SSH_PROTOFLAG_HOST_IN_FWD_OPEN); | ||
1384 | |||
1385 | /* Send the packet now. */ | ||
1386 | packet_send(); | ||
1387 | packet_write_wait(); | ||
1388 | |||
1389 | /* Destroy the session key integer and the public keys since we no longer | ||
1390 | need them. */ | ||
1391 | BN_clear_free(key); | ||
1392 | RSA_free(public_key); | ||
1393 | RSA_free(host_key); | ||
1394 | |||
1395 | debug("Sent encrypted session key."); | ||
1396 | |||
1397 | /* Set the encryption key. */ | ||
1398 | packet_set_encryption_key(session_key, SSH_SESSION_KEY_LENGTH, options.cipher); | ||
1399 | |||
1400 | /* We will no longer need the session key here. Destroy any extra copies. */ | ||
1401 | memset(session_key, 0, sizeof(session_key)); | ||
1402 | |||
1403 | /* Expect a success message from the server. Note that this message will | ||
1404 | be received in encrypted form. */ | ||
1405 | packet_read_expect(&payload_len, SSH_SMSG_SUCCESS); | ||
1406 | |||
1407 | debug("Received encrypted confirmation."); | ||
1408 | |||
1409 | /* Send the name of the user to log in as on the server. */ | ||
1410 | packet_start(SSH_CMSG_USER); | ||
1411 | packet_put_string(server_user, strlen(server_user)); | ||
1412 | packet_send(); | ||
1413 | packet_write_wait(); | ||
1414 | |||
1415 | /* The server should respond with success if no authentication is needed | ||
1416 | (the user has no password). Otherwise the server responds with | ||
1417 | failure. */ | ||
1418 | type = packet_read(&payload_len); | ||
1419 | if (type == SSH_SMSG_SUCCESS) | ||
1420 | return; /* Connection was accepted without authentication. */ | ||
1421 | if (type != SSH_SMSG_FAILURE) | ||
1422 | packet_disconnect("Protocol error: got %d in response to SSH_CMSG_USER", | ||
1423 | type); | ||
1424 | |||
1425 | #ifdef AFS | ||
1426 | /* Try Kerberos tgt passing if the server supports it. */ | ||
1427 | if ((supported_authentications & (1 << SSH_PASS_KERBEROS_TGT)) && | ||
1428 | options.kerberos_tgt_passing) | ||
1429 | { | ||
1430 | if (options.cipher == SSH_CIPHER_NONE) | ||
1431 | log("WARNING: Encryption is disabled! Ticket will be transmitted in the clear!"); | ||
1432 | (void)send_kerberos_tgt(); | ||
1433 | } | ||
1434 | |||
1435 | /* Try AFS token passing if the server supports it. */ | ||
1436 | if ((supported_authentications & (1 << SSH_PASS_AFS_TOKEN)) && | ||
1437 | options.afs_token_passing && k_hasafs()) { | ||
1438 | if (options.cipher == SSH_CIPHER_NONE) | ||
1439 | log("WARNING: Encryption is disabled! Token will be transmitted in the clear!"); | ||
1440 | send_afs_tokens(); | ||
1441 | } | ||
1442 | #endif /* AFS */ | ||
1443 | |||
1444 | #ifdef KRB4 | ||
1445 | if ((supported_authentications & (1 << SSH_AUTH_KERBEROS)) && | ||
1446 | options.kerberos_authentication) | ||
1447 | { | ||
1448 | debug("Trying Kerberos authentication."); | ||
1449 | if (try_kerberos_authentication()) { | ||
1450 | /* The server should respond with success or failure. */ | ||
1451 | type = packet_read(&payload_len); | ||
1452 | if (type == SSH_SMSG_SUCCESS) | ||
1453 | return; /* Successful connection. */ | ||
1454 | if (type != SSH_SMSG_FAILURE) | ||
1455 | packet_disconnect("Protocol error: got %d in response to Kerberos auth", type); | ||
1456 | } | ||
1457 | } | ||
1458 | #endif /* KRB4 */ | ||
1459 | |||
1460 | /* Use rhosts authentication if running in privileged socket and we do not | ||
1461 | wish to remain anonymous. */ | ||
1462 | if ((supported_authentications & (1 << SSH_AUTH_RHOSTS)) && | ||
1463 | options.rhosts_authentication) | ||
1464 | { | ||
1465 | debug("Trying rhosts authentication."); | ||
1466 | packet_start(SSH_CMSG_AUTH_RHOSTS); | ||
1467 | packet_put_string(local_user, strlen(local_user)); | ||
1468 | packet_send(); | ||
1469 | packet_write_wait(); | ||
1470 | |||
1471 | /* The server should respond with success or failure. */ | ||
1472 | type = packet_read(&payload_len); | ||
1473 | if (type == SSH_SMSG_SUCCESS) | ||
1474 | return; /* Successful connection. */ | ||
1475 | if (type != SSH_SMSG_FAILURE) | ||
1476 | packet_disconnect("Protocol error: got %d in response to rhosts auth", | ||
1477 | type); | ||
1478 | } | ||
1479 | |||
1480 | /* Try .rhosts or /etc/hosts.equiv authentication with RSA host | ||
1481 | authentication. */ | ||
1482 | if ((supported_authentications & (1 << SSH_AUTH_RHOSTS_RSA)) && | ||
1483 | options.rhosts_rsa_authentication && host_key_valid) | ||
1484 | { | ||
1485 | if (try_rhosts_rsa_authentication(local_user, own_host_key)) | ||
1486 | return; /* Successful authentication. */ | ||
1487 | } | ||
1488 | |||
1489 | /* Try RSA authentication if the server supports it. */ | ||
1490 | if ((supported_authentications & (1 << SSH_AUTH_RSA)) && | ||
1491 | options.rsa_authentication) | ||
1492 | { | ||
1493 | /* Try RSA authentication using the authentication agent. The agent | ||
1494 | is tried first because no passphrase is needed for it, whereas | ||
1495 | identity files may require passphrases. */ | ||
1496 | if (try_agent_authentication()) | ||
1497 | return; /* Successful connection. */ | ||
1498 | |||
1499 | /* Try RSA authentication for each identity. */ | ||
1500 | for (i = 0; i < options.num_identity_files; i++) | ||
1501 | if (try_rsa_authentication(pw, options.identity_files[i])) | ||
1502 | return; /* Successful connection. */ | ||
1503 | } | ||
1504 | |||
1505 | /* Try password authentication if the server supports it. */ | ||
1506 | if ((supported_authentications & (1 << SSH_AUTH_PASSWORD)) && | ||
1507 | options.password_authentication && !options.batch_mode) | ||
1508 | { | ||
1509 | char prompt[80]; | ||
1510 | snprintf(prompt, sizeof(prompt), "%.30s@%.30s's password: ", | ||
1511 | server_user, host); | ||
1512 | debug("Doing password authentication."); | ||
1513 | if (options.cipher == SSH_CIPHER_NONE) | ||
1514 | log("WARNING: Encryption is disabled! Password will be transmitted in clear text."); | ||
1515 | for (i = 0; i < options.number_of_password_prompts; i++) { | ||
1516 | if (i != 0) | ||
1517 | error("Permission denied, please try again."); | ||
1518 | password = read_passphrase(prompt, 0); | ||
1519 | packet_start(SSH_CMSG_AUTH_PASSWORD); | ||
1520 | packet_put_string(password, strlen(password)); | ||
1521 | memset(password, 0, strlen(password)); | ||
1522 | xfree(password); | ||
1523 | packet_send(); | 1341 | packet_send(); |
1524 | packet_write_wait(); | 1342 | packet_write_wait(); |
1525 | 1343 | ||
1344 | /* Destroy the session key integer and the public keys since we no longer need them. */ | ||
1345 | BN_clear_free(key); | ||
1346 | RSA_free(public_key); | ||
1347 | RSA_free(host_key); | ||
1348 | |||
1349 | debug("Sent encrypted session key."); | ||
1350 | |||
1351 | /* Set the encryption key. */ | ||
1352 | packet_set_encryption_key(session_key, SSH_SESSION_KEY_LENGTH, options.cipher); | ||
1353 | |||
1354 | /* We will no longer need the session key here. Destroy any extra copies. */ | ||
1355 | memset(session_key, 0, sizeof(session_key)); | ||
1356 | |||
1357 | /* Expect a success message from the server. Note that this | ||
1358 | message will be received in encrypted form. */ | ||
1359 | packet_read_expect(&payload_len, SSH_SMSG_SUCCESS); | ||
1360 | |||
1361 | debug("Received encrypted confirmation."); | ||
1362 | |||
1363 | /* Send the name of the user to log in as on the server. */ | ||
1364 | packet_start(SSH_CMSG_USER); | ||
1365 | packet_put_string(server_user, strlen(server_user)); | ||
1366 | packet_send(); | ||
1367 | packet_write_wait(); | ||
1368 | |||
1369 | /* The server should respond with success if no authentication is | ||
1370 | needed (the user has no password). Otherwise the server | ||
1371 | responds with failure. */ | ||
1526 | type = packet_read(&payload_len); | 1372 | type = packet_read(&payload_len); |
1373 | |||
1374 | /* check whether the connection was accepted without authentication. */ | ||
1527 | if (type == SSH_SMSG_SUCCESS) | 1375 | if (type == SSH_SMSG_SUCCESS) |
1528 | return; /* Successful connection. */ | 1376 | return; |
1529 | if (type != SSH_SMSG_FAILURE) | 1377 | if (type != SSH_SMSG_FAILURE) |
1530 | packet_disconnect("Protocol error: got %d in response to passwd auth", type); | 1378 | packet_disconnect("Protocol error: got %d in response to SSH_CMSG_USER", |
1531 | } | 1379 | type); |
1532 | } | 1380 | |
1381 | #ifdef AFS | ||
1382 | /* Try Kerberos tgt passing if the server supports it. */ | ||
1383 | if ((supported_authentications & (1 << SSH_PASS_KERBEROS_TGT)) && | ||
1384 | options.kerberos_tgt_passing) { | ||
1385 | if (options.cipher == SSH_CIPHER_NONE) | ||
1386 | log("WARNING: Encryption is disabled! Ticket will be transmitted in the clear!"); | ||
1387 | (void) send_kerberos_tgt(); | ||
1388 | } | ||
1389 | /* Try AFS token passing if the server supports it. */ | ||
1390 | if ((supported_authentications & (1 << SSH_PASS_AFS_TOKEN)) && | ||
1391 | options.afs_token_passing && k_hasafs()) { | ||
1392 | if (options.cipher == SSH_CIPHER_NONE) | ||
1393 | log("WARNING: Encryption is disabled! Token will be transmitted in the clear!"); | ||
1394 | send_afs_tokens(); | ||
1395 | } | ||
1396 | #endif /* AFS */ | ||
1533 | 1397 | ||
1534 | /* All authentication methods have failed. Exit with an error message. */ | 1398 | #ifdef KRB4 |
1535 | fatal("Permission denied."); | 1399 | if ((supported_authentications & (1 << SSH_AUTH_KERBEROS)) && |
1536 | /*NOTREACHED*/ | 1400 | options.kerberos_authentication) { |
1401 | debug("Trying Kerberos authentication."); | ||
1402 | if (try_kerberos_authentication()) { | ||
1403 | /* The server should respond with success or failure. */ | ||
1404 | type = packet_read(&payload_len); | ||
1405 | if (type == SSH_SMSG_SUCCESS) | ||
1406 | return; | ||
1407 | if (type != SSH_SMSG_FAILURE) | ||
1408 | packet_disconnect("Protocol error: got %d in response to Kerberos auth", type); | ||
1409 | } | ||
1410 | } | ||
1411 | #endif /* KRB4 */ | ||
1412 | |||
1413 | /* Use rhosts authentication if running in privileged socket and | ||
1414 | we do not wish to remain anonymous. */ | ||
1415 | if ((supported_authentications & (1 << SSH_AUTH_RHOSTS)) && | ||
1416 | options.rhosts_authentication) { | ||
1417 | debug("Trying rhosts authentication."); | ||
1418 | packet_start(SSH_CMSG_AUTH_RHOSTS); | ||
1419 | packet_put_string(local_user, strlen(local_user)); | ||
1420 | packet_send(); | ||
1421 | packet_write_wait(); | ||
1422 | |||
1423 | /* The server should respond with success or failure. */ | ||
1424 | type = packet_read(&payload_len); | ||
1425 | if (type == SSH_SMSG_SUCCESS) | ||
1426 | return; | ||
1427 | if (type != SSH_SMSG_FAILURE) | ||
1428 | packet_disconnect("Protocol error: got %d in response to rhosts auth", | ||
1429 | type); | ||
1430 | } | ||
1431 | /* Try .rhosts or /etc/hosts.equiv authentication with RSA host | ||
1432 | authentication. */ | ||
1433 | if ((supported_authentications & (1 << SSH_AUTH_RHOSTS_RSA)) && | ||
1434 | options.rhosts_rsa_authentication && host_key_valid) { | ||
1435 | if (try_rhosts_rsa_authentication(local_user, own_host_key)) | ||
1436 | return; | ||
1437 | } | ||
1438 | /* Try RSA authentication if the server supports it. */ | ||
1439 | if ((supported_authentications & (1 << SSH_AUTH_RSA)) && | ||
1440 | options.rsa_authentication) { | ||
1441 | /* Try RSA authentication using the authentication agent. | ||
1442 | The agent is tried first because no passphrase is | ||
1443 | needed for it, whereas identity files may require | ||
1444 | passphrases. */ | ||
1445 | if (try_agent_authentication()) | ||
1446 | return; | ||
1447 | |||
1448 | /* Try RSA authentication for each identity. */ | ||
1449 | for (i = 0; i < options.num_identity_files; i++) | ||
1450 | if (try_rsa_authentication(pw, options.identity_files[i])) | ||
1451 | return; | ||
1452 | } | ||
1453 | /* Try skey authentication if the server supports it. */ | ||
1454 | if ((supported_authentications & (1 << SSH_AUTH_TIS)) && | ||
1455 | options.skey_authentication && !options.batch_mode) { | ||
1456 | debug("Doing skey authentication."); | ||
1457 | |||
1458 | /* request a challenge */ | ||
1459 | packet_start(SSH_CMSG_AUTH_TIS); | ||
1460 | packet_send(); | ||
1461 | packet_write_wait(); | ||
1462 | |||
1463 | type = packet_read(&payload_len); | ||
1464 | if (type != SSH_SMSG_FAILURE && | ||
1465 | type != SSH_SMSG_AUTH_TIS_CHALLENGE) { | ||
1466 | packet_disconnect("Protocol error: got %d in response " | ||
1467 | "to skey auth", type); | ||
1468 | } | ||
1469 | if (type != SSH_SMSG_AUTH_TIS_CHALLENGE) { | ||
1470 | debug("No challenge for skey authentication."); | ||
1471 | } else { | ||
1472 | char *challenge, *response; | ||
1473 | challenge = packet_get_string(&payload_len); | ||
1474 | if (options.cipher == SSH_CIPHER_NONE) | ||
1475 | log("WARNING: Encryption is disabled! " | ||
1476 | "Reponse will be transmitted in clear text."); | ||
1477 | fprintf(stderr, "%s\n", challenge); | ||
1478 | fflush(stderr); | ||
1479 | for (i = 0; i < options.number_of_password_prompts; i++) { | ||
1480 | if (i != 0) | ||
1481 | error("Permission denied, please try again."); | ||
1482 | response = read_passphrase("Response: ", 0); | ||
1483 | packet_start(SSH_CMSG_AUTH_TIS_RESPONSE); | ||
1484 | packet_put_string(response, strlen(response)); | ||
1485 | memset(response, 0, strlen(response)); | ||
1486 | xfree(response); | ||
1487 | packet_send(); | ||
1488 | packet_write_wait(); | ||
1489 | type = packet_read(&payload_len); | ||
1490 | if (type == SSH_SMSG_SUCCESS) | ||
1491 | return; | ||
1492 | if (type != SSH_SMSG_FAILURE) | ||
1493 | packet_disconnect("Protocol error: got %d in response " | ||
1494 | "to skey auth", type); | ||
1495 | } | ||
1496 | } | ||
1497 | } | ||
1498 | /* Try password authentication if the server supports it. */ | ||
1499 | if ((supported_authentications & (1 << SSH_AUTH_PASSWORD)) && | ||
1500 | options.password_authentication && !options.batch_mode) { | ||
1501 | char prompt[80]; | ||
1502 | snprintf(prompt, sizeof(prompt), "%.30s@%.30s's password: ", | ||
1503 | server_user, host); | ||
1504 | debug("Doing password authentication."); | ||
1505 | if (options.cipher == SSH_CIPHER_NONE) | ||
1506 | log("WARNING: Encryption is disabled! Password will be transmitted in clear text."); | ||
1507 | for (i = 0; i < options.number_of_password_prompts; i++) { | ||
1508 | if (i != 0) | ||
1509 | error("Permission denied, please try again."); | ||
1510 | password = read_passphrase(prompt, 0); | ||
1511 | packet_start(SSH_CMSG_AUTH_PASSWORD); | ||
1512 | packet_put_string(password, strlen(password)); | ||
1513 | memset(password, 0, strlen(password)); | ||
1514 | xfree(password); | ||
1515 | packet_send(); | ||
1516 | packet_write_wait(); | ||
1517 | |||
1518 | type = packet_read(&payload_len); | ||
1519 | if (type == SSH_SMSG_SUCCESS) | ||
1520 | return; | ||
1521 | if (type != SSH_SMSG_FAILURE) | ||
1522 | packet_disconnect("Protocol error: got %d in response to passwd auth", type); | ||
1523 | } | ||
1524 | } | ||
1525 | /* All authentication methods have failed. Exit with an error message. */ | ||
1526 | fatal("Permission denied."); | ||
1527 | /* NOTREACHED */ | ||
1537 | } | 1528 | } |
@@ -9,7 +9,7 @@ | |||
9 | .\" | 9 | .\" |
10 | .\" Created: Sat Apr 22 21:55:14 1995 ylo | 10 | .\" Created: Sat Apr 22 21:55:14 1995 ylo |
11 | .\" | 11 | .\" |
12 | .\" $Id: sshd.8,v 1.8 1999/11/21 02:23:53 damien Exp $ | 12 | .\" $Id: sshd.8,v 1.9 1999/11/24 13:26:23 damien Exp $ |
13 | .\" | 13 | .\" |
14 | .Dd September 25, 1999 | 14 | .Dd September 25, 1999 |
15 | .Dt SSHD 8 | 15 | .Dt SSHD 8 |
@@ -26,6 +26,7 @@ | |||
26 | .Op Fl h Ar host_key_file | 26 | .Op Fl h Ar host_key_file |
27 | .Op Fl k Ar key_gen_time | 27 | .Op Fl k Ar key_gen_time |
28 | .Op Fl p Ar port | 28 | .Op Fl p Ar port |
29 | .Op Fl V Ar client_protocol_id | ||
29 | .Sh DESCRIPTION | 30 | .Sh DESCRIPTION |
30 | .Nm | 31 | .Nm |
31 | (Secure Shell Daemon) is the daemon program for | 32 | (Secure Shell Daemon) is the daemon program for |
@@ -165,6 +166,13 @@ Quiet mode. Nothing is sent to the system log. Normally the beginning, | |||
165 | authentication, and termination of each connection is logged. | 166 | authentication, and termination of each connection is logged. |
166 | .It Fl Q | 167 | .It Fl Q |
167 | Do not print an error message if RSA support is missing. | 168 | Do not print an error message if RSA support is missing. |
169 | .It Fl V Ar client_protocol_id | ||
170 | SSH2 compatibility mode. | ||
171 | When this options is specified | ||
172 | .Nm | ||
173 | assumes the client has sent the given version string | ||
174 | and skips the | ||
175 | Protocol Version Identification Exchange. | ||
168 | .El | 176 | .El |
169 | .Sh CONFIGURATION FILE | 177 | .Sh CONFIGURATION FILE |
170 | .Nm | 178 | .Nm |
@@ -320,7 +328,7 @@ The default is 600 (seconds). | |||
320 | Gives the verbosity level that is used when logging messages from | 328 | Gives the verbosity level that is used when logging messages from |
321 | .Nm sshd . | 329 | .Nm sshd . |
322 | The possible values are: | 330 | The possible values are: |
323 | QUIET, FATAL, ERROR, INFO, CHAT and DEBUG. | 331 | QUIET, FATAL, ERROR, INFO, VERBOSE and DEBUG. |
324 | The default is INFO. | 332 | The default is INFO. |
325 | Logging with level DEBUG violates the privacy of users | 333 | Logging with level DEBUG violates the privacy of users |
326 | and is not recommended. | 334 | and is not recommended. |
@@ -1,24 +1,17 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | |
3 | sshd.c | 3 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
4 | 4 | * All rights reserved | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Created: Fri Mar 17 17:09:28 1995 ylo |
6 | 6 | * This program is the ssh daemon. It listens for connections from clients, and | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * performs authentication, executes use commands or shell, and forwards |
8 | All rights reserved | 8 | * information to/from the application to the user client over an encrypted |
9 | 9 | * connection. This can also handle forwarding of X11, TCP/IP, and authentication | |
10 | Created: Fri Mar 17 17:09:28 1995 ylo | 10 | * agent connections. |
11 | 11 | */ | |
12 | This program is the ssh daemon. It listens for connections from clients, and | ||
13 | performs authentication, executes use commands or shell, and forwards | ||
14 | information to/from the application to the user client over an encrypted | ||
15 | connection. This can also handle forwarding of X11, TCP/IP, and authentication | ||
16 | agent connections. | ||
17 | |||
18 | */ | ||
19 | 12 | ||
20 | #include "includes.h" | 13 | #include "includes.h" |
21 | RCSID("$Id: sshd.c,v 1.29 1999/11/23 00:24:32 damien Exp $"); | 14 | RCSID("$Id: sshd.c,v 1.30 1999/11/24 13:26:23 damien Exp $"); |
22 | 15 | ||
23 | #include "xmalloc.h" | 16 | #include "xmalloc.h" |
24 | #include "rsa.h" | 17 | #include "rsa.h" |
@@ -52,10 +45,12 @@ ServerOptions options; | |||
52 | /* Name of the server configuration file. */ | 45 | /* Name of the server configuration file. */ |
53 | char *config_file_name = SERVER_CONFIG_FILE; | 46 | char *config_file_name = SERVER_CONFIG_FILE; |
54 | 47 | ||
55 | /* Debug mode flag. This can be set on the command line. If debug | 48 | /* |
56 | mode is enabled, extra debugging output will be sent to the system | 49 | * Debug mode flag. This can be set on the command line. If debug |
57 | log, the daemon will not go to background, and will exit after processing | 50 | * mode is enabled, extra debugging output will be sent to the system |
58 | the first connection. */ | 51 | * log, the daemon will not go to background, and will exit after processing |
52 | * the first connection. | ||
53 | */ | ||
59 | int debug_flag = 0; | 54 | int debug_flag = 0; |
60 | 55 | ||
61 | /* Flag indicating that the daemon is being started from inetd. */ | 56 | /* Flag indicating that the daemon is being started from inetd. */ |
@@ -74,15 +69,21 @@ char **saved_argv; | |||
74 | the SIGHUP signal handler. */ | 69 | the SIGHUP signal handler. */ |
75 | int listen_sock; | 70 | int listen_sock; |
76 | 71 | ||
77 | /* Flags set in auth-rsa from authorized_keys flags. These are set in | 72 | /* the client's version string, passed by sshd2 in compat mode. |
78 | auth-rsa.c. */ | 73 | if != NULL, sshd will skip the version-number exchange */ |
74 | char *client_version_string = NULL; | ||
75 | |||
76 | /* Flags set in auth-rsa from authorized_keys flags. These are set in auth-rsa.c. */ | ||
79 | int no_port_forwarding_flag = 0; | 77 | int no_port_forwarding_flag = 0; |
80 | int no_agent_forwarding_flag = 0; | 78 | int no_agent_forwarding_flag = 0; |
81 | int no_x11_forwarding_flag = 0; | 79 | int no_x11_forwarding_flag = 0; |
82 | int no_pty_flag = 0; | 80 | int no_pty_flag = 0; |
83 | char *forced_command = NULL; /* RSA authentication "command=" option. */ | 81 | |
84 | struct envstring *custom_environment = NULL; | 82 | /* RSA authentication "command=" option. */ |
85 | /* RSA authentication "environment=" options. */ | 83 | char *forced_command = NULL; |
84 | |||
85 | /* RSA authentication "environment=" options. */ | ||
86 | struct envstring *custom_environment = NULL; | ||
86 | 87 | ||
87 | /* Session id for the current session. */ | 88 | /* Session id for the current session. */ |
88 | unsigned char session_id[16]; | 89 | unsigned char session_id[16]; |
@@ -93,13 +94,9 @@ unsigned char session_id[16]; | |||
93 | The private key contains BIGNUMs, and we do not (in principle) have | 94 | The private key contains BIGNUMs, and we do not (in principle) have |
94 | access to the internals of them, and locking just the structure is not | 95 | access to the internals of them, and locking just the structure is not |
95 | very useful. Currently, memory locking is not implemented. */ | 96 | very useful. Currently, memory locking is not implemented. */ |
96 | struct | 97 | struct { |
97 | { | 98 | RSA *private_key; /* Private part of server key. */ |
98 | /* Private part of server key. */ | 99 | RSA *host_key; /* Private part of host key. */ |
99 | RSA *private_key; | ||
100 | |||
101 | /* Private part of host key. */ | ||
102 | RSA *host_key; | ||
103 | } sensitive_data; | 100 | } sensitive_data; |
104 | 101 | ||
105 | /* Flag indicating whether the current session key has been used. This flag | 102 | /* Flag indicating whether the current session key has been used. This flag |
@@ -116,2454 +113,2458 @@ RSA *public_key; | |||
116 | /* Prototypes for various functions defined later in this file. */ | 113 | /* Prototypes for various functions defined later in this file. */ |
117 | void do_connection(); | 114 | void do_connection(); |
118 | void do_authentication(char *user); | 115 | void do_authentication(char *user); |
119 | void do_authloop(struct passwd *pw); | 116 | void do_authloop(struct passwd * pw); |
120 | void do_fake_authloop(char *user); | 117 | void do_fake_authloop(char *user); |
121 | void do_authenticated(struct passwd *pw); | 118 | void do_authenticated(struct passwd * pw); |
122 | void do_exec_pty(const char *command, int ptyfd, int ttyfd, | 119 | void do_exec_pty(const char *command, int ptyfd, int ttyfd, |
123 | const char *ttyname, struct passwd *pw, const char *term, | 120 | const char *ttyname, struct passwd * pw, const char *term, |
124 | const char *display, const char *auth_proto, | 121 | const char *display, const char *auth_proto, |
125 | const char *auth_data); | 122 | const char *auth_data); |
126 | void do_exec_no_pty(const char *command, struct passwd *pw, | 123 | void do_exec_no_pty(const char *command, struct passwd * pw, |
127 | const char *display, const char *auth_proto, | 124 | const char *display, const char *auth_proto, |
128 | const char *auth_data); | 125 | const char *auth_data); |
129 | void do_child(const char *command, struct passwd *pw, const char *term, | 126 | void do_child(const char *command, struct passwd * pw, const char *term, |
130 | const char *display, const char *auth_proto, | 127 | const char *display, const char *auth_proto, |
131 | const char *auth_data, const char *ttyname); | 128 | const char *auth_data, const char *ttyname); |
132 | 129 | ||
133 | #ifdef HAVE_LIBPAM | 130 | #ifdef HAVE_LIBPAM |
134 | static int pamconv(int num_msg, const struct pam_message **msg, | 131 | static int pamconv(int num_msg, const struct pam_message **msg, |
135 | struct pam_response **resp, void *appdata_ptr); | 132 | struct pam_response **resp, void *appdata_ptr); |
136 | void do_pam_account_and_session(char *username, char *remote_user, | 133 | void do_pam_account_and_session(char *username, char *remote_user, |
137 | const char *remote_host); | 134 | const char *remote_host); |
138 | void pam_cleanup_proc(void *context); | 135 | void pam_cleanup_proc(void *context); |
139 | 136 | ||
140 | static struct pam_conv conv = { | 137 | static struct pam_conv conv = { |
141 | pamconv, | 138 | pamconv, |
142 | NULL | 139 | NULL |
143 | }; | 140 | }; |
144 | struct pam_handle_t *pamh = NULL; | 141 | struct pam_handle_t *pamh = NULL; |
145 | const char *pampasswd = NULL; | 142 | const char *pampasswd = NULL; |
146 | char *pamconv_msg = NULL; | 143 | char *pamconv_msg = NULL; |
147 | 144 | ||
148 | static int pamconv(int num_msg, const struct pam_message **msg, | 145 | static int pamconv(int num_msg, const struct pam_message **msg, |
149 | struct pam_response **resp, void *appdata_ptr) | 146 | struct pam_response **resp, void *appdata_ptr) |
150 | { | 147 | { |
151 | struct pam_response *reply; | 148 | struct pam_response *reply; |
152 | int count; | 149 | int count; |
153 | size_t msg_len; | 150 | size_t msg_len; |
154 | char *p; | 151 | char *p; |
155 | 152 | ||
156 | /* PAM will free this later */ | 153 | /* PAM will free this later */ |
157 | reply = malloc(num_msg * sizeof(*reply)); | 154 | reply = malloc(num_msg * sizeof(*reply)); |
158 | if (reply == NULL) | 155 | if (reply == NULL) |
159 | return PAM_CONV_ERR; | 156 | return PAM_CONV_ERR; |
160 | 157 | ||
161 | for(count = 0; count < num_msg; count++) | 158 | for(count = 0; count < num_msg; count++) { |
162 | { | 159 | switch (msg[count]->msg_style) { |
163 | switch (msg[count]->msg_style) | 160 | case PAM_PROMPT_ECHO_OFF: |
164 | { | 161 | if (pampasswd == NULL) { |
165 | case PAM_PROMPT_ECHO_OFF: | 162 | free(reply); |
166 | if (pampasswd == NULL) | 163 | return PAM_CONV_ERR; |
167 | { | 164 | } |
168 | free(reply); | 165 | reply[count].resp_retcode = PAM_SUCCESS; |
169 | return PAM_CONV_ERR; | 166 | reply[count].resp = xstrdup(pampasswd); |
170 | } | 167 | break; |
171 | reply[count].resp_retcode = PAM_SUCCESS; | 168 | |
172 | reply[count].resp = xstrdup(pampasswd); | 169 | case PAM_TEXT_INFO: |
173 | break; | 170 | reply[count].resp_retcode = PAM_SUCCESS; |
174 | 171 | reply[count].resp = xstrdup(""); | |
175 | case PAM_TEXT_INFO: | 172 | |
176 | reply[count].resp_retcode = PAM_SUCCESS; | 173 | if (msg[count]->msg == NULL) |
177 | reply[count].resp = xstrdup(""); | 174 | break; |
178 | 175 | ||
179 | if (msg[count]->msg == NULL) | 176 | debug("Adding PAM message: %s", msg[count]->msg); |
180 | break; | 177 | |
181 | debug("Adding PAM message: %s", msg[count]->msg); | 178 | msg_len = strlen(msg[count]->msg); |
182 | 179 | if (pamconv_msg) { | |
183 | msg_len = strlen(msg[count]->msg); | 180 | size_t n = strlen(pamconv_msg); |
184 | if (pamconv_msg) | 181 | pamconv_msg = xrealloc(pamconv_msg, n + msg_len + 2); |
185 | { | 182 | p = pamconv_msg + n; |
186 | size_t n = strlen(pamconv_msg); | 183 | } else { |
187 | pamconv_msg = xrealloc(pamconv_msg, n + msg_len + 2); | 184 | pamconv_msg = p = xmalloc(msg_len + 2); |
188 | p = pamconv_msg + n; | 185 | } |
186 | memcpy(p, msg[count]->msg, msg_len); | ||
187 | p[msg_len] = '\n'; | ||
188 | p[msg_len + 1] = '\0'; | ||
189 | break; | ||
190 | |||
191 | case PAM_PROMPT_ECHO_ON: | ||
192 | case PAM_ERROR_MSG: | ||
193 | default: | ||
194 | free(reply); | ||
195 | return PAM_CONV_ERR; | ||
196 | } | ||
189 | } | 197 | } |
190 | else | 198 | |
191 | pamconv_msg = p = xmalloc(msg_len + 2); | 199 | *resp = reply; |
192 | memcpy(p, msg[count]->msg, msg_len); | 200 | |
193 | p[msg_len] = '\n'; | 201 | return PAM_SUCCESS; |
194 | p[msg_len + 1] = '\0'; | ||
195 | break; | ||
196 | |||
197 | case PAM_PROMPT_ECHO_ON: | ||
198 | case PAM_ERROR_MSG: | ||
199 | default: | ||
200 | free(reply); | ||
201 | return PAM_CONV_ERR; | ||
202 | } | ||
203 | } | ||
204 | |||
205 | *resp = reply; | ||
206 | |||
207 | return PAM_SUCCESS; | ||
208 | } | 202 | } |
209 | 203 | ||
210 | void pam_cleanup_proc(void *context) | 204 | void pam_cleanup_proc(void *context) |
211 | { | 205 | { |
212 | int pam_retval; | 206 | int pam_retval; |
213 | 207 | ||
214 | if (pamh != NULL) | 208 | if (pamh != NULL) |
215 | { | 209 | { |
216 | pam_retval = pam_close_session((pam_handle_t *)pamh, 0); | 210 | pam_retval = pam_close_session((pam_handle_t *)pamh, 0); |
217 | if (pam_retval != PAM_SUCCESS) | 211 | if (pam_retval != PAM_SUCCESS) { |
218 | { | 212 | log("Cannot close PAM session: %.200s", |
219 | log("Cannot close PAM session: %.200s", | 213 | PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); |
220 | PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); | 214 | } |
221 | } | 215 | |
222 | 216 | pam_retval = pam_end((pam_handle_t *)pamh, pam_retval); | |
223 | pam_retval = pam_end((pam_handle_t *)pamh, pam_retval); | 217 | if (pam_retval != PAM_SUCCESS) { |
224 | if (pam_retval != PAM_SUCCESS) | 218 | log("Cannot release PAM authentication: %.200s", |
225 | { | 219 | PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); |
226 | log("Cannot release PAM authentication: %.200s", | 220 | } |
227 | PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); | 221 | } |
228 | } | ||
229 | } | ||
230 | } | 222 | } |
231 | 223 | ||
232 | void do_pam_account_and_session(char *username, char *remote_user, | 224 | void do_pam_account_and_session(char *username, char *remote_user, |
233 | const char *remote_host) | 225 | const char *remote_host) |
234 | { | 226 | { |
235 | int pam_retval; | 227 | int pam_retval; |
236 | 228 | ||
237 | if (remote_host != NULL) | 229 | if (remote_host != NULL) { |
238 | { | 230 | debug("PAM setting rhost to \"%.200s\"", remote_host); |
239 | debug("PAM setting rhost to \"%.200s\"", remote_host); | 231 | pam_retval = pam_set_item((pam_handle_t *)pamh, PAM_RHOST, remote_host); |
240 | pam_retval = pam_set_item((pam_handle_t *)pamh, PAM_RHOST, remote_host); | 232 | if (pam_retval != PAM_SUCCESS) { |
241 | if (pam_retval != PAM_SUCCESS) | 233 | log("PAM set rhost failed: %.200s", PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); |
242 | { | 234 | do_fake_authloop(username); |
243 | log("PAM set rhost failed: %.200s", PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); | 235 | } |
244 | do_fake_authloop(username); | 236 | } |
245 | } | 237 | |
246 | } | 238 | if (remote_user != NULL) { |
247 | 239 | debug("PAM setting ruser to \"%.200s\"", remote_user); | |
248 | if (remote_user != NULL) | 240 | pam_retval = pam_set_item((pam_handle_t *)pamh, PAM_RUSER, remote_user); |
249 | { | 241 | if (pam_retval != PAM_SUCCESS) { |
250 | debug("PAM setting ruser to \"%.200s\"", remote_user); | 242 | log("PAM set ruser failed: %.200s", PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); |
251 | pam_retval = pam_set_item((pam_handle_t *)pamh, PAM_RUSER, remote_user); | 243 | do_fake_authloop(username); |
252 | if (pam_retval != PAM_SUCCESS) | 244 | } |
253 | { | 245 | } |
254 | log("PAM set ruser failed: %.200s", PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); | 246 | |
255 | do_fake_authloop(username); | 247 | pam_retval = pam_acct_mgmt((pam_handle_t *)pamh, 0); |
256 | } | 248 | if (pam_retval != PAM_SUCCESS) { |
257 | } | 249 | log("PAM rejected by account configuration: %.200s", PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); |
258 | 250 | do_fake_authloop(username); | |
259 | pam_retval = pam_acct_mgmt((pam_handle_t *)pamh, 0); | 251 | } |
260 | if (pam_retval != PAM_SUCCESS) | 252 | |
261 | { | 253 | pam_retval = pam_open_session((pam_handle_t *)pamh, 0); |
262 | log("PAM rejected by account configuration: %.200s", PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); | 254 | if (pam_retval != PAM_SUCCESS) { |
263 | do_fake_authloop(username); | 255 | log("PAM session setup failed: %.200s", PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); |
264 | } | 256 | do_fake_authloop(username); |
265 | 257 | } | |
266 | pam_retval = pam_open_session((pam_handle_t *)pamh, 0); | ||
267 | if (pam_retval != PAM_SUCCESS) | ||
268 | { | ||
269 | log("PAM session setup failed: %.200s", PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); | ||
270 | do_fake_authloop(username); | ||
271 | } | ||
272 | } | 258 | } |
273 | #endif /* HAVE_LIBPAM */ | 259 | #endif /* HAVE_LIBPAM */ |
274 | 260 | ||
275 | /* Signal handler for SIGHUP. Sshd execs itself when it receives SIGHUP; | 261 | /* |
276 | the effect is to reread the configuration file (and to regenerate | 262 | * Signal handler for SIGHUP. Sshd execs itself when it receives SIGHUP; |
277 | the server key). */ | 263 | * the effect is to reread the configuration file (and to regenerate |
278 | 264 | * the server key). | |
279 | void sighup_handler(int sig) | 265 | */ |
266 | void | ||
267 | sighup_handler(int sig) | ||
280 | { | 268 | { |
281 | received_sighup = 1; | 269 | received_sighup = 1; |
282 | signal(SIGHUP, sighup_handler); | 270 | signal(SIGHUP, sighup_handler); |
283 | } | 271 | } |
284 | 272 | ||
285 | /* Called from the main program after receiving SIGHUP. Restarts the | 273 | /* |
286 | server. */ | 274 | * Called from the main program after receiving SIGHUP. |
287 | 275 | * Restarts the server. | |
288 | void sighup_restart() | 276 | */ |
277 | void | ||
278 | sighup_restart() | ||
289 | { | 279 | { |
290 | log("Received SIGHUP; restarting."); | 280 | log("Received SIGHUP; restarting."); |
291 | close(listen_sock); | 281 | close(listen_sock); |
292 | execv(saved_argv[0], saved_argv); | 282 | execv(saved_argv[0], saved_argv); |
293 | log("RESTART FAILED: av0='%s', error: %s.", av0, strerror(errno)); | 283 | log("RESTART FAILED: av0='%s', error: %s.", av0, strerror(errno)); |
294 | exit(1); | 284 | exit(1); |
295 | } | 285 | } |
296 | 286 | ||
297 | /* Generic signal handler for terminating signals in the master daemon. | 287 | /* |
298 | These close the listen socket; not closing it seems to cause "Address | 288 | * Generic signal handler for terminating signals in the master daemon. |
299 | already in use" problems on some machines, which is inconvenient. */ | 289 | * These close the listen socket; not closing it seems to cause "Address |
300 | 290 | * already in use" problems on some machines, which is inconvenient. | |
301 | void sigterm_handler(int sig) | 291 | */ |
292 | void | ||
293 | sigterm_handler(int sig) | ||
302 | { | 294 | { |
303 | log("Received signal %d; terminating.", sig); | 295 | log("Received signal %d; terminating.", sig); |
304 | close(listen_sock); | 296 | close(listen_sock); |
305 | exit(255); | 297 | exit(255); |
306 | } | 298 | } |
307 | 299 | ||
308 | /* SIGCHLD handler. This is called whenever a child dies. This will then | 300 | /* |
309 | reap any zombies left by exited c. */ | 301 | * SIGCHLD handler. This is called whenever a child dies. This will then |
310 | 302 | * reap any zombies left by exited c. | |
311 | void main_sigchld_handler(int sig) | 303 | */ |
304 | void | ||
305 | main_sigchld_handler(int sig) | ||
312 | { | 306 | { |
313 | int save_errno = errno; | 307 | int save_errno = errno; |
314 | int status; | 308 | int status; |
315 | 309 | ||
316 | while (waitpid(-1, &status, WNOHANG) > 0) | 310 | while (waitpid(-1, &status, WNOHANG) > 0) |
317 | ; | 311 | ; |
318 | 312 | ||
319 | signal(SIGCHLD, main_sigchld_handler); | 313 | signal(SIGCHLD, main_sigchld_handler); |
320 | errno = save_errno; | 314 | errno = save_errno; |
321 | } | 315 | } |
322 | 316 | ||
323 | /* Signal handler for the alarm after the login grace period has expired. */ | 317 | /* |
324 | 318 | * Signal handler for the alarm after the login grace period has expired. | |
325 | void grace_alarm_handler(int sig) | 319 | */ |
320 | void | ||
321 | grace_alarm_handler(int sig) | ||
326 | { | 322 | { |
327 | /* Close the connection. */ | 323 | /* Close the connection. */ |
328 | packet_close(); | 324 | packet_close(); |
329 | |||
330 | /* Log error and exit. */ | ||
331 | fatal("Timeout before authentication."); | ||
332 | } | ||
333 | 325 | ||
334 | /* Signal handler for the key regeneration alarm. Note that this | 326 | /* Log error and exit. */ |
335 | alarm only occurs in the daemon waiting for connections, and it does not | 327 | fatal("Timeout before authentication for %s.", get_remote_ipaddr()); |
336 | do anything with the private key or random state before forking. Thus there | 328 | } |
337 | should be no concurrency control/asynchronous execution problems. */ | ||
338 | 329 | ||
339 | void key_regeneration_alarm(int sig) | 330 | /* |
331 | * convert ssh auth msg type into description | ||
332 | */ | ||
333 | char * | ||
334 | get_authname(int type) | ||
340 | { | 335 | { |
341 | int save_errno = errno; | 336 | switch (type) { |
342 | 337 | case SSH_CMSG_AUTH_PASSWORD: | |
343 | /* Check if we should generate a new key. */ | 338 | return "password"; |
344 | if (key_used) | 339 | case SSH_CMSG_AUTH_RSA: |
345 | { | 340 | return "rsa"; |
346 | /* This should really be done in the background. */ | 341 | case SSH_CMSG_AUTH_RHOSTS_RSA: |
347 | log("Generating new %d bit RSA key.", options.server_key_bits); | 342 | return "rhosts-rsa"; |
348 | 343 | case SSH_CMSG_AUTH_RHOSTS: | |
349 | if (sensitive_data.private_key != NULL) | 344 | return "rhosts"; |
350 | RSA_free(sensitive_data.private_key); | 345 | #ifdef KRB4 |
351 | sensitive_data.private_key = RSA_new(); | 346 | case SSH_CMSG_AUTH_KERBEROS: |
352 | 347 | return "kerberos"; | |
353 | if (public_key != NULL) | 348 | #endif |
354 | RSA_free(public_key); | 349 | #ifdef SKEY |
355 | public_key = RSA_new(); | 350 | case SSH_CMSG_AUTH_TIS_RESPONSE: |
356 | 351 | return "s/key"; | |
357 | rsa_generate_key(sensitive_data.private_key, public_key, | 352 | #endif |
358 | options.server_key_bits); | 353 | } |
359 | arc4random_stir(); | 354 | fatal("get_authname: unknown auth %d: internal error", type); |
360 | key_used = 0; | 355 | return NULL; |
361 | log("RSA key generation complete."); | ||
362 | } | ||
363 | |||
364 | /* Reschedule the alarm. */ | ||
365 | signal(SIGALRM, key_regeneration_alarm); | ||
366 | alarm(options.key_regeneration_time); | ||
367 | errno = save_errno; | ||
368 | } | 356 | } |
369 | 357 | ||
370 | /* Main program for the daemon. */ | 358 | /* |
359 | * Signal handler for the key regeneration alarm. Note that this | ||
360 | * alarm only occurs in the daemon waiting for connections, and it does not | ||
361 | * do anything with the private key or random state before forking. | ||
362 | * Thus there should be no concurrency control/asynchronous execution | ||
363 | * problems. | ||
364 | */ | ||
365 | void | ||
366 | key_regeneration_alarm(int sig) | ||
367 | { | ||
368 | int save_errno = errno; | ||
369 | |||
370 | /* Check if we should generate a new key. */ | ||
371 | if (key_used) { | ||
372 | /* This should really be done in the background. */ | ||
373 | log("Generating new %d bit RSA key.", options.server_key_bits); | ||
374 | |||
375 | if (sensitive_data.private_key != NULL) | ||
376 | RSA_free(sensitive_data.private_key); | ||
377 | sensitive_data.private_key = RSA_new(); | ||
378 | |||
379 | if (public_key != NULL) | ||
380 | RSA_free(public_key); | ||
381 | public_key = RSA_new(); | ||
382 | |||
383 | rsa_generate_key(sensitive_data.private_key, public_key, | ||
384 | options.server_key_bits); | ||
385 | arc4random_stir(); | ||
386 | key_used = 0; | ||
387 | log("RSA key generation complete."); | ||
388 | } | ||
389 | /* Reschedule the alarm. */ | ||
390 | signal(SIGALRM, key_regeneration_alarm); | ||
391 | alarm(options.key_regeneration_time); | ||
392 | errno = save_errno; | ||
393 | } | ||
371 | 394 | ||
395 | /* | ||
396 | * Main program for the daemon. | ||
397 | */ | ||
372 | int | 398 | int |
373 | main(int ac, char **av) | 399 | main(int ac, char **av) |
374 | { | 400 | { |
375 | extern char *optarg; | 401 | extern char *optarg; |
376 | extern int optind; | 402 | extern int optind; |
377 | int opt, aux, sock_in, sock_out, newsock, i, pid, on = 1; | 403 | int opt, aux, sock_in, sock_out, newsock, i, pid, on = 1; |
378 | int remote_major, remote_minor; | 404 | int remote_major, remote_minor; |
379 | int silentrsa = 0; | 405 | int silentrsa = 0; |
380 | struct sockaddr_in sin; | 406 | struct sockaddr_in sin; |
381 | char buf[100]; /* Must not be larger than remote_version. */ | 407 | char buf[100]; /* Must not be larger than remote_version. */ |
382 | char remote_version[100]; /* Must be at least as big as buf. */ | 408 | char remote_version[100]; /* Must be at least as big as buf. */ |
383 | int remote_port; | 409 | const char *remote_ip; |
384 | char *comment; | 410 | int remote_port; |
385 | FILE *f; | 411 | char *comment; |
386 | struct linger linger; | 412 | FILE *f; |
387 | 413 | struct linger linger; | |
388 | /* Save argv[0]. */ | 414 | |
389 | saved_argv = av; | 415 | /* Save argv[0]. */ |
390 | if (strchr(av[0], '/')) | 416 | saved_argv = av; |
391 | av0 = strrchr(av[0], '/') + 1; | 417 | if (strchr(av[0], '/')) |
392 | else | 418 | av0 = strrchr(av[0], '/') + 1; |
393 | av0 = av[0]; | 419 | else |
394 | 420 | av0 = av[0]; | |
395 | /* Initialize configuration options to their default values. */ | 421 | |
396 | initialize_server_options(&options); | 422 | /* Initialize configuration options to their default values. */ |
397 | 423 | initialize_server_options(&options); | |
398 | /* Parse command-line arguments. */ | 424 | |
399 | while ((opt = getopt(ac, av, "f:p:b:k:h:g:diqQ")) != EOF) | 425 | /* Parse command-line arguments. */ |
400 | { | 426 | while ((opt = getopt(ac, av, "f:p:b:k:h:g:V:diqQ")) != EOF) { |
401 | switch (opt) | 427 | switch (opt) { |
402 | { | 428 | case 'f': |
403 | case 'f': | 429 | config_file_name = optarg; |
404 | config_file_name = optarg; | 430 | break; |
405 | break; | 431 | case 'd': |
406 | case 'd': | 432 | debug_flag = 1; |
407 | debug_flag = 1; | 433 | options.log_level = SYSLOG_LEVEL_DEBUG; |
408 | options.log_level = SYSLOG_LEVEL_DEBUG; | 434 | break; |
409 | break; | 435 | case 'i': |
410 | case 'i': | 436 | inetd_flag = 1; |
411 | inetd_flag = 1; | 437 | break; |
412 | break; | 438 | case 'Q': |
413 | case 'Q': | 439 | silentrsa = 1; |
414 | silentrsa = 1; | 440 | break; |
415 | break; | 441 | case 'q': |
416 | case 'q': | 442 | options.log_level = SYSLOG_LEVEL_QUIET; |
417 | options.log_level = SYSLOG_LEVEL_QUIET; | 443 | break; |
418 | break; | 444 | case 'b': |
419 | case 'b': | 445 | options.server_key_bits = atoi(optarg); |
420 | options.server_key_bits = atoi(optarg); | 446 | break; |
421 | break; | 447 | case 'p': |
422 | case 'p': | 448 | options.port = atoi(optarg); |
423 | options.port = atoi(optarg); | 449 | break; |
424 | break; | 450 | case 'g': |
425 | case 'g': | 451 | options.login_grace_time = atoi(optarg); |
426 | options.login_grace_time = atoi(optarg); | 452 | break; |
427 | break; | 453 | case 'k': |
428 | case 'k': | 454 | options.key_regeneration_time = atoi(optarg); |
429 | options.key_regeneration_time = atoi(optarg); | 455 | break; |
430 | break; | 456 | case 'h': |
431 | case 'h': | 457 | options.host_key_file = optarg; |
432 | options.host_key_file = optarg; | 458 | break; |
433 | break; | 459 | case 'V': |
434 | case '?': | 460 | client_version_string = optarg; |
435 | default: | 461 | /* only makes sense with inetd_flag, i.e. no listen() */ |
436 | fprintf(stderr, "sshd version %s\n", SSH_VERSION); | 462 | inetd_flag = 1; |
437 | fprintf(stderr, "Usage: %s [options]\n", av0); | 463 | break; |
438 | fprintf(stderr, "Options:\n"); | 464 | case '?': |
439 | fprintf(stderr, " -f file Configuration file (default %s/sshd_config)\n", ETCDIR); | 465 | default: |
440 | fprintf(stderr, " -d Debugging mode\n"); | 466 | fprintf(stderr, "sshd version %s\n", SSH_VERSION); |
441 | fprintf(stderr, " -i Started from inetd\n"); | 467 | fprintf(stderr, "Usage: %s [options]\n", av0); |
442 | fprintf(stderr, " -q Quiet (no logging)\n"); | 468 | fprintf(stderr, "Options:\n"); |
443 | fprintf(stderr, " -p port Listen on the specified port (default: 22)\n"); | 469 | fprintf(stderr, " -f file Configuration file (default %s/sshd_config)\n", ETCDIR); |
444 | fprintf(stderr, " -k seconds Regenerate server key every this many seconds (default: 3600)\n"); | 470 | fprintf(stderr, " -d Debugging mode\n"); |
445 | fprintf(stderr, " -g seconds Grace period for authentication (default: 300)\n"); | 471 | fprintf(stderr, " -i Started from inetd\n"); |
446 | fprintf(stderr, " -b bits Size of server RSA key (default: 768 bits)\n"); | 472 | fprintf(stderr, " -q Quiet (no logging)\n"); |
447 | fprintf(stderr, " -h file File from which to read host key (default: %s)\n", | 473 | fprintf(stderr, " -p port Listen on the specified port (default: 22)\n"); |
448 | HOST_KEY_FILE); | 474 | fprintf(stderr, " -k seconds Regenerate server key every this many seconds (default: 3600)\n"); |
449 | exit(1); | 475 | fprintf(stderr, " -g seconds Grace period for authentication (default: 300)\n"); |
476 | fprintf(stderr, " -b bits Size of server RSA key (default: 768 bits)\n"); | ||
477 | fprintf(stderr, " -h file File from which to read host key (default: %s)\n", | ||
478 | HOST_KEY_FILE); | ||
479 | exit(1); | ||
480 | } | ||
481 | } | ||
482 | |||
483 | /* check if RSA support exists */ | ||
484 | if (rsa_alive() == 0) { | ||
485 | if (silentrsa == 0) | ||
486 | printf("sshd: no RSA support in libssl and libcrypto -- exiting. See ssl(8)\n"); | ||
487 | log("no RSA support in libssl and libcrypto -- exiting. See ssl(8)"); | ||
488 | exit(1); | ||
489 | } | ||
490 | /* Read server configuration options from the configuration file. */ | ||
491 | read_server_config(&options, config_file_name); | ||
492 | |||
493 | /* Fill in default values for those options not explicitly set. */ | ||
494 | fill_default_server_options(&options); | ||
495 | |||
496 | /* Check certain values for sanity. */ | ||
497 | if (options.server_key_bits < 512 || | ||
498 | options.server_key_bits > 32768) { | ||
499 | fprintf(stderr, "Bad server key size.\n"); | ||
500 | exit(1); | ||
501 | } | ||
502 | if (options.port < 1 || options.port > 65535) { | ||
503 | fprintf(stderr, "Bad port number.\n"); | ||
504 | exit(1); | ||
505 | } | ||
506 | /* Check that there are no remaining arguments. */ | ||
507 | if (optind < ac) { | ||
508 | fprintf(stderr, "Extra argument %s.\n", av[optind]); | ||
509 | exit(1); | ||
450 | } | 510 | } |
451 | } | 511 | /* Force logging to stderr while loading the private host key |
452 | 512 | unless started from inetd */ | |
453 | /* check if RSA support exists */ | 513 | log_init(av0, options.log_level, options.log_facility, !inetd_flag); |
454 | if (rsa_alive() == 0) { | 514 | |
455 | if (silentrsa == 0) | 515 | debug("sshd version %.100s", SSH_VERSION); |
456 | printf("sshd: no RSA support in libssl and libcrypto -- exiting. See ssl(8)\n"); | 516 | |
457 | log("no RSA support in libssl and libcrypto -- exiting. See ssl(8)"); | 517 | sensitive_data.host_key = RSA_new(); |
458 | exit(1); | 518 | errno = 0; |
459 | } | 519 | /* Load the host key. It must have empty passphrase. */ |
460 | 520 | if (!load_private_key(options.host_key_file, "", | |
461 | /* Read server configuration options from the configuration file. */ | 521 | sensitive_data.host_key, &comment)) { |
462 | read_server_config(&options, config_file_name); | 522 | error("Could not load host key: %.200s: %.100s", |
463 | 523 | options.host_key_file, strerror(errno)); | |
464 | /* Fill in default values for those options not explicitly set. */ | 524 | exit(1); |
465 | fill_default_server_options(&options); | 525 | } |
466 | 526 | xfree(comment); | |
467 | /* Check certain values for sanity. */ | 527 | |
468 | if (options.server_key_bits < 512 || | 528 | /* Initialize the log (it is reinitialized below in case we |
469 | options.server_key_bits > 32768) | 529 | forked). */ |
470 | { | 530 | if (debug_flag && !inetd_flag) |
471 | fprintf(stderr, "Bad server key size.\n"); | 531 | log_stderr = 1; |
472 | exit(1); | 532 | log_init(av0, options.log_level, options.log_facility, log_stderr); |
473 | } | 533 | |
474 | if (options.port < 1 || options.port > 65535) | 534 | /* If not in debugging mode, and not started from inetd, |
475 | { | 535 | disconnect from the controlling terminal, and fork. The |
476 | fprintf(stderr, "Bad port number.\n"); | 536 | original process exits. */ |
477 | exit(1); | 537 | if (!debug_flag && !inetd_flag) { |
478 | } | ||
479 | |||
480 | /* Check that there are no remaining arguments. */ | ||
481 | if (optind < ac) | ||
482 | { | ||
483 | fprintf(stderr, "Extra argument %s.\n", av[optind]); | ||
484 | exit(1); | ||
485 | } | ||
486 | |||
487 | /* Force logging to stderr while loading the private host key | ||
488 | unless started from inetd */ | ||
489 | log_init(av0, options.log_level, options.log_facility, !inetd_flag); | ||
490 | |||
491 | debug("sshd version %.100s", SSH_VERSION); | ||
492 | |||
493 | sensitive_data.host_key = RSA_new(); | ||
494 | errno = 0; | ||
495 | /* Load the host key. It must have empty passphrase. */ | ||
496 | if (!load_private_key(options.host_key_file, "", | ||
497 | sensitive_data.host_key, &comment)) | ||
498 | { | ||
499 | error("Could not load host key: %.200s: %.100s", | ||
500 | options.host_key_file, strerror(errno)); | ||
501 | exit(1); | ||
502 | } | ||
503 | xfree(comment); | ||
504 | |||
505 | /* Initialize the log (it is reinitialized below in case we forked). */ | ||
506 | if (debug_flag && !inetd_flag) | ||
507 | log_stderr = 1; | ||
508 | log_init(av0, options.log_level, options.log_facility, log_stderr); | ||
509 | |||
510 | /* If not in debugging mode, and not started from inetd, disconnect from | ||
511 | the controlling terminal, and fork. The original process exits. */ | ||
512 | if (!debug_flag && !inetd_flag) | ||
513 | { | ||
514 | #ifdef TIOCNOTTY | 538 | #ifdef TIOCNOTTY |
515 | int fd; | 539 | int fd; |
516 | #endif /* TIOCNOTTY */ | 540 | #endif /* TIOCNOTTY */ |
517 | if (daemon(0, 0) < 0) | 541 | if (daemon(0, 0) < 0) |
518 | fatal("daemon() failed: %.200s", strerror(errno)); | 542 | fatal("daemon() failed: %.200s", strerror(errno)); |
519 | 543 | ||
520 | /* Disconnect from the controlling tty. */ | 544 | /* Disconnect from the controlling tty. */ |
521 | #ifdef TIOCNOTTY | 545 | #ifdef TIOCNOTTY |
522 | fd = open("/dev/tty", O_RDWR|O_NOCTTY); | 546 | fd = open("/dev/tty", O_RDWR | O_NOCTTY); |
523 | if (fd >= 0) | 547 | if (fd >= 0) { |
524 | { | 548 | (void) ioctl(fd, TIOCNOTTY, NULL); |
525 | (void)ioctl(fd, TIOCNOTTY, NULL); | 549 | close(fd); |
526 | close(fd); | 550 | } |
527 | } | ||
528 | #endif /* TIOCNOTTY */ | 551 | #endif /* TIOCNOTTY */ |
529 | } | ||
530 | |||
531 | /* Reinitialize the log (because of the fork above). */ | ||
532 | log_init(av0, options.log_level, options.log_facility, log_stderr); | ||
533 | |||
534 | /* Check that server and host key lengths differ sufficiently. This is | ||
535 | necessary to make double encryption work with rsaref. Oh, I hate | ||
536 | software patents. I dont know if this can go? Niels */ | ||
537 | if (options.server_key_bits > | ||
538 | BN_num_bits(sensitive_data.host_key->n) - SSH_KEY_BITS_RESERVED && | ||
539 | options.server_key_bits < | ||
540 | BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED) | ||
541 | { | ||
542 | options.server_key_bits = | ||
543 | BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED; | ||
544 | debug("Forcing server key to %d bits to make it differ from host key.", | ||
545 | options.server_key_bits); | ||
546 | } | ||
547 | |||
548 | /* Do not display messages to stdout in RSA code. */ | ||
549 | rsa_set_verbose(0); | ||
550 | |||
551 | /* Initialize the random number generator. */ | ||
552 | arc4random_stir(); | ||
553 | |||
554 | /* Chdir to the root directory so that the current disk can be unmounted | ||
555 | if desired. */ | ||
556 | chdir("/"); | ||
557 | |||
558 | /* Close connection cleanly after attack. */ | ||
559 | cipher_attack_detected = packet_disconnect; | ||
560 | |||
561 | /* Start listening for a socket, unless started from inetd. */ | ||
562 | if (inetd_flag) | ||
563 | { | ||
564 | int s1, s2; | ||
565 | s1 = dup(0); /* Make sure descriptors 0, 1, and 2 are in use. */ | ||
566 | s2 = dup(s1); | ||
567 | sock_in = dup(0); | ||
568 | sock_out = dup(1); | ||
569 | /* We intentionally do not close the descriptors 0, 1, and 2 as our | ||
570 | code for setting the descriptors won\'t work if ttyfd happens to | ||
571 | be one of those. */ | ||
572 | debug("inetd sockets after dupping: %d, %d", sock_in, sock_out); | ||
573 | |||
574 | public_key = RSA_new(); | ||
575 | sensitive_data.private_key = RSA_new(); | ||
576 | /* Generate an rsa key. */ | ||
577 | log("Generating %d bit RSA key.", options.server_key_bits); | ||
578 | rsa_generate_key(sensitive_data.private_key, public_key, | ||
579 | options.server_key_bits); | ||
580 | arc4random_stir(); | ||
581 | log("RSA key generation complete."); | ||
582 | } | ||
583 | else | ||
584 | { | ||
585 | /* Create socket for listening. */ | ||
586 | listen_sock = socket(AF_INET, SOCK_STREAM, 0); | ||
587 | if (listen_sock < 0) | ||
588 | fatal("socket: %.100s", strerror(errno)); | ||
589 | |||
590 | /* Set socket options. We try to make the port reusable and have it | ||
591 | close as fast as possible without waiting in unnecessary wait states | ||
592 | on close. */ | ||
593 | setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, | ||
594 | sizeof(on)); | ||
595 | linger.l_onoff = 1; | ||
596 | linger.l_linger = 5; | ||
597 | setsockopt(listen_sock, SOL_SOCKET, SO_LINGER, (void *)&linger, | ||
598 | sizeof(linger)); | ||
599 | |||
600 | /* Initialize the socket address. */ | ||
601 | memset(&sin, 0, sizeof(sin)); | ||
602 | sin.sin_family = AF_INET; | ||
603 | sin.sin_addr = options.listen_addr; | ||
604 | sin.sin_port = htons(options.port); | ||
605 | |||
606 | /* Bind the socket to the desired port. */ | ||
607 | if (bind(listen_sock, (struct sockaddr *)&sin, sizeof(sin)) < 0) | ||
608 | { | ||
609 | error("bind: %.100s", strerror(errno)); | ||
610 | shutdown(listen_sock, SHUT_RDWR); | ||
611 | close(listen_sock); | ||
612 | fatal("Bind to port %d failed.", options.port); | ||
613 | } | 552 | } |
614 | 553 | /* Reinitialize the log (because of the fork above). */ | |
615 | if (!debug_flag) | 554 | log_init(av0, options.log_level, options.log_facility, log_stderr); |
616 | { | 555 | |
617 | /* Record our pid in /etc/sshd_pid to make it easier to kill the | 556 | /* Check that server and host key lengths differ sufficiently. |
618 | correct sshd. We don\'t want to do this before the bind above | 557 | This is necessary to make double encryption work with rsaref. |
619 | because the bind will fail if there already is a daemon, and this | 558 | Oh, I hate software patents. I dont know if this can go? Niels */ |
620 | will overwrite any old pid in the file. */ | 559 | if (options.server_key_bits > |
621 | f = fopen(SSH_DAEMON_PID_FILE, "w"); | 560 | BN_num_bits(sensitive_data.host_key->n) - SSH_KEY_BITS_RESERVED && |
622 | if (f) | 561 | options.server_key_bits < |
623 | { | 562 | BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED) { |
624 | fprintf(f, "%u\n", (unsigned int)getpid()); | 563 | options.server_key_bits = |
625 | fclose(f); | 564 | BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED; |
626 | } | 565 | debug("Forcing server key to %d bits to make it differ from host key.", |
566 | options.server_key_bits); | ||
627 | } | 567 | } |
628 | 568 | /* Do not display messages to stdout in RSA code. */ | |
629 | /* Start listening on the port. */ | 569 | rsa_set_verbose(0); |
630 | log("Server listening on port %d.", options.port); | 570 | |
631 | if (listen(listen_sock, 5) < 0) | 571 | /* Initialize the random number generator. */ |
632 | fatal("listen: %.100s", strerror(errno)); | 572 | arc4random_stir(); |
633 | 573 | ||
634 | public_key = RSA_new(); | 574 | /* Chdir to the root directory so that the current disk can be |
635 | sensitive_data.private_key = RSA_new(); | 575 | unmounted if desired. */ |
636 | /* Generate an rsa key. */ | 576 | chdir("/"); |
637 | log("Generating %d bit RSA key.", options.server_key_bits); | 577 | |
638 | rsa_generate_key(sensitive_data.private_key, public_key, | 578 | /* Close connection cleanly after attack. */ |
639 | options.server_key_bits); | 579 | cipher_attack_detected = packet_disconnect; |
640 | arc4random_stir(); | 580 | |
641 | log("RSA key generation complete."); | 581 | /* Start listening for a socket, unless started from inetd. */ |
642 | 582 | if (inetd_flag) { | |
643 | /* Schedule server key regeneration alarm. */ | 583 | int s1, s2; |
644 | signal(SIGALRM, key_regeneration_alarm); | 584 | s1 = dup(0); /* Make sure descriptors 0, 1, and 2 are in use. */ |
645 | alarm(options.key_regeneration_time); | 585 | s2 = dup(s1); |
646 | 586 | sock_in = dup(0); | |
647 | /* Arrange to restart on SIGHUP. The handler needs listen_sock. */ | 587 | sock_out = dup(1); |
648 | signal(SIGHUP, sighup_handler); | 588 | /* We intentionally do not close the descriptors 0, 1, and 2 |
649 | signal(SIGTERM, sigterm_handler); | 589 | as our code for setting the descriptors won\'t work |
650 | signal(SIGQUIT, sigterm_handler); | 590 | if ttyfd happens to be one of those. */ |
651 | 591 | debug("inetd sockets after dupping: %d, %d", sock_in, sock_out); | |
652 | /* Arrange SIGCHLD to be caught. */ | 592 | |
653 | signal(SIGCHLD, main_sigchld_handler); | 593 | public_key = RSA_new(); |
654 | 594 | sensitive_data.private_key = RSA_new(); | |
655 | /* Stay listening for connections until the system crashes or the | 595 | |
656 | daemon is killed with a signal. */ | 596 | log("Generating %d bit RSA key.", options.server_key_bits); |
657 | for (;;) | 597 | rsa_generate_key(sensitive_data.private_key, public_key, |
658 | { | 598 | options.server_key_bits); |
659 | if (received_sighup) | 599 | arc4random_stir(); |
660 | sighup_restart(); | 600 | log("RSA key generation complete."); |
661 | /* Wait in accept until there is a connection. */ | 601 | } else { |
662 | aux = sizeof(sin); | 602 | /* Create socket for listening. */ |
663 | newsock = accept(listen_sock, (struct sockaddr *)&sin, &aux); | 603 | listen_sock = socket(AF_INET, SOCK_STREAM, 0); |
664 | if (received_sighup) | 604 | if (listen_sock < 0) |
665 | sighup_restart(); | 605 | fatal("socket: %.100s", strerror(errno)); |
666 | if (newsock < 0) | 606 | |
667 | { | 607 | /* Set socket options. We try to make the port reusable |
668 | if (errno == EINTR) | 608 | and have it close as fast as possible without waiting |
669 | continue; | 609 | in unnecessary wait states on close. */ |
670 | error("accept: %.100s", strerror(errno)); | 610 | setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, (void *) &on, |
671 | continue; | 611 | sizeof(on)); |
672 | } | 612 | linger.l_onoff = 1; |
673 | 613 | linger.l_linger = 5; | |
674 | /* Got connection. Fork a child to handle it, unless we are in | 614 | setsockopt(listen_sock, SOL_SOCKET, SO_LINGER, (void *) &linger, |
675 | debugging mode. */ | 615 | sizeof(linger)); |
676 | if (debug_flag) | 616 | |
677 | { | 617 | /* Initialize the socket address. */ |
678 | /* In debugging mode. Close the listening socket, and start | 618 | memset(&sin, 0, sizeof(sin)); |
679 | processing the connection without forking. */ | 619 | sin.sin_family = AF_INET; |
680 | debug("Server will not fork when running in debugging mode."); | 620 | sin.sin_addr = options.listen_addr; |
681 | close(listen_sock); | 621 | sin.sin_port = htons(options.port); |
682 | sock_in = newsock; | 622 | |
683 | sock_out = newsock; | 623 | /* Bind the socket to the desired port. */ |
684 | pid = getpid(); | 624 | if (bind(listen_sock, (struct sockaddr *) & sin, sizeof(sin)) < 0) { |
685 | break; | 625 | error("bind: %.100s", strerror(errno)); |
686 | } | 626 | shutdown(listen_sock, SHUT_RDWR); |
687 | else | 627 | close(listen_sock); |
688 | { | 628 | fatal("Bind to port %d failed.", options.port); |
689 | /* Normal production daemon. Fork, and have the child process | 629 | } |
690 | the connection. The parent continues listening. */ | 630 | if (!debug_flag) { |
691 | if ((pid = fork()) == 0) | 631 | /* Record our pid in /etc/sshd_pid to make it |
692 | { | 632 | easier to kill the correct sshd. We don\'t |
693 | /* Child. Close the listening socket, and start using | 633 | want to do this before the bind above because |
694 | the accepted socket. Reinitialize logging (since our | 634 | the bind will fail if there already is a |
695 | pid has changed). We break out of the loop to handle | 635 | daemon, and this will overwrite any old pid in |
696 | the connection. */ | 636 | the file. */ |
697 | close(listen_sock); | 637 | f = fopen(SSH_DAEMON_PID_FILE, "w"); |
698 | sock_in = newsock; | 638 | if (f) { |
699 | sock_out = newsock; | 639 | fprintf(f, "%u\n", (unsigned int) getpid()); |
700 | log_init(av0, options.log_level, options.log_facility, log_stderr); | 640 | fclose(f); |
701 | break; | 641 | } |
702 | } | 642 | } |
703 | } | ||
704 | 643 | ||
705 | /* Parent. Stay in the loop. */ | 644 | log("Server listening on port %d.", options.port); |
706 | if (pid < 0) | 645 | if (listen(listen_sock, 5) < 0) |
707 | error("fork: %.100s", strerror(errno)); | 646 | fatal("listen: %.100s", strerror(errno)); |
708 | else | 647 | |
709 | debug("Forked child %d.", pid); | 648 | public_key = RSA_new(); |
649 | sensitive_data.private_key = RSA_new(); | ||
650 | |||
651 | log("Generating %d bit RSA key.", options.server_key_bits); | ||
652 | rsa_generate_key(sensitive_data.private_key, public_key, | ||
653 | options.server_key_bits); | ||
654 | arc4random_stir(); | ||
655 | log("RSA key generation complete."); | ||
656 | |||
657 | /* Schedule server key regeneration alarm. */ | ||
658 | signal(SIGALRM, key_regeneration_alarm); | ||
659 | alarm(options.key_regeneration_time); | ||
660 | |||
661 | /* Arrange to restart on SIGHUP. The handler needs listen_sock. */ | ||
662 | signal(SIGHUP, sighup_handler); | ||
663 | signal(SIGTERM, sigterm_handler); | ||
664 | signal(SIGQUIT, sigterm_handler); | ||
665 | |||
666 | /* Arrange SIGCHLD to be caught. */ | ||
667 | signal(SIGCHLD, main_sigchld_handler); | ||
668 | |||
669 | /* Stay listening for connections until the system crashes | ||
670 | or the daemon is killed with a signal. */ | ||
671 | for (;;) { | ||
672 | if (received_sighup) | ||
673 | sighup_restart(); | ||
674 | /* Wait in accept until there is a connection. */ | ||
675 | aux = sizeof(sin); | ||
676 | newsock = accept(listen_sock, (struct sockaddr *) & sin, &aux); | ||
677 | if (received_sighup) | ||
678 | sighup_restart(); | ||
679 | if (newsock < 0) { | ||
680 | if (errno == EINTR) | ||
681 | continue; | ||
682 | error("accept: %.100s", strerror(errno)); | ||
683 | continue; | ||
684 | } | ||
685 | /* Got connection. Fork a child to handle it, | ||
686 | unless we are in debugging mode. */ | ||
687 | if (debug_flag) { | ||
688 | /* In debugging mode. Close the listening | ||
689 | socket, and start processing the | ||
690 | connection without forking. */ | ||
691 | debug("Server will not fork when running in debugging mode."); | ||
692 | close(listen_sock); | ||
693 | sock_in = newsock; | ||
694 | sock_out = newsock; | ||
695 | pid = getpid(); | ||
696 | break; | ||
697 | } else { | ||
698 | /* Normal production daemon. Fork, and | ||
699 | have the child process the connection. | ||
700 | The parent continues listening. */ | ||
701 | if ((pid = fork()) == 0) { | ||
702 | /* Child. Close the listening | ||
703 | socket, and start using the | ||
704 | accepted socket. Reinitialize | ||
705 | logging (since our pid has | ||
706 | changed). We break out of the | ||
707 | loop to handle the connection. */ | ||
708 | close(listen_sock); | ||
709 | sock_in = newsock; | ||
710 | sock_out = newsock; | ||
711 | log_init(av0, options.log_level, options.log_facility, log_stderr); | ||
712 | break; | ||
713 | } | ||
714 | } | ||
715 | |||
716 | /* Parent. Stay in the loop. */ | ||
717 | if (pid < 0) | ||
718 | error("fork: %.100s", strerror(errno)); | ||
719 | else | ||
720 | debug("Forked child %d.", pid); | ||
721 | |||
722 | /* Mark that the key has been used (it was "given" to the child). */ | ||
723 | key_used = 1; | ||
724 | |||
725 | arc4random_stir(); | ||
726 | |||
727 | /* Close the new socket (the child is now taking care of it). */ | ||
728 | close(newsock); | ||
729 | } | ||
730 | } | ||
710 | 731 | ||
711 | /* Mark that the key has been used (it was "given" to the child). */ | 732 | /* This is the child processing a new connection. */ |
712 | key_used = 1; | 733 | |
734 | /* Disable the key regeneration alarm. We will not regenerate the | ||
735 | key since we are no longer in a position to give it to anyone. | ||
736 | We will not restart on SIGHUP since it no longer makes sense. */ | ||
737 | alarm(0); | ||
738 | signal(SIGALRM, SIG_DFL); | ||
739 | signal(SIGHUP, SIG_DFL); | ||
740 | signal(SIGTERM, SIG_DFL); | ||
741 | signal(SIGQUIT, SIG_DFL); | ||
742 | signal(SIGCHLD, SIG_DFL); | ||
743 | |||
744 | /* Set socket options for the connection. We want the socket to | ||
745 | close as fast as possible without waiting for anything. If the | ||
746 | connection is not a socket, these will do nothing. */ | ||
747 | /* setsockopt(sock_in, SOL_SOCKET, SO_REUSEADDR, (void *)&on, | ||
748 | sizeof(on)); */ | ||
749 | linger.l_onoff = 1; | ||
750 | linger.l_linger = 5; | ||
751 | setsockopt(sock_in, SOL_SOCKET, SO_LINGER, (void *) &linger, sizeof(linger)); | ||
752 | |||
753 | /* Register our connection. This turns encryption off because we | ||
754 | do not have a key. */ | ||
755 | packet_set_connection(sock_in, sock_out); | ||
756 | |||
757 | remote_port = get_remote_port(); | ||
758 | remote_ip = get_remote_ipaddr(); | ||
759 | |||
760 | /* Check whether logins are denied from this host. */ | ||
761 | #ifdef LIBWRAP | ||
762 | { | ||
763 | struct request_info req; | ||
713 | 764 | ||
714 | arc4random_stir(); | 765 | request_init(&req, RQ_DAEMON, av0, RQ_FILE, sock_in, NULL); |
766 | fromhost(&req); | ||
715 | 767 | ||
716 | /* Close the new socket (the child is now taking care of it). */ | 768 | if (!hosts_access(&req)) { |
717 | close(newsock); | 769 | close(sock_in); |
770 | close(sock_out); | ||
771 | refuse(&req); | ||
772 | } | ||
773 | verbose("Connection from %.500s port %d", eval_client(&req), remote_port); | ||
718 | } | 774 | } |
719 | } | ||
720 | |||
721 | /* This is the child processing a new connection. */ | ||
722 | |||
723 | /* Disable the key regeneration alarm. We will not regenerate the key | ||
724 | since we are no longer in a position to give it to anyone. We will | ||
725 | not restart on SIGHUP since it no longer makes sense. */ | ||
726 | alarm(0); | ||
727 | signal(SIGALRM, SIG_DFL); | ||
728 | signal(SIGHUP, SIG_DFL); | ||
729 | signal(SIGTERM, SIG_DFL); | ||
730 | signal(SIGQUIT, SIG_DFL); | ||
731 | signal(SIGCHLD, SIG_DFL); | ||
732 | |||
733 | /* Set socket options for the connection. We want the socket to close | ||
734 | as fast as possible without waiting for anything. If the connection | ||
735 | is not a socket, these will do nothing. */ | ||
736 | /* setsockopt(sock_in, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); */ | ||
737 | linger.l_onoff = 1; | ||
738 | linger.l_linger = 5; | ||
739 | setsockopt(sock_in, SOL_SOCKET, SO_LINGER, (void *)&linger, sizeof(linger)); | ||
740 | |||
741 | /* Register our connection. This turns encryption off because we do not | ||
742 | have a key. */ | ||
743 | packet_set_connection(sock_in, sock_out); | ||
744 | |||
745 | remote_port = get_remote_port(); | ||
746 | |||
747 | /* Check whether logins are denied from this host. */ | ||
748 | #ifdef LIBWRAP | ||
749 | { | ||
750 | struct request_info req; | ||
751 | |||
752 | request_init(&req, RQ_DAEMON, av0, RQ_FILE, sock_in, NULL); | ||
753 | fromhost(&req); | ||
754 | |||
755 | if (!hosts_access(&req)) { | ||
756 | close(sock_in); | ||
757 | close(sock_out); | ||
758 | refuse(&req); | ||
759 | } | ||
760 | log("Connection from %.500s port %d", eval_client(&req), remote_port); | ||
761 | } | ||
762 | #else | 775 | #else |
763 | /* Log the connection. */ | 776 | /* Log the connection. */ |
764 | log("Connection from %.100s port %d", get_remote_ipaddr(), remote_port); | 777 | verbose("Connection from %.500s port %d", remote_ip, remote_port); |
765 | #endif /* LIBWRAP */ | 778 | #endif /* LIBWRAP */ |
766 | 779 | ||
767 | /* We don\'t want to listen forever unless the other side successfully | 780 | /* We don\'t want to listen forever unless the other side |
768 | authenticates itself. So we set up an alarm which is cleared after | 781 | successfully authenticates itself. So we set up an alarm which |
769 | successful authentication. A limit of zero indicates no limit. | 782 | is cleared after successful authentication. A limit of zero |
770 | Note that we don\'t set the alarm in debugging mode; it is just annoying | 783 | indicates no limit. Note that we don\'t set the alarm in |
771 | to have the server exit just when you are about to discover the bug. */ | 784 | debugging mode; it is just annoying to have the server exit |
772 | signal(SIGALRM, grace_alarm_handler); | 785 | just when you are about to discover the bug. */ |
773 | if (!debug_flag) | 786 | signal(SIGALRM, grace_alarm_handler); |
774 | alarm(options.login_grace_time); | 787 | if (!debug_flag) |
775 | 788 | alarm(options.login_grace_time); | |
776 | /* Send our protocol version identification. */ | 789 | |
777 | snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", | 790 | if (client_version_string != NULL) { |
778 | PROTOCOL_MAJOR, PROTOCOL_MINOR, SSH_VERSION); | 791 | /* we are exec'ed by sshd2, so skip exchange of protocol version */ |
779 | if (write(sock_out, buf, strlen(buf)) != strlen(buf)) | 792 | strlcpy(buf, client_version_string, sizeof(buf)); |
780 | fatal("Could not write ident string."); | 793 | } else { |
781 | 794 | /* Send our protocol version identification. */ | |
782 | /* Read other side\'s version identification. */ | 795 | snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n", |
783 | for (i = 0; i < sizeof(buf) - 1; i++) | 796 | PROTOCOL_MAJOR, PROTOCOL_MINOR, SSH_VERSION); |
784 | { | 797 | if (write(sock_out, buf, strlen(buf)) != strlen(buf)) |
785 | if (read(sock_in, &buf[i], 1) != 1) | 798 | fatal("Could not write ident string to %s.", get_remote_ipaddr()); |
786 | fatal("Did not receive ident string."); | 799 | |
787 | if (buf[i] == '\r') | 800 | /* Read other side\'s version identification. */ |
788 | { | 801 | for (i = 0; i < sizeof(buf) - 1; i++) { |
789 | buf[i] = '\n'; | 802 | if (read(sock_in, &buf[i], 1) != 1) |
790 | buf[i + 1] = 0; | 803 | fatal("Did not receive ident string from %s.", get_remote_ipaddr()); |
791 | break; | 804 | if (buf[i] == '\r') { |
805 | buf[i] = '\n'; | ||
806 | buf[i + 1] = 0; | ||
807 | break; | ||
808 | } | ||
809 | if (buf[i] == '\n') { | ||
810 | /* buf[i] == '\n' */ | ||
811 | buf[i + 1] = 0; | ||
812 | break; | ||
813 | } | ||
814 | } | ||
815 | buf[sizeof(buf) - 1] = 0; | ||
792 | } | 816 | } |
793 | if (buf[i] == '\n') | 817 | |
794 | { | 818 | /* Check that the versions match. In future this might accept |
795 | /* buf[i] == '\n' */ | 819 | several versions and set appropriate flags to handle them. */ |
796 | buf[i + 1] = 0; | 820 | if (sscanf(buf, "SSH-%d.%d-%[^\n]\n", &remote_major, &remote_minor, |
797 | break; | 821 | remote_version) != 3) { |
822 | const char *s = "Protocol mismatch.\n"; | ||
823 | (void) write(sock_out, s, strlen(s)); | ||
824 | close(sock_in); | ||
825 | close(sock_out); | ||
826 | fatal("Bad protocol version identification '%.100s' from %s", | ||
827 | buf, get_remote_ipaddr()); | ||
828 | } | ||
829 | debug("Client protocol version %d.%d; client software version %.100s", | ||
830 | remote_major, remote_minor, remote_version); | ||
831 | if (remote_major != PROTOCOL_MAJOR) { | ||
832 | const char *s = "Protocol major versions differ.\n"; | ||
833 | (void) write(sock_out, s, strlen(s)); | ||
834 | close(sock_in); | ||
835 | close(sock_out); | ||
836 | fatal("Protocol major versions differ for %s: %d vs. %d", | ||
837 | get_remote_ipaddr(), | ||
838 | PROTOCOL_MAJOR, remote_major); | ||
798 | } | 839 | } |
799 | } | 840 | /* Check that the client has sufficiently high software version. */ |
800 | buf[sizeof(buf) - 1] = 0; | 841 | if (remote_major == 1 && remote_minor < 3) |
801 | 842 | packet_disconnect("Your ssh version is too old and is no longer supported. Please install a newer version."); | |
802 | /* Check that the versions match. In future this might accept several | 843 | |
803 | versions and set appropriate flags to handle them. */ | 844 | if (remote_major == 1 && remote_minor == 3) { |
804 | if (sscanf(buf, "SSH-%d.%d-%[^\n]\n", &remote_major, &remote_minor, | 845 | enable_compat13(); |
805 | remote_version) != 3) | 846 | if (strcmp(remote_version, "OpenSSH-1.1") != 0) { |
806 | { | 847 | debug("Agent forwarding disabled, remote version is not compatible."); |
807 | const char *s = "Protocol mismatch.\n"; | 848 | no_agent_forwarding_flag = 1; |
808 | (void) write(sock_out, s, strlen(s)); | 849 | } |
809 | close(sock_in); | 850 | } |
810 | close(sock_out); | 851 | /* Check that the connection comes from a privileged port. Rhosts- |
811 | fatal("Bad protocol version identification: %.100s", buf); | 852 | and Rhosts-RSA-Authentication only make sense from priviledged |
812 | } | 853 | programs. Of course, if the intruder has root access on his |
813 | debug("Client protocol version %d.%d; client software version %.100s", | 854 | local machine, he can connect from any port. So do not use |
814 | remote_major, remote_minor, remote_version); | 855 | these authentication methods from machines that you do not trust. */ |
815 | if (remote_major != PROTOCOL_MAJOR) | 856 | if (remote_port >= IPPORT_RESERVED || |
816 | { | 857 | remote_port < IPPORT_RESERVED / 2) { |
817 | const char *s = "Protocol major versions differ.\n"; | 858 | options.rhosts_authentication = 0; |
818 | (void) write(sock_out, s, strlen(s)); | 859 | options.rhosts_rsa_authentication = 0; |
819 | close(sock_in); | 860 | } |
820 | close(sock_out); | 861 | packet_set_nonblocking(); |
821 | fatal("Protocol major versions differ: %d vs. %d", | 862 | |
822 | PROTOCOL_MAJOR, remote_major); | 863 | /* Handle the connection. */ |
823 | } | 864 | do_connection(); |
824 | |||
825 | /* Check that the client has sufficiently high software version. */ | ||
826 | if (remote_major == 1 && remote_minor < 3) | ||
827 | packet_disconnect("Your ssh version is too old and is no longer supported. Please install a newer version."); | ||
828 | |||
829 | if (remote_major == 1 && remote_minor == 3) { | ||
830 | enable_compat13(); | ||
831 | if (strcmp(remote_version, "OpenSSH-1.1") != 0) { | ||
832 | debug("Agent forwarding disabled, remote version is not compatible."); | ||
833 | no_agent_forwarding_flag = 1; | ||
834 | } | ||
835 | } | ||
836 | |||
837 | /* Check that the connection comes from a privileged port. | ||
838 | Rhosts- and Rhosts-RSA-Authentication only make sense | ||
839 | from priviledged programs. | ||
840 | Of course, if the intruder has root access on his local machine, | ||
841 | he can connect from any port. So do not use these authentication | ||
842 | methods from machines that you do not trust. */ | ||
843 | if (remote_port >= IPPORT_RESERVED || | ||
844 | remote_port < IPPORT_RESERVED / 2) | ||
845 | { | ||
846 | options.rhosts_authentication = 0; | ||
847 | options.rhosts_rsa_authentication = 0; | ||
848 | } | ||
849 | |||
850 | packet_set_nonblocking(); | ||
851 | |||
852 | /* Handle the connection. */ | ||
853 | do_connection(); | ||
854 | 865 | ||
855 | #ifdef KRB4 | 866 | #ifdef KRB4 |
856 | /* Cleanup user's ticket cache file. */ | 867 | /* Cleanup user's ticket cache file. */ |
857 | if (options.kerberos_ticket_cleanup) | 868 | if (options.kerberos_ticket_cleanup) |
858 | (void) dest_tkt(); | 869 | (void) dest_tkt(); |
859 | #endif /* KRB4 */ | 870 | #endif /* KRB4 */ |
860 | 871 | ||
861 | /* Cleanup user's local Xauthority file. */ | 872 | /* Cleanup user's local Xauthority file. */ |
862 | if (xauthfile) unlink(xauthfile); | 873 | if (xauthfile) |
874 | unlink(xauthfile); | ||
863 | 875 | ||
864 | /* The connection has been terminated. */ | 876 | /* The connection has been terminated. */ |
865 | log("Closing connection to %.100s", inet_ntoa(sin.sin_addr)); | 877 | verbose("Closing connection to %.100s", remote_ip); |
866 | 878 | ||
867 | #ifdef HAVE_LIBPAM | 879 | #ifdef HAVE_LIBPAM |
868 | { | 880 | { |
869 | int retval; | 881 | int retval; |
870 | |||
871 | if (pamh != NULL) | ||
872 | { | ||
873 | debug("Closing PAM session."); | ||
874 | retval = pam_close_session((pam_handle_t *)pamh, 0); | ||
875 | |||
876 | debug("Terminating PAM library."); | ||
877 | if (pam_end((pam_handle_t *)pamh, retval) != PAM_SUCCESS) | ||
878 | log("Cannot release PAM authentication."); | ||
879 | |||
880 | fatal_remove_cleanup(&pam_cleanup_proc, NULL); | ||
881 | } | ||
882 | } | ||
883 | #endif /* HAVE_LIBPAM */ | ||
884 | 882 | ||
885 | packet_close(); | 883 | if (pamh != NULL) { |
884 | debug("Closing PAM session."); | ||
885 | retval = pam_close_session((pam_handle_t *)pamh, 0); | ||
886 | 886 | ||
887 | exit(0); | 887 | debug("Terminating PAM library."); |
888 | } | 888 | if (pam_end((pam_handle_t *)pamh, retval) != PAM_SUCCESS) |
889 | log("Cannot release PAM authentication."); | ||
890 | |||
891 | fatal_remove_cleanup(&pam_cleanup_proc, NULL); | ||
892 | } | ||
893 | } | ||
894 | #endif /* HAVE_LIBPAM */ | ||
889 | 895 | ||
890 | /* Process an incoming connection. Protocol version identifiers have already | 896 | packet_close(); |
891 | been exchanged. This sends server key and performs the key exchange. | 897 | exit(0); |
892 | Server and host keys will no longer be needed after this functions. */ | 898 | } |
893 | 899 | ||
900 | /* | ||
901 | * Process an incoming connection. Protocol version identifiers have already | ||
902 | * been exchanged. This sends server key and performs the key exchange. | ||
903 | * Server and host keys will no longer be needed after this functions. | ||
904 | */ | ||
894 | void | 905 | void |
895 | do_connection() | 906 | do_connection() |
896 | { | 907 | { |
897 | int i, len; | 908 | int i, len; |
898 | BIGNUM *session_key_int; | 909 | BIGNUM *session_key_int; |
899 | unsigned char session_key[SSH_SESSION_KEY_LENGTH]; | 910 | unsigned char session_key[SSH_SESSION_KEY_LENGTH]; |
900 | unsigned char check_bytes[8]; | 911 | unsigned char check_bytes[8]; |
901 | char *user; | 912 | char *user; |
902 | unsigned int cipher_type, auth_mask, protocol_flags; | 913 | unsigned int cipher_type, auth_mask, protocol_flags; |
903 | int plen, slen; | 914 | int plen, slen; |
904 | u_int32_t rand = 0; | 915 | u_int32_t rand = 0; |
905 | 916 | ||
906 | /* Generate check bytes that the client must send back in the user packet | 917 | /* Generate check bytes that the client must send back in the user |
907 | in order for it to be accepted; this is used to defy ip spoofing | 918 | packet in order for it to be accepted; this is used to defy ip |
908 | attacks. Note that this only works against somebody doing IP spoofing | 919 | spoofing attacks. Note that this only works against somebody |
909 | from a remote machine; any machine on the local network can still see | 920 | doing IP spoofing from a remote machine; any machine on the |
910 | outgoing packets and catch the random cookie. This only affects | 921 | local network can still see outgoing packets and catch the |
911 | rhosts authentication, and this is one of the reasons why it is | 922 | random cookie. This only affects rhosts authentication, and |
912 | inherently insecure. */ | 923 | this is one of the reasons why it is inherently insecure. */ |
913 | for (i = 0; i < 8; i++) { | 924 | for (i = 0; i < 8; i++) { |
914 | if (i % 4 == 0) | 925 | if (i % 4 == 0) |
915 | rand = arc4random(); | 926 | rand = arc4random(); |
916 | check_bytes[i] = rand & 0xff; | 927 | check_bytes[i] = rand & 0xff; |
917 | rand >>= 8; | 928 | rand >>= 8; |
918 | } | 929 | } |
919 | 930 | ||
920 | /* Send our public key. We include in the packet 64 bits of random | 931 | /* Send our public key. We include in the packet 64 bits of |
921 | data that must be matched in the reply in order to prevent IP spoofing. */ | 932 | random data that must be matched in the reply in order to |
922 | packet_start(SSH_SMSG_PUBLIC_KEY); | 933 | prevent IP spoofing. */ |
923 | for (i = 0; i < 8; i++) | 934 | packet_start(SSH_SMSG_PUBLIC_KEY); |
924 | packet_put_char(check_bytes[i]); | 935 | for (i = 0; i < 8; i++) |
925 | 936 | packet_put_char(check_bytes[i]); | |
926 | /* Store our public server RSA key. */ | 937 | |
927 | packet_put_int(BN_num_bits(public_key->n)); | 938 | /* Store our public server RSA key. */ |
928 | packet_put_bignum(public_key->e); | 939 | packet_put_int(BN_num_bits(public_key->n)); |
929 | packet_put_bignum(public_key->n); | 940 | packet_put_bignum(public_key->e); |
930 | 941 | packet_put_bignum(public_key->n); | |
931 | /* Store our public host RSA key. */ | 942 | |
932 | packet_put_int(BN_num_bits(sensitive_data.host_key->n)); | 943 | /* Store our public host RSA key. */ |
933 | packet_put_bignum(sensitive_data.host_key->e); | 944 | packet_put_int(BN_num_bits(sensitive_data.host_key->n)); |
934 | packet_put_bignum(sensitive_data.host_key->n); | 945 | packet_put_bignum(sensitive_data.host_key->e); |
935 | 946 | packet_put_bignum(sensitive_data.host_key->n); | |
936 | /* Put protocol flags. */ | 947 | |
937 | packet_put_int(SSH_PROTOFLAG_HOST_IN_FWD_OPEN); | 948 | /* Put protocol flags. */ |
938 | 949 | packet_put_int(SSH_PROTOFLAG_HOST_IN_FWD_OPEN); | |
939 | /* Declare which ciphers we support. */ | 950 | |
940 | packet_put_int(cipher_mask()); | 951 | /* Declare which ciphers we support. */ |
941 | 952 | packet_put_int(cipher_mask()); | |
942 | /* Declare supported authentication types. */ | 953 | |
943 | auth_mask = 0; | 954 | /* Declare supported authentication types. */ |
944 | if (options.rhosts_authentication) | 955 | auth_mask = 0; |
945 | auth_mask |= 1 << SSH_AUTH_RHOSTS; | 956 | if (options.rhosts_authentication) |
946 | if (options.rhosts_rsa_authentication) | 957 | auth_mask |= 1 << SSH_AUTH_RHOSTS; |
947 | auth_mask |= 1 << SSH_AUTH_RHOSTS_RSA; | 958 | if (options.rhosts_rsa_authentication) |
948 | if (options.rsa_authentication) | 959 | auth_mask |= 1 << SSH_AUTH_RHOSTS_RSA; |
949 | auth_mask |= 1 << SSH_AUTH_RSA; | 960 | if (options.rsa_authentication) |
961 | auth_mask |= 1 << SSH_AUTH_RSA; | ||
950 | #ifdef KRB4 | 962 | #ifdef KRB4 |
951 | if (options.kerberos_authentication) | 963 | if (options.kerberos_authentication) |
952 | auth_mask |= 1 << SSH_AUTH_KERBEROS; | 964 | auth_mask |= 1 << SSH_AUTH_KERBEROS; |
953 | #endif | 965 | #endif |
954 | #ifdef AFS | 966 | #ifdef AFS |
955 | if (options.kerberos_tgt_passing) | 967 | if (options.kerberos_tgt_passing) |
956 | auth_mask |= 1 << SSH_PASS_KERBEROS_TGT; | 968 | auth_mask |= 1 << SSH_PASS_KERBEROS_TGT; |
957 | if (options.afs_token_passing) | 969 | if (options.afs_token_passing) |
958 | auth_mask |= 1 << SSH_PASS_AFS_TOKEN; | 970 | auth_mask |= 1 << SSH_PASS_AFS_TOKEN; |
959 | #endif | 971 | #endif |
960 | if (options.password_authentication) | 972 | #ifdef SKEY |
961 | auth_mask |= 1 << SSH_AUTH_PASSWORD; | 973 | if (options.skey_authentication == 1) |
962 | packet_put_int(auth_mask); | 974 | auth_mask |= 1 << SSH_AUTH_TIS; |
963 | 975 | #endif | |
964 | /* Send the packet and wait for it to be sent. */ | 976 | if (options.password_authentication) |
965 | packet_send(); | 977 | auth_mask |= 1 << SSH_AUTH_PASSWORD; |
966 | packet_write_wait(); | 978 | packet_put_int(auth_mask); |
967 | 979 | ||
968 | debug("Sent %d bit public key and %d bit host key.", | 980 | /* Send the packet and wait for it to be sent. */ |
969 | BN_num_bits(public_key->n), BN_num_bits(sensitive_data.host_key->n)); | 981 | packet_send(); |
970 | 982 | packet_write_wait(); | |
971 | /* Read clients reply (cipher type and session key). */ | 983 | |
972 | packet_read_expect(&plen, SSH_CMSG_SESSION_KEY); | 984 | debug("Sent %d bit public key and %d bit host key.", |
973 | 985 | BN_num_bits(public_key->n), BN_num_bits(sensitive_data.host_key->n)); | |
974 | /* Get cipher type. */ | 986 | |
975 | cipher_type = packet_get_char(); | 987 | /* Read clients reply (cipher type and session key). */ |
976 | 988 | packet_read_expect(&plen, SSH_CMSG_SESSION_KEY); | |
977 | /* Get check bytes from the packet. These must match those we sent earlier | 989 | |
978 | with the public key packet. */ | 990 | /* Get cipher type. */ |
979 | for (i = 0; i < 8; i++) | 991 | cipher_type = packet_get_char(); |
980 | if (check_bytes[i] != packet_get_char()) | 992 | |
981 | packet_disconnect("IP Spoofing check bytes do not match."); | 993 | /* Get check bytes from the packet. These must match those we |
982 | 994 | sent earlier with the public key packet. */ | |
983 | debug("Encryption type: %.200s", cipher_name(cipher_type)); | 995 | for (i = 0; i < 8; i++) |
984 | 996 | if (check_bytes[i] != packet_get_char()) | |
985 | /* Get the encrypted integer. */ | 997 | packet_disconnect("IP Spoofing check bytes do not match."); |
986 | session_key_int = BN_new(); | 998 | |
987 | packet_get_bignum(session_key_int, &slen); | 999 | debug("Encryption type: %.200s", cipher_name(cipher_type)); |
988 | 1000 | ||
989 | /* Get protocol flags. */ | 1001 | /* Get the encrypted integer. */ |
990 | protocol_flags = packet_get_int(); | 1002 | session_key_int = BN_new(); |
991 | packet_set_protocol_flags(protocol_flags); | 1003 | packet_get_bignum(session_key_int, &slen); |
992 | 1004 | ||
993 | packet_integrity_check(plen, 1 + 8 + slen + 4, SSH_CMSG_SESSION_KEY); | 1005 | /* Get protocol flags. */ |
994 | 1006 | protocol_flags = packet_get_int(); | |
995 | /* Decrypt it using our private server key and private host key (key with | 1007 | packet_set_protocol_flags(protocol_flags); |
996 | larger modulus first). */ | 1008 | |
997 | if (BN_cmp(sensitive_data.private_key->n, sensitive_data.host_key->n) > 0) | 1009 | packet_integrity_check(plen, 1 + 8 + slen + 4, SSH_CMSG_SESSION_KEY); |
998 | { | 1010 | |
999 | /* Private key has bigger modulus. */ | 1011 | /* Decrypt it using our private server key and private host key |
1000 | if (BN_num_bits(sensitive_data.private_key->n) < | 1012 | (key with larger modulus first). */ |
1001 | BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED) { | 1013 | if (BN_cmp(sensitive_data.private_key->n, sensitive_data.host_key->n) > 0) { |
1002 | fatal("do_connection: private_key %d < host_key %d + SSH_KEY_BITS_RESERVED %d", | 1014 | /* Private key has bigger modulus. */ |
1003 | BN_num_bits(sensitive_data.private_key->n), | 1015 | if (BN_num_bits(sensitive_data.private_key->n) < |
1004 | BN_num_bits(sensitive_data.host_key->n), | 1016 | BN_num_bits(sensitive_data.host_key->n) + SSH_KEY_BITS_RESERVED) { |
1005 | SSH_KEY_BITS_RESERVED); | 1017 | fatal("do_connection: %s: private_key %d < host_key %d + SSH_KEY_BITS_RESERVED %d", |
1006 | } | 1018 | get_remote_ipaddr(), |
1007 | 1019 | BN_num_bits(sensitive_data.private_key->n), | |
1008 | rsa_private_decrypt(session_key_int, session_key_int, | 1020 | BN_num_bits(sensitive_data.host_key->n), |
1009 | sensitive_data.private_key); | 1021 | SSH_KEY_BITS_RESERVED); |
1010 | rsa_private_decrypt(session_key_int, session_key_int, | 1022 | } |
1011 | sensitive_data.host_key); | 1023 | rsa_private_decrypt(session_key_int, session_key_int, |
1012 | } | 1024 | sensitive_data.private_key); |
1013 | else | 1025 | rsa_private_decrypt(session_key_int, session_key_int, |
1014 | { | 1026 | sensitive_data.host_key); |
1015 | /* Host key has bigger modulus (or they are equal). */ | 1027 | } else { |
1016 | if (BN_num_bits(sensitive_data.host_key->n) < | 1028 | /* Host key has bigger modulus (or they are equal). */ |
1017 | BN_num_bits(sensitive_data.private_key->n) + SSH_KEY_BITS_RESERVED) { | 1029 | if (BN_num_bits(sensitive_data.host_key->n) < |
1018 | fatal("do_connection: host_key %d < private_key %d + SSH_KEY_BITS_RESERVED %d", | 1030 | BN_num_bits(sensitive_data.private_key->n) + SSH_KEY_BITS_RESERVED) { |
1019 | BN_num_bits(sensitive_data.host_key->n), | 1031 | fatal("do_connection: %s: host_key %d < private_key %d + SSH_KEY_BITS_RESERVED %d", |
1020 | BN_num_bits(sensitive_data.private_key->n), | 1032 | get_remote_ipaddr(), |
1021 | SSH_KEY_BITS_RESERVED); | 1033 | BN_num_bits(sensitive_data.host_key->n), |
1022 | } | 1034 | BN_num_bits(sensitive_data.private_key->n), |
1023 | rsa_private_decrypt(session_key_int, session_key_int, | 1035 | SSH_KEY_BITS_RESERVED); |
1024 | sensitive_data.host_key); | 1036 | } |
1025 | rsa_private_decrypt(session_key_int, session_key_int, | 1037 | rsa_private_decrypt(session_key_int, session_key_int, |
1026 | sensitive_data.private_key); | 1038 | sensitive_data.host_key); |
1027 | } | 1039 | rsa_private_decrypt(session_key_int, session_key_int, |
1028 | 1040 | sensitive_data.private_key); | |
1029 | /* Compute session id for this session. */ | 1041 | } |
1030 | compute_session_id(session_id, check_bytes, | 1042 | |
1031 | sensitive_data.host_key->n, | 1043 | /* Compute session id for this session. */ |
1032 | sensitive_data.private_key->n); | 1044 | compute_session_id(session_id, check_bytes, |
1033 | 1045 | sensitive_data.host_key->n, | |
1034 | /* Extract session key from the decrypted integer. The key is in the | 1046 | sensitive_data.private_key->n); |
1035 | least significant 256 bits of the integer; the first byte of the | 1047 | |
1036 | key is in the highest bits. */ | 1048 | /* Extract session key from the decrypted integer. The key is in |
1037 | BN_mask_bits(session_key_int, sizeof(session_key) * 8); | 1049 | the least significant 256 bits of the integer; the first byte |
1038 | len = BN_num_bytes(session_key_int); | 1050 | of the key is in the highest bits. */ |
1039 | if (len < 0 || len > sizeof(session_key)) | 1051 | BN_mask_bits(session_key_int, sizeof(session_key) * 8); |
1040 | fatal("do_connection: bad len: session_key_int %d > sizeof(session_key) %d", | 1052 | len = BN_num_bytes(session_key_int); |
1041 | len, sizeof(session_key)); | 1053 | if (len < 0 || len > sizeof(session_key)) |
1042 | memset(session_key, 0, sizeof(session_key)); | 1054 | fatal("do_connection: bad len from %s: session_key_int %d > sizeof(session_key) %d", |
1043 | BN_bn2bin(session_key_int, session_key + sizeof(session_key) - len); | 1055 | get_remote_ipaddr(), |
1044 | 1056 | len, sizeof(session_key)); | |
1045 | /* Xor the first 16 bytes of the session key with the session id. */ | 1057 | memset(session_key, 0, sizeof(session_key)); |
1046 | for (i = 0; i < 16; i++) | 1058 | BN_bn2bin(session_key_int, session_key + sizeof(session_key) - len); |
1047 | session_key[i] ^= session_id[i]; | 1059 | |
1048 | 1060 | /* Xor the first 16 bytes of the session key with the session id. */ | |
1049 | /* Destroy the decrypted integer. It is no longer needed. */ | 1061 | for (i = 0; i < 16; i++) |
1050 | BN_clear_free(session_key_int); | 1062 | session_key[i] ^= session_id[i]; |
1051 | 1063 | ||
1052 | /* Set the session key. From this on all communications will be | 1064 | /* Destroy the decrypted integer. It is no longer needed. */ |
1053 | encrypted. */ | 1065 | BN_clear_free(session_key_int); |
1054 | packet_set_encryption_key(session_key, SSH_SESSION_KEY_LENGTH, cipher_type); | 1066 | |
1055 | 1067 | /* Set the session key. From this on all communications will be encrypted. */ | |
1056 | /* Destroy our copy of the session key. It is no longer needed. */ | 1068 | packet_set_encryption_key(session_key, SSH_SESSION_KEY_LENGTH, cipher_type); |
1057 | memset(session_key, 0, sizeof(session_key)); | 1069 | |
1058 | 1070 | /* Destroy our copy of the session key. It is no longer needed. */ | |
1059 | debug("Received session key; encryption turned on."); | 1071 | memset(session_key, 0, sizeof(session_key)); |
1060 | 1072 | ||
1061 | /* Send an acknowledgement packet. Note that this packet is sent | 1073 | debug("Received session key; encryption turned on."); |
1062 | encrypted. */ | 1074 | |
1063 | packet_start(SSH_SMSG_SUCCESS); | 1075 | /* Send an acknowledgement packet. Note that this packet is sent encrypted. */ |
1064 | packet_send(); | 1076 | packet_start(SSH_SMSG_SUCCESS); |
1065 | packet_write_wait(); | 1077 | packet_send(); |
1066 | 1078 | packet_write_wait(); | |
1067 | /* Get the name of the user that we wish to log in as. */ | 1079 | |
1068 | packet_read_expect(&plen, SSH_CMSG_USER); | 1080 | /* Get the name of the user that we wish to log in as. */ |
1069 | 1081 | packet_read_expect(&plen, SSH_CMSG_USER); | |
1070 | /* Get the user name. */ | 1082 | |
1071 | { | 1083 | /* Get the user name. */ |
1072 | int ulen; | 1084 | { |
1073 | user = packet_get_string(&ulen); | 1085 | int ulen; |
1074 | packet_integrity_check(plen, (4 + ulen), SSH_CMSG_USER); | 1086 | user = packet_get_string(&ulen); |
1075 | } | 1087 | packet_integrity_check(plen, (4 + ulen), SSH_CMSG_USER); |
1076 | 1088 | } | |
1077 | /* Destroy the private and public keys. They will no longer be needed. */ | 1089 | |
1078 | RSA_free(public_key); | 1090 | /* Destroy the private and public keys. They will no longer be needed. */ |
1079 | RSA_free(sensitive_data.private_key); | 1091 | RSA_free(public_key); |
1080 | RSA_free(sensitive_data.host_key); | 1092 | RSA_free(sensitive_data.private_key); |
1081 | 1093 | RSA_free(sensitive_data.host_key); | |
1082 | setproctitle("%s", user); | ||
1083 | /* Do the authentication. */ | ||
1084 | do_authentication(user); | ||
1085 | } | ||
1086 | 1094 | ||
1087 | /* Check if the user is allowed to log in via ssh. If user is listed in | 1095 | setproctitle("%s", user); |
1088 | DenyUsers or user's primary group is listed in DenyGroups, false will | 1096 | /* Do the authentication. */ |
1089 | be returned. If AllowUsers isn't empty and user isn't listed there, or | 1097 | do_authentication(user); |
1090 | if AllowGroups isn't empty and user isn't listed there, false will be | 1098 | } |
1091 | returned. Otherwise true is returned. | ||
1092 | XXX This function should also check if user has a valid shell */ | ||
1093 | 1099 | ||
1100 | /* | ||
1101 | * Check if the user is allowed to log in via ssh. If user is listed in | ||
1102 | * DenyUsers or user's primary group is listed in DenyGroups, false will | ||
1103 | * be returned. If AllowUsers isn't empty and user isn't listed there, or | ||
1104 | * if AllowGroups isn't empty and user isn't listed there, false will be | ||
1105 | * returned. Otherwise true is returned. | ||
1106 | * XXX This function should also check if user has a valid shell | ||
1107 | */ | ||
1094 | static int | 1108 | static int |
1095 | allowed_user(struct passwd *pw) | 1109 | allowed_user(struct passwd * pw) |
1096 | { | 1110 | { |
1097 | struct group *grp; | 1111 | struct group *grp; |
1098 | int i; | 1112 | int i; |
1099 | 1113 | ||
1100 | /* Shouldn't be called if pw is NULL, but better safe than sorry... */ | 1114 | /* Shouldn't be called if pw is NULL, but better safe than sorry... */ |
1101 | if (!pw) | 1115 | if (!pw) |
1102 | return 0; | 1116 | return 0; |
1103 | 1117 | ||
1104 | /* XXX Should check for valid login shell */ | 1118 | /* XXX Should check for valid login shell */ |
1105 | 1119 | ||
1106 | /* Return false if user is listed in DenyUsers */ | 1120 | /* Return false if user is listed in DenyUsers */ |
1107 | if (options.num_deny_users > 0) | 1121 | if (options.num_deny_users > 0) { |
1108 | { | 1122 | if (!pw->pw_name) |
1109 | if (!pw->pw_name) | 1123 | return 0; |
1110 | return 0; | 1124 | for (i = 0; i < options.num_deny_users; i++) |
1111 | for (i = 0; i < options.num_deny_users; i++) | 1125 | if (match_pattern(pw->pw_name, options.deny_users[i])) |
1112 | if (match_pattern(pw->pw_name, options.deny_users[i])) | 1126 | return 0; |
1113 | return 0; | 1127 | } |
1114 | } | 1128 | /* Return false if AllowUsers isn't empty and user isn't listed |
1115 | 1129 | there */ | |
1116 | /* Return false if AllowUsers isn't empty and user isn't listed there */ | 1130 | if (options.num_allow_users > 0) { |
1117 | if (options.num_allow_users > 0) | 1131 | if (!pw->pw_name) |
1118 | { | 1132 | return 0; |
1119 | if (!pw->pw_name) | 1133 | for (i = 0; i < options.num_allow_users; i++) |
1120 | return 0; | 1134 | if (match_pattern(pw->pw_name, options.allow_users[i])) |
1121 | for (i = 0; i < options.num_allow_users; i++) | 1135 | break; |
1122 | if (match_pattern(pw->pw_name, options.allow_users[i])) | 1136 | /* i < options.num_allow_users iff we break for loop */ |
1123 | break; | 1137 | if (i >= options.num_allow_users) |
1124 | /* i < options.num_allow_users iff we break for loop */ | 1138 | return 0; |
1125 | if (i >= options.num_allow_users) | 1139 | } |
1126 | return 0; | 1140 | /* Get the primary group name if we need it. Return false if it fails */ |
1127 | } | 1141 | if (options.num_deny_groups > 0 || options.num_allow_groups > 0) { |
1128 | 1142 | grp = getgrgid(pw->pw_gid); | |
1129 | /* Get the primary group name if we need it. Return false if it fails */ | 1143 | if (!grp) |
1130 | if (options.num_deny_groups > 0 || options.num_allow_groups > 0 ) | 1144 | return 0; |
1131 | { | 1145 | |
1132 | grp = getgrgid(pw->pw_gid); | 1146 | /* Return false if user's group is listed in DenyGroups */ |
1133 | if (!grp) | 1147 | if (options.num_deny_groups > 0) { |
1134 | return 0; | 1148 | if (!grp->gr_name) |
1135 | 1149 | return 0; | |
1136 | /* Return false if user's group is listed in DenyGroups */ | 1150 | for (i = 0; i < options.num_deny_groups; i++) |
1137 | if (options.num_deny_groups > 0) | 1151 | if (match_pattern(grp->gr_name, options.deny_groups[i])) |
1138 | { | 1152 | return 0; |
1139 | if (!grp->gr_name) | 1153 | } |
1140 | return 0; | 1154 | /* Return false if AllowGroups isn't empty and user's |
1141 | for (i = 0; i < options.num_deny_groups; i++) | 1155 | group isn't listed there */ |
1142 | if (match_pattern(grp->gr_name, options.deny_groups[i])) | 1156 | if (options.num_allow_groups > 0) { |
1143 | return 0; | 1157 | if (!grp->gr_name) |
1144 | } | 1158 | return 0; |
1145 | 1159 | for (i = 0; i < options.num_allow_groups; i++) | |
1146 | /* Return false if AllowGroups isn't empty and user's group isn't | 1160 | if (match_pattern(grp->gr_name, options.allow_groups[i])) |
1147 | listed there */ | 1161 | break; |
1148 | if (options.num_allow_groups > 0) | 1162 | /* i < options.num_allow_groups iff we break for |
1149 | { | 1163 | loop */ |
1150 | if (!grp->gr_name) | 1164 | if (i >= options.num_allow_groups) |
1151 | return 0; | 1165 | return 0; |
1152 | for (i = 0; i < options.num_allow_groups; i++) | 1166 | } |
1153 | if (match_pattern(grp->gr_name, options.allow_groups[i])) | 1167 | } |
1154 | break; | 1168 | /* We found no reason not to let this user try to log on... */ |
1155 | /* i < options.num_allow_groups iff we break for loop */ | 1169 | return 1; |
1156 | if (i >= options.num_allow_groups) | ||
1157 | return 0; | ||
1158 | } | ||
1159 | } | ||
1160 | |||
1161 | /* We found no reason not to let this user try to log on... */ | ||
1162 | return 1; | ||
1163 | } | 1170 | } |
1164 | 1171 | ||
1165 | /* Performs authentication of an incoming connection. Session key has already | 1172 | /* |
1166 | been exchanged and encryption is enabled. User is the user name to log | 1173 | * Performs authentication of an incoming connection. Session key has already |
1167 | in as (received from the client). */ | 1174 | * been exchanged and encryption is enabled. User is the user name to log |
1168 | 1175 | * in as (received from the client). | |
1176 | */ | ||
1169 | void | 1177 | void |
1170 | do_authentication(char *user) | 1178 | do_authentication(char *user) |
1171 | { | 1179 | { |
1172 | struct passwd *pw, pwcopy; | 1180 | struct passwd *pw, pwcopy; |
1173 | 1181 | ||
1174 | #ifdef AFS | 1182 | #ifdef AFS |
1175 | /* If machine has AFS, set process authentication group. */ | 1183 | /* If machine has AFS, set process authentication group. */ |
1176 | if (k_hasafs()) { | 1184 | if (k_hasafs()) { |
1177 | k_setpag(); | 1185 | k_setpag(); |
1178 | k_unlog(); | 1186 | k_unlog(); |
1179 | } | 1187 | } |
1180 | #endif /* AFS */ | 1188 | #endif /* AFS */ |
1181 | 1189 | ||
1182 | /* Verify that the user is a valid user. */ | 1190 | /* Verify that the user is a valid user. */ |
1183 | pw = getpwnam(user); | 1191 | pw = getpwnam(user); |
1184 | if (!pw || !allowed_user(pw)) | 1192 | if (!pw || !allowed_user(pw)) |
1185 | do_fake_authloop(user); | 1193 | do_fake_authloop(user); |
1186 | 1194 | ||
1187 | /* Take a copy of the returned structure. */ | 1195 | /* Take a copy of the returned structure. */ |
1188 | memset(&pwcopy, 0, sizeof(pwcopy)); | 1196 | memset(&pwcopy, 0, sizeof(pwcopy)); |
1189 | pwcopy.pw_name = xstrdup(pw->pw_name); | 1197 | pwcopy.pw_name = xstrdup(pw->pw_name); |
1190 | pwcopy.pw_passwd = xstrdup(pw->pw_passwd); | 1198 | pwcopy.pw_passwd = xstrdup(pw->pw_passwd); |
1191 | pwcopy.pw_uid = pw->pw_uid; | 1199 | pwcopy.pw_uid = pw->pw_uid; |
1192 | pwcopy.pw_gid = pw->pw_gid; | 1200 | pwcopy.pw_gid = pw->pw_gid; |
1193 | pwcopy.pw_dir = xstrdup(pw->pw_dir); | 1201 | pwcopy.pw_dir = xstrdup(pw->pw_dir); |
1194 | pwcopy.pw_shell = xstrdup(pw->pw_shell); | 1202 | pwcopy.pw_shell = xstrdup(pw->pw_shell); |
1195 | pw = &pwcopy; | 1203 | pw = &pwcopy; |
1196 | 1204 | ||
1197 | #ifdef HAVE_LIBPAM | 1205 | #ifdef HAVE_LIBPAM |
1198 | { | 1206 | { |
1199 | int pam_retval; | 1207 | int pam_retval; |
1200 | 1208 | ||
1201 | debug("Starting up PAM with username \"%.200s\"", pw->pw_name); | 1209 | debug("Starting up PAM with username \"%.200s\"", pw->pw_name); |
1202 | 1210 | ||
1203 | pam_retval = pam_start("sshd", pw->pw_name, &conv, (pam_handle_t**)&pamh); | 1211 | pam_retval = pam_start("sshd", pw->pw_name, &conv, (pam_handle_t**)&pamh); |
1204 | if (pam_retval != PAM_SUCCESS) | 1212 | if (pam_retval != PAM_SUCCESS) |
1205 | fatal("PAM initialisation failed: %.200s", PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); | 1213 | fatal("PAM initialisation failed: %.200s", PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); |
1206 | 1214 | ||
1207 | fatal_add_cleanup(&pam_cleanup_proc, NULL); | 1215 | fatal_add_cleanup(&pam_cleanup_proc, NULL); |
1208 | } | 1216 | } |
1209 | #endif | 1217 | #endif |
1210 | 1218 | ||
1211 | /* If we are not running as root, the user must have the same uid as the | 1219 | /* If we are not running as root, the user must have the same uid |
1212 | server. */ | 1220 | as the server. */ |
1213 | if (getuid() != 0 && pw->pw_uid != getuid()) | 1221 | if (getuid() != 0 && pw->pw_uid != getuid()) |
1214 | packet_disconnect("Cannot change user when server not running as root."); | 1222 | packet_disconnect("Cannot change user when server not running as root."); |
1215 | 1223 | ||
1216 | debug("Attempting authentication for %.100s.", user); | 1224 | debug("Attempting authentication for %.100s.", user); |
1217 | 1225 | ||
1218 | /* If the user has no password, accept authentication immediately. */ | 1226 | /* If the user has no password, accept authentication immediately. */ |
1219 | if (options.password_authentication && | 1227 | if (options.password_authentication && |
1220 | #ifdef KRB4 | 1228 | #ifdef KRB4 |
1221 | (!options.kerberos_authentication || options.kerberos_or_local_passwd) && | 1229 | (!options.kerberos_authentication || options.kerberos_or_local_passwd) && |
1222 | #endif /* KRB4 */ | 1230 | #endif /* KRB4 */ |
1223 | auth_password(pw, "")) | 1231 | auth_password(pw, "")) { |
1224 | { | 1232 | /* Authentication with empty password succeeded. */ |
1225 | /* Authentication with empty password succeeded. */ | 1233 | log("Login for user %s from %.100s, accepted without authentication.", |
1226 | debug("Login for user %.100s accepted without authentication.", user); | 1234 | pw->pw_name, get_remote_ipaddr()); |
1227 | } else { | 1235 | } else { |
1228 | /* Loop until the user has been authenticated or the connection is closed, | 1236 | /* Loop until the user has been authenticated or the |
1229 | do_authloop() returns only if authentication is successfull */ | 1237 | connection is closed, do_authloop() returns only if |
1230 | do_authloop(pw); | 1238 | authentication is successfull */ |
1231 | } | 1239 | do_authloop(pw); |
1232 | 1240 | } | |
1233 | /* XXX log unified auth message */ | 1241 | |
1234 | 1242 | /* Check if the user is logging in as root and root logins are disallowed. */ | |
1235 | /* Check if the user is logging in as root and root logins are disallowed. */ | 1243 | if (pw->pw_uid == 0 && !options.permit_root_login) { |
1236 | if (pw->pw_uid == 0 && !options.permit_root_login) | 1244 | if (forced_command) |
1237 | { | 1245 | log("Root login accepted for forced command."); |
1238 | if (forced_command) | 1246 | else |
1239 | log("Root login accepted for forced command."); | 1247 | packet_disconnect("ROOT LOGIN REFUSED FROM %.200s", |
1240 | else | 1248 | get_canonical_hostname()); |
1241 | packet_disconnect("ROOT LOGIN REFUSED FROM %.200s", | 1249 | } |
1242 | get_canonical_hostname()); | 1250 | /* The user has been authenticated and accepted. */ |
1243 | } | 1251 | packet_start(SSH_SMSG_SUCCESS); |
1244 | 1252 | packet_send(); | |
1245 | /* The user has been authenticated and accepted. */ | 1253 | packet_write_wait(); |
1246 | packet_start(SSH_SMSG_SUCCESS); | 1254 | |
1247 | packet_send(); | 1255 | /* Perform session preparation. */ |
1248 | packet_write_wait(); | 1256 | do_authenticated(pw); |
1249 | |||
1250 | /* Perform session preparation. */ | ||
1251 | do_authenticated(pw); | ||
1252 | } | 1257 | } |
1253 | 1258 | ||
1254 | #define MAX_AUTH_FAILURES 5 | 1259 | #define AUTH_FAIL_MAX 6 |
1260 | #define AUTH_FAIL_LOG (AUTH_FAIL_MAX/2) | ||
1261 | #define AUTH_FAIL_MSG "Too many authentication failures for %.100s" | ||
1255 | 1262 | ||
1256 | /* read packets and try to authenticate local user *pw. | 1263 | /* |
1257 | return if authentication is successfull */ | 1264 | * read packets and try to authenticate local user *pw. |
1265 | * return if authentication is successfull | ||
1266 | */ | ||
1258 | void | 1267 | void |
1259 | do_authloop(struct passwd *pw) | 1268 | do_authloop(struct passwd * pw) |
1260 | { | 1269 | { |
1261 | int authentication_failures = 0; | 1270 | int attempt = 0; |
1262 | unsigned int bits; | 1271 | unsigned int bits; |
1263 | BIGNUM *client_host_key_e, *client_host_key_n; | 1272 | BIGNUM *client_host_key_e, *client_host_key_n; |
1264 | BIGNUM *n; | 1273 | BIGNUM *n; |
1265 | char *client_user = NULL, *password = NULL; | 1274 | char *client_user = NULL, *password = NULL; |
1266 | int plen, dlen, nlen, ulen, elen; | 1275 | char user[1024]; |
1276 | int plen, dlen, nlen, ulen, elen; | ||
1277 | int type = 0; | ||
1278 | void (*authlog) (const char *fmt,...) = verbose; | ||
1267 | #ifdef HAVE_LIBPAM | 1279 | #ifdef HAVE_LIBPAM |
1268 | int pam_retval; | 1280 | int pam_retval; |
1269 | #endif /* HAVE_LIBPAM */ | 1281 | #endif /* HAVE_LIBPAM */ |
1270 | 1282 | ||
1271 | /* Indicate that authentication is needed. */ | 1283 | /* Indicate that authentication is needed. */ |
1272 | packet_start(SSH_SMSG_FAILURE); | 1284 | packet_start(SSH_SMSG_FAILURE); |
1273 | packet_send(); | 1285 | packet_send(); |
1274 | packet_write_wait(); | 1286 | packet_write_wait(); |
1275 | 1287 | ||
1276 | for (;;) { | 1288 | for (attempt = 1;; attempt++) { |
1277 | int authenticated = 0; | 1289 | int authenticated = 0; |
1290 | strlcpy(user, "", sizeof user); | ||
1278 | 1291 | ||
1279 | /* Get a packet from the client. */ | 1292 | /* Get a packet from the client. */ |
1280 | int type = packet_read(&plen); | 1293 | type = packet_read(&plen); |
1281 | 1294 | ||
1282 | /* Process the packet. */ | 1295 | /* Process the packet. */ |
1283 | switch (type) | 1296 | switch (type) { |
1284 | { | ||
1285 | #ifdef AFS | 1297 | #ifdef AFS |
1286 | case SSH_CMSG_HAVE_KERBEROS_TGT: | 1298 | case SSH_CMSG_HAVE_KERBEROS_TGT: |
1287 | if (!options.kerberos_tgt_passing) | 1299 | if (!options.kerberos_tgt_passing) { |
1288 | { | 1300 | /* packet_get_all(); */ |
1289 | /* packet_get_all(); */ | 1301 | verbose("Kerberos tgt passing disabled."); |
1290 | log("Kerberos tgt passing disabled."); | 1302 | break; |
1291 | break; | 1303 | } else { |
1292 | } | 1304 | /* Accept Kerberos tgt. */ |
1293 | else { | 1305 | char *tgt = packet_get_string(&dlen); |
1294 | /* Accept Kerberos tgt. */ | 1306 | packet_integrity_check(plen, 4 + dlen, type); |
1295 | char *tgt = packet_get_string(&dlen); | 1307 | if (!auth_kerberos_tgt(pw, tgt)) |
1296 | packet_integrity_check(plen, 4 + dlen, type); | 1308 | verbose("Kerberos tgt REFUSED for %s", pw->pw_name); |
1297 | if (!auth_kerberos_tgt(pw, tgt)) | 1309 | xfree(tgt); |
1298 | debug("Kerberos tgt REFUSED for %s", pw->pw_name); | 1310 | } |
1299 | xfree(tgt); | 1311 | continue; |
1300 | } | 1312 | |
1301 | continue; | 1313 | case SSH_CMSG_HAVE_AFS_TOKEN: |
1302 | 1314 | if (!options.afs_token_passing || !k_hasafs()) { | |
1303 | case SSH_CMSG_HAVE_AFS_TOKEN: | 1315 | /* packet_get_all(); */ |
1304 | if (!options.afs_token_passing || !k_hasafs()) { | 1316 | verbose("AFS token passing disabled."); |
1305 | /* packet_get_all(); */ | 1317 | break; |
1306 | log("AFS token passing disabled."); | 1318 | } else { |
1307 | break; | 1319 | /* Accept AFS token. */ |
1308 | } | 1320 | char *token_string = packet_get_string(&dlen); |
1309 | else { | 1321 | packet_integrity_check(plen, 4 + dlen, type); |
1310 | /* Accept AFS token. */ | 1322 | if (!auth_afs_token(pw, token_string)) |
1311 | char *token_string = packet_get_string(&dlen); | 1323 | verbose("AFS token REFUSED for %s", pw->pw_name); |
1312 | packet_integrity_check(plen, 4 + dlen, type); | 1324 | xfree(token_string); |
1313 | if (!auth_afs_token(pw, token_string)) | 1325 | } |
1314 | debug("AFS token REFUSED for %s", pw->pw_name); | 1326 | continue; |
1315 | xfree(token_string); | ||
1316 | } | ||
1317 | continue; | ||
1318 | #endif /* AFS */ | 1327 | #endif /* AFS */ |
1319 | |||
1320 | #ifdef KRB4 | 1328 | #ifdef KRB4 |
1321 | case SSH_CMSG_AUTH_KERBEROS: | 1329 | case SSH_CMSG_AUTH_KERBEROS: |
1322 | if (!options.kerberos_authentication) | 1330 | if (!options.kerberos_authentication) { |
1323 | { | 1331 | /* packet_get_all(); */ |
1324 | /* packet_get_all(); */ | 1332 | verbose("Kerberos authentication disabled."); |
1325 | log("Kerberos authentication disabled."); | 1333 | break; |
1326 | break; | 1334 | } else { |
1327 | } | 1335 | /* Try Kerberos v4 authentication. */ |
1328 | else { | 1336 | KTEXT_ST auth; |
1329 | /* Try Kerberos v4 authentication. */ | 1337 | char *tkt_user = NULL; |
1330 | KTEXT_ST auth; | 1338 | char *kdata = packet_get_string((unsigned int *) &auth.length); |
1331 | char *tkt_user = NULL; | 1339 | packet_integrity_check(plen, 4 + auth.length, type); |
1332 | char *kdata = packet_get_string((unsigned int *)&auth.length); | 1340 | |
1333 | packet_integrity_check(plen, 4 + auth.length, type); | 1341 | if (auth.length < MAX_KTXT_LEN) |
1334 | 1342 | memcpy(auth.dat, kdata, auth.length); | |
1335 | if (auth.length < MAX_KTXT_LEN) | 1343 | xfree(kdata); |
1336 | memcpy(auth.dat, kdata, auth.length); | 1344 | |
1337 | xfree(kdata); | 1345 | authenticated = auth_krb4(pw->pw_name, &auth, &tkt_user); |
1338 | 1346 | ||
1339 | authenticated = auth_krb4(pw->pw_name, &auth, &tkt_user); | 1347 | if (authenticated) { |
1340 | 1348 | snprintf(user, sizeof user, " tktuser %s", tkt_user); | |
1341 | log("Kerberos authentication %s%s for account %s from %s", | 1349 | xfree(tkt_user); |
1342 | authenticated ? "accepted " : "failed", | 1350 | } |
1343 | tkt_user != NULL ? tkt_user : "", | 1351 | } |
1344 | pw->pw_name, get_canonical_hostname()); | 1352 | break; |
1345 | if (authenticated) | ||
1346 | xfree(tkt_user); | ||
1347 | } | ||
1348 | break; | ||
1349 | #endif /* KRB4 */ | 1353 | #endif /* KRB4 */ |
1350 | 1354 | ||
1351 | case SSH_CMSG_AUTH_RHOSTS: | 1355 | case SSH_CMSG_AUTH_RHOSTS: |
1352 | if (!options.rhosts_authentication) | 1356 | if (!options.rhosts_authentication) { |
1353 | { | 1357 | verbose("Rhosts authentication disabled."); |
1354 | log("Rhosts authentication disabled."); | 1358 | break; |
1355 | break; | 1359 | } |
1356 | } | 1360 | /* Get client user name. Note that we just have |
1357 | 1361 | to trust the client; this is one reason why | |
1358 | /* Get client user name. Note that we just have to trust the client; | 1362 | rhosts authentication is insecure. (Another is |
1359 | this is one reason why rhosts authentication is insecure. | 1363 | IP-spoofing on a local network.) */ |
1360 | (Another is IP-spoofing on a local network.) */ | 1364 | client_user = packet_get_string(&ulen); |
1361 | client_user = packet_get_string(&dlen); | 1365 | packet_integrity_check(plen, 4 + ulen, type); |
1362 | packet_integrity_check(plen, 4 + dlen, type); | 1366 | |
1363 | 1367 | /* Try to authenticate using /etc/hosts.equiv and | |
1364 | /* Try to authenticate using /etc/hosts.equiv and .rhosts. */ | 1368 | .rhosts. */ |
1365 | authenticated = auth_rhosts(pw, client_user); | 1369 | authenticated = auth_rhosts(pw, client_user); |
1366 | 1370 | ||
1367 | log("Rhosts authentication %s for %.100s, remote %.100s on %.700s.", | 1371 | snprintf(user, sizeof user, " ruser %s", client_user); |
1368 | authenticated ? "accepted" : "failed", | ||
1369 | pw->pw_name, client_user, get_canonical_hostname()); | ||
1370 | #ifndef HAVE_LIBPAM | 1372 | #ifndef HAVE_LIBPAM |
1371 | xfree(client_user); | 1373 | xfree(client_user); |
1372 | #endif /* HAVE_LIBPAM */ | 1374 | #endif /* HAVE_LIBPAM */ |
1373 | break; | 1375 | break; |
1374 | 1376 | ||
1375 | case SSH_CMSG_AUTH_RHOSTS_RSA: | 1377 | case SSH_CMSG_AUTH_RHOSTS_RSA: |
1376 | if (!options.rhosts_rsa_authentication) | 1378 | if (!options.rhosts_rsa_authentication) { |
1377 | { | 1379 | verbose("Rhosts with RSA authentication disabled."); |
1378 | log("Rhosts with RSA authentication disabled."); | 1380 | break; |
1379 | break; | 1381 | } |
1380 | } | 1382 | /* Get client user name. Note that we just have |
1381 | 1383 | to trust the client; root on the client machine | |
1382 | /* Get client user name. Note that we just have to trust | 1384 | can claim to be any user. */ |
1383 | the client; root on the client machine can claim to be | 1385 | client_user = packet_get_string(&ulen); |
1384 | any user. */ | 1386 | |
1385 | client_user = packet_get_string(&ulen); | 1387 | /* Get the client host key. */ |
1386 | 1388 | client_host_key_e = BN_new(); | |
1387 | /* Get the client host key. */ | 1389 | client_host_key_n = BN_new(); |
1388 | client_host_key_e = BN_new(); | 1390 | bits = packet_get_int(); |
1389 | client_host_key_n = BN_new(); | 1391 | packet_get_bignum(client_host_key_e, &elen); |
1390 | bits = packet_get_int(); | 1392 | packet_get_bignum(client_host_key_n, &nlen); |
1391 | packet_get_bignum(client_host_key_e, &elen); | 1393 | |
1392 | packet_get_bignum(client_host_key_n, &nlen); | 1394 | if (bits != BN_num_bits(client_host_key_n)) |
1393 | 1395 | error("Warning: keysize mismatch for client_host_key: " | |
1394 | if (bits != BN_num_bits(client_host_key_n)) | 1396 | "actual %d, announced %d", BN_num_bits(client_host_key_n), bits); |
1395 | error("Warning: keysize mismatch for client_host_key: " | 1397 | packet_integrity_check(plen, (4 + ulen) + 4 + elen + nlen, type); |
1396 | "actual %d, announced %d", BN_num_bits(client_host_key_n), bits); | 1398 | |
1397 | packet_integrity_check(plen, (4 + ulen) + 4 + elen + nlen, type); | 1399 | authenticated = auth_rhosts_rsa(pw, client_user, |
1398 | 1400 | client_host_key_e, client_host_key_n); | |
1399 | authenticated = auth_rhosts_rsa(pw, client_user, | 1401 | BN_clear_free(client_host_key_e); |
1400 | client_host_key_e, client_host_key_n); | 1402 | BN_clear_free(client_host_key_n); |
1401 | log("Rhosts authentication %s for %.100s, remote %.100s.", | 1403 | |
1402 | authenticated ? "accepted" : "failed", | 1404 | snprintf(user, sizeof user, " ruser %s", client_user); |
1403 | pw->pw_name, client_user); | ||
1404 | #ifndef HAVE_LIBPAM | 1405 | #ifndef HAVE_LIBPAM |
1405 | xfree(client_user); | 1406 | xfree(client_user); |
1406 | #endif /* HAVE_LIBPAM */ | 1407 | #endif /* HAVE_LIBPAM */ |
1407 | BN_clear_free(client_host_key_e); | 1408 | break; |
1408 | BN_clear_free(client_host_key_n); | 1409 | |
1409 | break; | 1410 | case SSH_CMSG_AUTH_RSA: |
1410 | 1411 | if (!options.rsa_authentication) { | |
1411 | case SSH_CMSG_AUTH_RSA: | 1412 | verbose("RSA authentication disabled."); |
1412 | if (!options.rsa_authentication) | 1413 | break; |
1413 | { | 1414 | } |
1414 | log("RSA authentication disabled."); | 1415 | /* RSA authentication requested. */ |
1415 | break; | 1416 | n = BN_new(); |
1416 | } | 1417 | packet_get_bignum(n, &nlen); |
1417 | 1418 | packet_integrity_check(plen, nlen, type); | |
1418 | /* RSA authentication requested. */ | 1419 | authenticated = auth_rsa(pw, n); |
1419 | n = BN_new(); | 1420 | BN_clear_free(n); |
1420 | packet_get_bignum(n, &nlen); | 1421 | break; |
1421 | packet_integrity_check(plen, nlen, type); | 1422 | |
1422 | 1423 | case SSH_CMSG_AUTH_PASSWORD: | |
1423 | authenticated = auth_rsa(pw, n); | 1424 | if (!options.password_authentication) { |
1424 | log("RSA authentication %s for %.100s.", | 1425 | verbose("Password authentication disabled."); |
1425 | authenticated ? "accepted" : "failed", | 1426 | break; |
1426 | pw->pw_name); | 1427 | } |
1427 | BN_clear_free(n); | 1428 | /* Read user password. It is in plain text, but |
1428 | break; | 1429 | was transmitted over the encrypted channel so |
1429 | 1430 | it is not visible to an outside observer. */ | |
1430 | case SSH_CMSG_AUTH_PASSWORD: | 1431 | password = packet_get_string(&dlen); |
1431 | if (!options.password_authentication) | 1432 | packet_integrity_check(plen, 4 + dlen, type); |
1432 | { | 1433 | |
1433 | log("Password authentication disabled."); | ||
1434 | break; | ||
1435 | } | ||
1436 | |||
1437 | /* Read user password. It is in plain text, but was transmitted | ||
1438 | over the encrypted channel so it is not visible to an outside | ||
1439 | observer. */ | ||
1440 | password = packet_get_string(&dlen); | ||
1441 | packet_integrity_check(plen, 4 + dlen, type); | ||
1442 | |||
1443 | #ifdef HAVE_LIBPAM | 1434 | #ifdef HAVE_LIBPAM |
1444 | /* Do PAM auth with password */ | 1435 | /* Do PAM auth with password */ |
1445 | pampasswd = password; | 1436 | pampasswd = password; |
1446 | pam_retval = pam_authenticate((pam_handle_t *)pamh, 0); | 1437 | pam_retval = pam_authenticate((pam_handle_t *)pamh, 0); |
1447 | if (pam_retval == PAM_SUCCESS) | 1438 | if (pam_retval == PAM_SUCCESS) { |
1448 | { | 1439 | log("PAM Password authentication accepted for user \"%.100s\"", pw->pw_name); |
1449 | log("PAM Password authentication accepted for user \"%.100s\"", pw->pw_name); | 1440 | authenticated = 1; |
1450 | authenticated = 1; | 1441 | break; |
1451 | break; | 1442 | } |
1452 | } | 1443 | |
1453 | 1444 | log("PAM Password authentication for \"%.100s\" failed: %s", | |
1454 | log("PAM Password authentication for \"%.100s\" failed: %s", | 1445 | pw->pw_name, PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); |
1455 | pw->pw_name, PAM_STRERROR((pam_handle_t *)pamh, pam_retval)); | 1446 | break; |
1456 | break; | ||
1457 | #else /* HAVE_LIBPAM */ | 1447 | #else /* HAVE_LIBPAM */ |
1458 | /* Try authentication with the password. */ | 1448 | /* Try authentication with the password. */ |
1459 | authenticated = auth_password(pw, password); | 1449 | authenticated = auth_password(pw, password); |
1460 | log("Password authentication %s for %.100s.", | 1450 | |
1461 | authenticated ? "accepted" : "failed", | 1451 | memset(password, 0, strlen(password)); |
1462 | pw->pw_name); | 1452 | xfree(password); |
1463 | 1453 | break; | |
1464 | memset(password, 0, strlen(password)); | ||
1465 | xfree(password); | ||
1466 | break; | ||
1467 | #endif /* HAVE_LIBPAM */ | 1454 | #endif /* HAVE_LIBPAM */ |
1468 | 1455 | ||
1469 | case SSH_CMSG_AUTH_TIS: | 1456 | #ifdef SKEY |
1470 | /* TIS Authentication is unsupported */ | 1457 | case SSH_CMSG_AUTH_TIS: |
1471 | log("TIS authentication disabled."); | 1458 | debug("rcvd SSH_CMSG_AUTH_TIS"); |
1472 | break; | 1459 | if (options.skey_authentication == 1) { |
1473 | 1460 | char *skeyinfo = skey_keyinfo(pw->pw_name); | |
1474 | default: | 1461 | if (skeyinfo == NULL) { |
1475 | /* Any unknown messages will be ignored (and failure returned) | 1462 | debug("generating fake skeyinfo for %.100s.", pw->pw_name); |
1476 | during authentication. */ | 1463 | skeyinfo = skey_fake_keyinfo(pw->pw_name); |
1477 | log("Unknown message during authentication: type %d", type); | 1464 | } |
1478 | break; /* Respond with a failure message. */ | 1465 | if (skeyinfo != NULL) { |
1479 | } | 1466 | /* we send our s/key- in |
1480 | 1467 | tis-challenge messages */ | |
1481 | if (authenticated) | 1468 | debug("sending challenge '%s'", skeyinfo); |
1482 | break; | 1469 | packet_start(SSH_SMSG_AUTH_TIS_CHALLENGE); |
1483 | if (++authentication_failures >= MAX_AUTH_FAILURES) | 1470 | packet_put_string(skeyinfo, strlen(skeyinfo)); |
1484 | packet_disconnect("Too many authentication failures for %.100s from %.200s", | 1471 | packet_send(); |
1485 | pw->pw_name, get_canonical_hostname()); | 1472 | packet_write_wait(); |
1486 | /* Send a message indicating that the authentication attempt failed. */ | 1473 | continue; |
1487 | packet_start(SSH_SMSG_FAILURE); | 1474 | } |
1488 | packet_send(); | 1475 | } |
1489 | packet_write_wait(); | 1476 | break; |
1490 | } | 1477 | case SSH_CMSG_AUTH_TIS_RESPONSE: |
1478 | debug("rcvd SSH_CMSG_AUTH_TIS_RESPONSE"); | ||
1479 | if (options.skey_authentication == 1) { | ||
1480 | char *response = packet_get_string(&dlen); | ||
1481 | debug("skey response == '%s'", response); | ||
1482 | packet_integrity_check(plen, 4 + dlen, type); | ||
1483 | authenticated = (skey_haskey(pw->pw_name) == 0 && | ||
1484 | skey_passcheck(pw->pw_name, response) != -1); | ||
1485 | xfree(response); | ||
1486 | } | ||
1487 | break; | ||
1488 | #else | ||
1489 | case SSH_CMSG_AUTH_TIS: | ||
1490 | /* TIS Authentication is unsupported */ | ||
1491 | log("TIS authentication unsupported."); | ||
1492 | break; | ||
1493 | #endif | ||
1494 | |||
1495 | default: | ||
1496 | /* Any unknown messages will be ignored (and | ||
1497 | failure returned) during authentication. */ | ||
1498 | log("Unknown message during authentication: type %d", type); | ||
1499 | break; | ||
1500 | } | ||
1501 | |||
1502 | /* Raise logging level */ | ||
1503 | if (authenticated || | ||
1504 | attempt == AUTH_FAIL_LOG || | ||
1505 | type == SSH_CMSG_AUTH_PASSWORD) | ||
1506 | authlog = log; | ||
1507 | |||
1508 | authlog("%s %s for %.200s from %.200s port %d%s", | ||
1509 | authenticated ? "Accepted" : "Failed", | ||
1510 | get_authname(type), | ||
1511 | pw->pw_uid == 0 ? "ROOT" : pw->pw_name, | ||
1512 | get_remote_ipaddr(), | ||
1513 | get_remote_port(), | ||
1514 | user); | ||
1491 | 1515 | ||
1492 | #ifdef HAVE_LIBPAM | 1516 | #ifdef HAVE_LIBPAM |
1493 | do_pam_account_and_session(pw->pw_name, client_user, get_canonical_hostname()); | 1517 | do_pam_account_and_session(pw->pw_name, client_user, |
1518 | get_canonical_hostname()); | ||
1494 | 1519 | ||
1495 | /* Clean up */ | 1520 | /* Clean up */ |
1496 | if (client_user != NULL) | 1521 | if (client_user != NULL) |
1497 | xfree(client_user); | 1522 | xfree(client_user); |
1498 | 1523 | ||
1499 | if (password != NULL) | 1524 | if (password != NULL) { |
1500 | { | 1525 | memset(password, 0, strlen(password)); |
1501 | memset(password, 0, strlen(password)); | 1526 | xfree(password); |
1502 | xfree(password); | 1527 | } |
1503 | } | ||
1504 | #endif /* HAVE_LIBPAM */ | 1528 | #endif /* HAVE_LIBPAM */ |
1529 | |||
1530 | if (authenticated) | ||
1531 | return; | ||
1532 | |||
1533 | if (attempt > AUTH_FAIL_MAX) | ||
1534 | packet_disconnect(AUTH_FAIL_MSG, pw->pw_name); | ||
1535 | |||
1536 | /* Send a message indicating that the authentication attempt failed. */ | ||
1537 | packet_start(SSH_SMSG_FAILURE); | ||
1538 | packet_send(); | ||
1539 | packet_write_wait(); | ||
1540 | } | ||
1505 | } | 1541 | } |
1506 | 1542 | ||
1507 | /* The user does not exist or access is denied, | 1543 | /* |
1508 | but fake indication that authentication is needed. */ | 1544 | * The user does not exist or access is denied, |
1545 | * but fake indication that authentication is needed. | ||
1546 | */ | ||
1509 | void | 1547 | void |
1510 | do_fake_authloop(char *user) | 1548 | do_fake_authloop(char *user) |
1511 | { | 1549 | { |
1512 | int authentication_failures = 0; | 1550 | int attempt = 0; |
1513 | 1551 | ||
1514 | /* Indicate that authentication is needed. */ | 1552 | log("Faking authloop for illegal user %.200s from %.200s port %d", |
1515 | packet_start(SSH_SMSG_FAILURE); | 1553 | user, |
1516 | packet_send(); | 1554 | get_remote_ipaddr(), |
1517 | packet_write_wait(); | 1555 | get_remote_port()); |
1518 | 1556 | ||
1519 | /* Keep reading packets, and always respond with a failure. This is to | 1557 | /* Indicate that authentication is needed. */ |
1520 | avoid disclosing whether such a user really exists. */ | 1558 | packet_start(SSH_SMSG_FAILURE); |
1521 | for (;;) | 1559 | packet_send(); |
1522 | { | 1560 | packet_write_wait(); |
1523 | /* Read a packet. This will not return if the client disconnects. */ | 1561 | |
1524 | int plen; | 1562 | /* Keep reading packets, and always respond with a failure. This |
1525 | int type = packet_read(&plen); | 1563 | is to avoid disclosing whether such a user really exists. */ |
1564 | for (attempt = 1;; attempt++) { | ||
1565 | /* Read a packet. This will not return if the client | ||
1566 | disconnects. */ | ||
1567 | int plen; | ||
1568 | int type = packet_read(&plen); | ||
1526 | #ifdef SKEY | 1569 | #ifdef SKEY |
1527 | int passw_len; | 1570 | int dlen; |
1528 | char *password, *skeyinfo; | 1571 | char *password, *skeyinfo; |
1529 | if (options.password_authentication && | 1572 | if (options.password_authentication && |
1530 | options.skey_authentication == 1 && | 1573 | options.skey_authentication == 1 && |
1531 | type == SSH_CMSG_AUTH_PASSWORD && | 1574 | type == SSH_CMSG_AUTH_PASSWORD && |
1532 | (password = packet_get_string(&passw_len)) != NULL && | 1575 | (password = packet_get_string(&dlen)) != NULL && |
1533 | passw_len == 5 && | 1576 | dlen == 5 && |
1534 | strncasecmp(password, "s/key", 5) == 0 && | 1577 | strncasecmp(password, "s/key", 5) == 0 && |
1535 | (skeyinfo = skey_fake_keyinfo(user)) != NULL ){ | 1578 | (skeyinfo = skey_fake_keyinfo(user)) != NULL) { |
1536 | /* Send a fake s/key challenge. */ | 1579 | /* Send a fake s/key challenge. */ |
1537 | packet_send_debug(skeyinfo); | 1580 | packet_send_debug(skeyinfo); |
1538 | } | 1581 | } |
1539 | #endif | 1582 | #endif |
1540 | if (++authentication_failures >= MAX_AUTH_FAILURES) | 1583 | if (attempt > AUTH_FAIL_MAX) |
1541 | packet_disconnect("Too many authentication failures for %.100s from %.200s", | 1584 | packet_disconnect(AUTH_FAIL_MSG, user); |
1542 | user, get_canonical_hostname()); | 1585 | |
1543 | /* Send failure. This should be indistinguishable from a failed | 1586 | /* Send failure. This should be indistinguishable from a |
1544 | authentication. */ | 1587 | failed authentication. */ |
1545 | packet_start(SSH_SMSG_FAILURE); | 1588 | packet_start(SSH_SMSG_FAILURE); |
1546 | packet_send(); | 1589 | packet_send(); |
1547 | packet_write_wait(); | 1590 | packet_write_wait(); |
1548 | } | 1591 | } |
1549 | /*NOTREACHED*/ | 1592 | /* NOTREACHED */ |
1550 | abort(); | 1593 | abort(); |
1551 | } | 1594 | } |
1552 | 1595 | ||
1553 | 1596 | ||
1554 | /* Remove local Xauthority file. */ | 1597 | /* |
1598 | * Remove local Xauthority file. | ||
1599 | */ | ||
1555 | static void | 1600 | static void |
1556 | xauthfile_cleanup_proc(void *ignore) | 1601 | xauthfile_cleanup_proc(void *ignore) |
1557 | { | 1602 | { |
1558 | debug("xauthfile_cleanup_proc called"); | 1603 | debug("xauthfile_cleanup_proc called"); |
1559 | 1604 | ||
1560 | if (xauthfile != NULL) { | 1605 | if (xauthfile != NULL) { |
1561 | unlink(xauthfile); | 1606 | unlink(xauthfile); |
1562 | xfree(xauthfile); | 1607 | xfree(xauthfile); |
1563 | xauthfile = NULL; | 1608 | xauthfile = NULL; |
1564 | } | 1609 | } |
1565 | } | 1610 | } |
1566 | 1611 | ||
1567 | /* Prepares for an interactive session. This is called after the user has | 1612 | /* |
1568 | been successfully authenticated. During this message exchange, pseudo | 1613 | * Prepares for an interactive session. This is called after the user has |
1569 | terminals are allocated, X11, TCP/IP, and authentication agent forwardings | 1614 | * been successfully authenticated. During this message exchange, pseudo |
1570 | are requested, etc. */ | 1615 | * terminals are allocated, X11, TCP/IP, and authentication agent forwardings |
1571 | 1616 | * are requested, etc. | |
1572 | void do_authenticated(struct passwd *pw) | 1617 | */ |
1618 | void | ||
1619 | do_authenticated(struct passwd * pw) | ||
1573 | { | 1620 | { |
1574 | int type; | 1621 | int type; |
1575 | int compression_level = 0, enable_compression_after_reply = 0; | 1622 | int compression_level = 0, enable_compression_after_reply = 0; |
1576 | int have_pty = 0, ptyfd = -1, ttyfd = -1, xauthfd = -1; | 1623 | int have_pty = 0, ptyfd = -1, ttyfd = -1, xauthfd = -1; |
1577 | int row, col, xpixel, ypixel, screen; | 1624 | int row, col, xpixel, ypixel, screen; |
1578 | char ttyname[64]; | 1625 | char ttyname[64]; |
1579 | char *command, *term = NULL, *display = NULL, *proto = NULL, *data = NULL; | 1626 | char *command, *term = NULL, *display = NULL, *proto = NULL, |
1580 | struct group *grp; | 1627 | *data = NULL; |
1581 | gid_t tty_gid; | 1628 | struct group *grp; |
1582 | mode_t tty_mode; | 1629 | gid_t tty_gid; |
1583 | int n_bytes; | 1630 | mode_t tty_mode; |
1584 | 1631 | int n_bytes; | |
1585 | /* Cancel the alarm we set to limit the time taken for authentication. */ | 1632 | |
1586 | alarm(0); | 1633 | /* Cancel the alarm we set to limit the time taken for |
1587 | 1634 | authentication. */ | |
1588 | /* Inform the channel mechanism that we are the server side and that | 1635 | alarm(0); |
1589 | the client may request to connect to any port at all. (The user could | 1636 | |
1590 | do it anyway, and we wouldn\'t know what is permitted except by the | 1637 | /* Inform the channel mechanism that we are the server side and |
1591 | client telling us, so we can equally well trust the client not to request | 1638 | that the client may request to connect to any port at all. |
1592 | anything bogus.) */ | 1639 | (The user could do it anyway, and we wouldn\'t know what is |
1593 | channel_permit_all_opens(); | 1640 | permitted except by the client telling us, so we can equally |
1594 | 1641 | well trust the client not to request anything bogus.) */ | |
1595 | /* We stay in this loop until the client requests to execute a shell or a | 1642 | channel_permit_all_opens(); |
1596 | command. */ | 1643 | |
1597 | while (1) | 1644 | /* We stay in this loop until the client requests to execute a |
1598 | { | 1645 | shell or a command. */ |
1599 | int plen, dlen; | 1646 | while (1) { |
1600 | 1647 | int plen, dlen; | |
1601 | /* Get a packet from the client. */ | 1648 | |
1602 | type = packet_read(&plen); | 1649 | /* Get a packet from the client. */ |
1603 | 1650 | type = packet_read(&plen); | |
1604 | /* Process the packet. */ | 1651 | |
1605 | switch (type) | 1652 | /* Process the packet. */ |
1606 | { | 1653 | switch (type) { |
1607 | case SSH_CMSG_REQUEST_COMPRESSION: | 1654 | case SSH_CMSG_REQUEST_COMPRESSION: |
1608 | packet_integrity_check(plen, 4, type); | 1655 | packet_integrity_check(plen, 4, type); |
1609 | compression_level = packet_get_int(); | 1656 | compression_level = packet_get_int(); |
1610 | if (compression_level < 1 || compression_level > 9) | 1657 | if (compression_level < 1 || compression_level > 9) { |
1611 | { | 1658 | packet_send_debug("Received illegal compression level %d.", |
1612 | packet_send_debug("Received illegal compression level %d.", | 1659 | compression_level); |
1613 | compression_level); | 1660 | goto fail; |
1614 | goto fail; | 1661 | } |
1615 | } | 1662 | /* Enable compression after we have responded with SUCCESS. */ |
1616 | /* Enable compression after we have responded with SUCCESS. */ | 1663 | enable_compression_after_reply = 1; |
1617 | enable_compression_after_reply = 1; | 1664 | break; |
1618 | break; | 1665 | |
1619 | 1666 | case SSH_CMSG_REQUEST_PTY: | |
1620 | case SSH_CMSG_REQUEST_PTY: | 1667 | if (no_pty_flag) { |
1621 | if (no_pty_flag) | 1668 | debug("Allocating a pty not permitted for this authentication."); |
1622 | { | 1669 | goto fail; |
1623 | debug("Allocating a pty not permitted for this authentication."); | 1670 | } |
1624 | goto fail; | 1671 | if (have_pty) |
1625 | } | 1672 | packet_disconnect("Protocol error: you already have a pty."); |
1626 | if (have_pty) | 1673 | |
1627 | packet_disconnect("Protocol error: you already have a pty."); | 1674 | debug("Allocating pty."); |
1628 | 1675 | ||
1629 | debug("Allocating pty."); | 1676 | /* Allocate a pty and open it. */ |
1630 | 1677 | if (!pty_allocate(&ptyfd, &ttyfd, ttyname)) { | |
1631 | /* Allocate a pty and open it. */ | 1678 | error("Failed to allocate pty."); |
1632 | if (!pty_allocate(&ptyfd, &ttyfd, ttyname)) | 1679 | goto fail; |
1633 | { | 1680 | } |
1634 | error("Failed to allocate pty."); | 1681 | /* Determine the group to make the owner of the tty. */ |
1635 | goto fail; | 1682 | grp = getgrnam("tty"); |
1636 | } | 1683 | if (grp) { |
1637 | 1684 | tty_gid = grp->gr_gid; | |
1638 | /* Determine the group to make the owner of the tty. */ | 1685 | tty_mode = S_IRUSR | S_IWUSR | S_IWGRP; |
1639 | grp = getgrnam("tty"); | 1686 | } else { |
1640 | if (grp) | 1687 | tty_gid = pw->pw_gid; |
1641 | { | 1688 | tty_mode = S_IRUSR | S_IWUSR | S_IWGRP | S_IWOTH; |
1642 | tty_gid = grp->gr_gid; | 1689 | } |
1643 | tty_mode = S_IRUSR|S_IWUSR|S_IWGRP; | 1690 | |
1644 | } | 1691 | /* Change ownership of the tty. */ |
1645 | else | 1692 | if (chown(ttyname, pw->pw_uid, tty_gid) < 0) |
1646 | { | 1693 | fatal("chown(%.100s, %d, %d) failed: %.100s", |
1647 | tty_gid = pw->pw_gid; | 1694 | ttyname, pw->pw_uid, tty_gid, strerror(errno)); |
1648 | tty_mode = S_IRUSR|S_IWUSR|S_IWGRP|S_IWOTH; | 1695 | if (chmod(ttyname, tty_mode) < 0) |
1649 | } | 1696 | fatal("chmod(%.100s, 0%o) failed: %.100s", |
1650 | 1697 | ttyname, tty_mode, strerror(errno)); | |
1651 | /* Change ownership of the tty. */ | 1698 | |
1652 | if (chown(ttyname, pw->pw_uid, tty_gid) < 0) | 1699 | /* Get TERM from the packet. Note that the value may be of arbitrary length. */ |
1653 | fatal("chown(%.100s, %d, %d) failed: %.100s", | 1700 | term = packet_get_string(&dlen); |
1654 | ttyname, pw->pw_uid, tty_gid, strerror(errno)); | 1701 | packet_integrity_check(dlen, strlen(term), type); |
1655 | if (chmod(ttyname, tty_mode) < 0) | 1702 | /* packet_integrity_check(plen, 4 + dlen + 4*4 + n_bytes, type); */ |
1656 | fatal("chmod(%.100s, 0%o) failed: %.100s", | 1703 | /* Remaining bytes */ |
1657 | ttyname, tty_mode, strerror(errno)); | 1704 | n_bytes = plen - (4 + dlen + 4 * 4); |
1658 | 1705 | ||
1659 | /* Get TERM from the packet. Note that the value may be of arbitrary | 1706 | if (strcmp(term, "") == 0) |
1660 | length. */ | 1707 | term = NULL; |
1661 | 1708 | ||
1662 | term = packet_get_string(&dlen); | 1709 | /* Get window size from the packet. */ |
1663 | packet_integrity_check(dlen, strlen(term), type); | 1710 | row = packet_get_int(); |
1664 | /* packet_integrity_check(plen, 4 + dlen + 4*4 + n_bytes, type); */ | 1711 | col = packet_get_int(); |
1665 | /* Remaining bytes */ | 1712 | xpixel = packet_get_int(); |
1666 | n_bytes = plen - (4 + dlen + 4*4); | 1713 | ypixel = packet_get_int(); |
1667 | 1714 | pty_change_window_size(ptyfd, row, col, xpixel, ypixel); | |
1668 | if (strcmp(term, "") == 0) | 1715 | |
1669 | term = NULL; | 1716 | /* Get tty modes from the packet. */ |
1670 | 1717 | tty_parse_modes(ttyfd, &n_bytes); | |
1671 | /* Get window size from the packet. */ | 1718 | packet_integrity_check(plen, 4 + dlen + 4 * 4 + n_bytes, type); |
1672 | row = packet_get_int(); | 1719 | |
1673 | col = packet_get_int(); | 1720 | /* Indicate that we now have a pty. */ |
1674 | xpixel = packet_get_int(); | 1721 | have_pty = 1; |
1675 | ypixel = packet_get_int(); | 1722 | break; |
1676 | pty_change_window_size(ptyfd, row, col, xpixel, ypixel); | 1723 | |
1677 | 1724 | case SSH_CMSG_X11_REQUEST_FORWARDING: | |
1678 | /* Get tty modes from the packet. */ | 1725 | if (!options.x11_forwarding) { |
1679 | tty_parse_modes(ttyfd, &n_bytes); | 1726 | packet_send_debug("X11 forwarding disabled in server configuration file."); |
1680 | packet_integrity_check(plen, 4 + dlen + 4*4 + n_bytes, type); | 1727 | goto fail; |
1681 | 1728 | } | |
1682 | /* Indicate that we now have a pty. */ | ||
1683 | have_pty = 1; | ||
1684 | break; | ||
1685 | |||
1686 | case SSH_CMSG_X11_REQUEST_FORWARDING: | ||
1687 | if (!options.x11_forwarding) | ||
1688 | { | ||
1689 | packet_send_debug("X11 forwarding disabled in server configuration file."); | ||
1690 | goto fail; | ||
1691 | } | ||
1692 | #ifdef XAUTH_PATH | 1729 | #ifdef XAUTH_PATH |
1693 | if (no_x11_forwarding_flag) | 1730 | if (no_x11_forwarding_flag) { |
1694 | { | 1731 | packet_send_debug("X11 forwarding not permitted for this authentication."); |
1695 | packet_send_debug("X11 forwarding not permitted for this authentication."); | 1732 | goto fail; |
1696 | goto fail; | 1733 | } |
1697 | } | 1734 | debug("Received request for X11 forwarding with auth spoofing."); |
1698 | debug("Received request for X11 forwarding with auth spoofing."); | 1735 | if (display) |
1699 | if (display) | 1736 | packet_disconnect("Protocol error: X11 display already set."); |
1700 | packet_disconnect("Protocol error: X11 display already set."); | 1737 | { |
1701 | { | 1738 | int proto_len, data_len; |
1702 | int proto_len, data_len; | 1739 | proto = packet_get_string(&proto_len); |
1703 | proto = packet_get_string(&proto_len); | 1740 | data = packet_get_string(&data_len); |
1704 | data = packet_get_string(&data_len); | 1741 | packet_integrity_check(plen, 4 + proto_len + 4 + data_len + 4, type); |
1705 | packet_integrity_check(plen, 4+proto_len + 4+data_len + 4, type); | 1742 | } |
1706 | } | 1743 | if (packet_get_protocol_flags() & SSH_PROTOFLAG_SCREEN_NUMBER) |
1707 | if (packet_get_protocol_flags() & SSH_PROTOFLAG_SCREEN_NUMBER) | 1744 | screen = packet_get_int(); |
1708 | screen = packet_get_int(); | 1745 | else |
1709 | else | 1746 | screen = 0; |
1710 | screen = 0; | 1747 | display = x11_create_display_inet(screen); |
1711 | display = x11_create_display_inet(screen); | 1748 | if (!display) |
1712 | if (!display) | 1749 | goto fail; |
1713 | goto fail; | 1750 | |
1714 | 1751 | /* Setup to always have a local .Xauthority. */ | |
1715 | /* Setup to always have a local .Xauthority. */ | 1752 | xauthfile = xmalloc(MAXPATHLEN); |
1716 | xauthfile = xmalloc(MAXPATHLEN); | 1753 | snprintf(xauthfile, MAXPATHLEN, "/tmp/XauthXXXXXX"); |
1717 | snprintf(xauthfile, MAXPATHLEN, "/tmp/XauthXXXXXX"); | 1754 | |
1718 | 1755 | if ((xauthfd = mkstemp(xauthfile)) != -1) { | |
1719 | if ((xauthfd = mkstemp(xauthfile)) != -1) { | 1756 | fchown(xauthfd, pw->pw_uid, pw->pw_gid); |
1720 | fchown(xauthfd, pw->pw_uid, pw->pw_gid); | 1757 | close(xauthfd); |
1721 | close(xauthfd); | 1758 | fatal_add_cleanup(xauthfile_cleanup_proc, NULL); |
1722 | fatal_add_cleanup(xauthfile_cleanup_proc, NULL); | 1759 | } else { |
1723 | } | 1760 | xfree(xauthfile); |
1724 | else { | 1761 | xauthfile = NULL; |
1725 | xfree(xauthfile); | 1762 | } |
1726 | xauthfile = NULL; | 1763 | break; |
1727 | } | ||
1728 | break; | ||
1729 | #else /* XAUTH_PATH */ | 1764 | #else /* XAUTH_PATH */ |
1730 | /* No xauth program; we won't accept forwarding with spoofing. */ | 1765 | packet_send_debug("No xauth program; cannot forward with spoofing."); |
1731 | packet_send_debug("No xauth program; cannot forward with spoofing."); | 1766 | goto fail; |
1732 | goto fail; | ||
1733 | #endif /* XAUTH_PATH */ | 1767 | #endif /* XAUTH_PATH */ |
1734 | 1768 | ||
1735 | case SSH_CMSG_AGENT_REQUEST_FORWARDING: | 1769 | case SSH_CMSG_AGENT_REQUEST_FORWARDING: |
1736 | if (no_agent_forwarding_flag) | 1770 | if (no_agent_forwarding_flag) { |
1737 | { | 1771 | debug("Authentication agent forwarding not permitted for this authentication."); |
1738 | debug("Authentication agent forwarding not permitted for this authentication."); | 1772 | goto fail; |
1739 | goto fail; | 1773 | } |
1740 | } | 1774 | debug("Received authentication agent forwarding request."); |
1741 | debug("Received authentication agent forwarding request."); | 1775 | auth_input_request_forwarding(pw); |
1742 | auth_input_request_forwarding(pw); | 1776 | break; |
1743 | break; | 1777 | |
1744 | 1778 | case SSH_CMSG_PORT_FORWARD_REQUEST: | |
1745 | case SSH_CMSG_PORT_FORWARD_REQUEST: | 1779 | if (no_port_forwarding_flag) { |
1746 | if (no_port_forwarding_flag) | 1780 | debug("Port forwarding not permitted for this authentication."); |
1747 | { | 1781 | goto fail; |
1748 | debug("Port forwarding not permitted for this authentication."); | 1782 | } |
1749 | goto fail; | 1783 | debug("Received TCP/IP port forwarding request."); |
1750 | } | 1784 | channel_input_port_forward_request(pw->pw_uid == 0); |
1751 | debug("Received TCP/IP port forwarding request."); | 1785 | break; |
1752 | channel_input_port_forward_request(pw->pw_uid == 0); | 1786 | |
1753 | break; | 1787 | case SSH_CMSG_MAX_PACKET_SIZE: |
1754 | 1788 | if (packet_set_maxsize(packet_get_int()) < 0) | |
1755 | case SSH_CMSG_MAX_PACKET_SIZE: | 1789 | goto fail; |
1756 | if (packet_set_maxsize(packet_get_int()) < 0) | 1790 | break; |
1757 | goto fail; | 1791 | |
1758 | break; | 1792 | case SSH_CMSG_EXEC_SHELL: |
1759 | 1793 | /* Set interactive/non-interactive mode. */ | |
1760 | case SSH_CMSG_EXEC_SHELL: | 1794 | packet_set_interactive(have_pty || display != NULL, |
1761 | /* Set interactive/non-interactive mode. */ | 1795 | options.keepalives); |
1762 | packet_set_interactive(have_pty || display != NULL, | 1796 | |
1763 | options.keepalives); | 1797 | if (forced_command != NULL) |
1764 | 1798 | goto do_forced_command; | |
1765 | if (forced_command != NULL) | 1799 | debug("Forking shell."); |
1766 | goto do_forced_command; | 1800 | packet_integrity_check(plen, 0, type); |
1767 | debug("Forking shell."); | 1801 | if (have_pty) |
1768 | packet_integrity_check(plen, 0, type); | 1802 | do_exec_pty(NULL, ptyfd, ttyfd, ttyname, pw, term, display, proto, data); |
1769 | if (have_pty) | 1803 | else |
1770 | do_exec_pty(NULL, ptyfd, ttyfd, ttyname, pw, term, display, proto, | 1804 | do_exec_no_pty(NULL, pw, display, proto, data); |
1771 | data); | 1805 | return; |
1772 | else | 1806 | |
1773 | do_exec_no_pty(NULL, pw, display, proto, data); | 1807 | case SSH_CMSG_EXEC_CMD: |
1774 | return; | 1808 | /* Set interactive/non-interactive mode. */ |
1775 | 1809 | packet_set_interactive(have_pty || display != NULL, | |
1776 | case SSH_CMSG_EXEC_CMD: | 1810 | options.keepalives); |
1777 | /* Set interactive/non-interactive mode. */ | 1811 | |
1778 | packet_set_interactive(have_pty || display != NULL, | 1812 | if (forced_command != NULL) |
1779 | options.keepalives); | 1813 | goto do_forced_command; |
1780 | 1814 | /* Get command from the packet. */ | |
1781 | if (forced_command != NULL) | 1815 | { |
1782 | goto do_forced_command; | 1816 | int dlen; |
1783 | /* Get command from the packet. */ | 1817 | command = packet_get_string(&dlen); |
1784 | { | 1818 | debug("Executing command '%.500s'", command); |
1785 | int dlen; | 1819 | packet_integrity_check(plen, 4 + dlen, type); |
1786 | command = packet_get_string(&dlen); | 1820 | } |
1787 | debug("Executing command '%.500s'", command); | 1821 | if (have_pty) |
1788 | packet_integrity_check(plen, 4 + dlen, type); | 1822 | do_exec_pty(command, ptyfd, ttyfd, ttyname, pw, term, display, proto, data); |
1789 | } | 1823 | else |
1790 | if (have_pty) | 1824 | do_exec_no_pty(command, pw, display, proto, data); |
1791 | do_exec_pty(command, ptyfd, ttyfd, ttyname, pw, term, display, | 1825 | xfree(command); |
1792 | proto, data); | 1826 | return; |
1793 | else | 1827 | |
1794 | do_exec_no_pty(command, pw, display, proto, data); | 1828 | default: |
1795 | xfree(command); | 1829 | /* Any unknown messages in this phase are ignored, |
1796 | return; | 1830 | and a failure message is returned. */ |
1797 | 1831 | log("Unknown packet type received after authentication: %d", type); | |
1798 | default: | 1832 | goto fail; |
1799 | /* Any unknown messages in this phase are ignored, and a failure | 1833 | } |
1800 | message is returned. */ | ||
1801 | log("Unknown packet type received after authentication: %d", type); | ||
1802 | goto fail; | ||
1803 | } | ||
1804 | 1834 | ||
1805 | /* The request was successfully processed. */ | 1835 | /* The request was successfully processed. */ |
1806 | packet_start(SSH_SMSG_SUCCESS); | 1836 | packet_start(SSH_SMSG_SUCCESS); |
1807 | packet_send(); | 1837 | packet_send(); |
1808 | packet_write_wait(); | 1838 | packet_write_wait(); |
1809 | 1839 | ||
1810 | /* Enable compression now that we have replied if appropriate. */ | 1840 | /* Enable compression now that we have replied if appropriate. */ |
1811 | if (enable_compression_after_reply) | 1841 | if (enable_compression_after_reply) { |
1812 | { | 1842 | enable_compression_after_reply = 0; |
1813 | enable_compression_after_reply = 0; | 1843 | packet_start_compression(compression_level); |
1814 | packet_start_compression(compression_level); | 1844 | } |
1815 | } | 1845 | continue; |
1816 | 1846 | ||
1817 | continue; | 1847 | fail: |
1818 | 1848 | /* The request failed. */ | |
1819 | fail: | 1849 | packet_start(SSH_SMSG_FAILURE); |
1820 | /* The request failed. */ | 1850 | packet_send(); |
1821 | packet_start(SSH_SMSG_FAILURE); | 1851 | packet_write_wait(); |
1822 | packet_send(); | 1852 | continue; |
1823 | packet_write_wait(); | ||
1824 | continue; | ||
1825 | |||
1826 | do_forced_command: | ||
1827 | /* There is a forced command specified for this login. Execute it. */ | ||
1828 | debug("Executing forced command: %.900s", forced_command); | ||
1829 | if (have_pty) | ||
1830 | do_exec_pty(forced_command, ptyfd, ttyfd, ttyname, pw, term, display, | ||
1831 | proto, data); | ||
1832 | else | ||
1833 | do_exec_no_pty(forced_command, pw, display, proto, data); | ||
1834 | return; | ||
1835 | } | ||
1836 | } | ||
1837 | 1853 | ||
1838 | /* This is called to fork and execute a command when we have no tty. This | 1854 | do_forced_command: |
1839 | will call do_child from the child, and server_loop from the parent after | 1855 | /* There is a forced command specified for this login. |
1840 | setting up file descriptors and such. */ | 1856 | Execute it. */ |
1857 | debug("Executing forced command: %.900s", forced_command); | ||
1858 | if (have_pty) | ||
1859 | do_exec_pty(forced_command, ptyfd, ttyfd, ttyname, pw, term, display, proto, data); | ||
1860 | else | ||
1861 | do_exec_no_pty(forced_command, pw, display, proto, data); | ||
1862 | return; | ||
1863 | } | ||
1864 | } | ||
1841 | 1865 | ||
1842 | void do_exec_no_pty(const char *command, struct passwd *pw, | 1866 | /* |
1843 | const char *display, const char *auth_proto, | 1867 | * This is called to fork and execute a command when we have no tty. This |
1844 | const char *auth_data) | 1868 | * will call do_child from the child, and server_loop from the parent after |
1845 | { | 1869 | * setting up file descriptors and such. |
1846 | int pid; | 1870 | */ |
1871 | void | ||
1872 | do_exec_no_pty(const char *command, struct passwd * pw, | ||
1873 | const char *display, const char *auth_proto, | ||
1874 | const char *auth_data) | ||
1875 | { | ||
1876 | int pid; | ||
1847 | 1877 | ||
1848 | #ifdef USE_PIPES | 1878 | #ifdef USE_PIPES |
1849 | int pin[2], pout[2], perr[2]; | 1879 | int pin[2], pout[2], perr[2]; |
1850 | /* Allocate pipes for communicating with the program. */ | 1880 | /* Allocate pipes for communicating with the program. */ |
1851 | if (pipe(pin) < 0 || pipe(pout) < 0 || pipe(perr) < 0) | 1881 | if (pipe(pin) < 0 || pipe(pout) < 0 || pipe(perr) < 0) |
1852 | packet_disconnect("Could not create pipes: %.100s", | 1882 | packet_disconnect("Could not create pipes: %.100s", |
1853 | strerror(errno)); | 1883 | strerror(errno)); |
1854 | #else /* USE_PIPES */ | 1884 | #else /* USE_PIPES */ |
1855 | int inout[2], err[2]; | 1885 | int inout[2], err[2]; |
1856 | /* Uses socket pairs to communicate with the program. */ | 1886 | /* Uses socket pairs to communicate with the program. */ |
1857 | if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) < 0 || | 1887 | if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) < 0 || |
1858 | socketpair(AF_UNIX, SOCK_STREAM, 0, err) < 0) | 1888 | socketpair(AF_UNIX, SOCK_STREAM, 0, err) < 0) |
1859 | packet_disconnect("Could not create socket pairs: %.100s", | 1889 | packet_disconnect("Could not create socket pairs: %.100s", |
1860 | strerror(errno)); | 1890 | strerror(errno)); |
1861 | #endif /* USE_PIPES */ | 1891 | #endif /* USE_PIPES */ |
1862 | |||
1863 | setproctitle("%s@notty", pw->pw_name); | ||
1864 | 1892 | ||
1865 | /* Fork the child. */ | 1893 | setproctitle("%s@notty", pw->pw_name); |
1866 | if ((pid = fork()) == 0) | ||
1867 | { | ||
1868 | /* Child. Reinitialize the log since the pid has changed. */ | ||
1869 | log_init(av0, options.log_level, options.log_facility, log_stderr); | ||
1870 | 1894 | ||
1871 | /* Create a new session and process group since the 4.4BSD setlogin() | 1895 | /* Fork the child. */ |
1872 | affects the entire process group. */ | 1896 | if ((pid = fork()) == 0) { |
1873 | if (setsid() < 0) | 1897 | /* Child. Reinitialize the log since the pid has changed. */ |
1874 | error("setsid failed: %.100s", strerror(errno)); | 1898 | log_init(av0, options.log_level, options.log_facility, log_stderr); |
1899 | |||
1900 | /* Create a new session and process group since the 4.4BSD | ||
1901 | setlogin() affects the entire process group. */ | ||
1902 | if (setsid() < 0) | ||
1903 | error("setsid failed: %.100s", strerror(errno)); | ||
1875 | 1904 | ||
1876 | #ifdef USE_PIPES | 1905 | #ifdef USE_PIPES |
1877 | /* Redirect stdin. We close the parent side of the socket pair, | 1906 | /* Redirect stdin. We close the parent side of the socket |
1878 | and make the child side the standard input. */ | 1907 | pair, and make the child side the standard input. */ |
1879 | close(pin[1]); | 1908 | close(pin[1]); |
1880 | if (dup2(pin[0], 0) < 0) | 1909 | if (dup2(pin[0], 0) < 0) |
1881 | perror("dup2 stdin"); | 1910 | perror("dup2 stdin"); |
1882 | close(pin[0]); | 1911 | close(pin[0]); |
1883 | 1912 | ||
1884 | /* Redirect stdout. */ | 1913 | /* Redirect stdout. */ |
1885 | close(pout[0]); | 1914 | close(pout[0]); |
1886 | if (dup2(pout[1], 1) < 0) | 1915 | if (dup2(pout[1], 1) < 0) |
1887 | perror("dup2 stdout"); | 1916 | perror("dup2 stdout"); |
1888 | close(pout[1]); | 1917 | close(pout[1]); |
1889 | 1918 | ||
1890 | /* Redirect stderr. */ | 1919 | /* Redirect stderr. */ |
1891 | close(perr[0]); | 1920 | close(perr[0]); |
1892 | if (dup2(perr[1], 2) < 0) | 1921 | if (dup2(perr[1], 2) < 0) |
1893 | perror("dup2 stderr"); | 1922 | perror("dup2 stderr"); |
1894 | close(perr[1]); | 1923 | close(perr[1]); |
1895 | #else /* USE_PIPES */ | 1924 | #else /* USE_PIPES */ |
1896 | /* Redirect stdin, stdout, and stderr. Stdin and stdout will use the | 1925 | /* Redirect stdin, stdout, and stderr. Stdin and stdout |
1897 | same socket, as some programs (particularly rdist) seem to depend | 1926 | will use the same socket, as some programs |
1898 | on it. */ | 1927 | (particularly rdist) seem to depend on it. */ |
1899 | close(inout[1]); | 1928 | close(inout[1]); |
1900 | close(err[1]); | 1929 | close(err[1]); |
1901 | if (dup2(inout[0], 0) < 0) /* stdin */ | 1930 | if (dup2(inout[0], 0) < 0) /* stdin */ |
1902 | perror("dup2 stdin"); | 1931 | perror("dup2 stdin"); |
1903 | if (dup2(inout[0], 1) < 0) /* stdout. Note: same socket as stdin. */ | 1932 | if (dup2(inout[0], 1) < 0) /* stdout. Note: same socket as stdin. */ |
1904 | perror("dup2 stdout"); | 1933 | perror("dup2 stdout"); |
1905 | if (dup2(err[0], 2) < 0) /* stderr */ | 1934 | if (dup2(err[0], 2) < 0) /* stderr */ |
1906 | perror("dup2 stderr"); | 1935 | perror("dup2 stderr"); |
1907 | #endif /* USE_PIPES */ | 1936 | #endif /* USE_PIPES */ |
1908 | 1937 | ||
1909 | /* Do processing for the child (exec command etc). */ | 1938 | /* Do processing for the child (exec command etc). */ |
1910 | do_child(command, pw, NULL, display, auth_proto, auth_data, NULL); | 1939 | do_child(command, pw, NULL, display, auth_proto, auth_data, NULL); |
1911 | /*NOTREACHED*/ | 1940 | /* NOTREACHED */ |
1912 | } | 1941 | } |
1913 | if (pid < 0) | 1942 | if (pid < 0) |
1914 | packet_disconnect("fork failed: %.100s", strerror(errno)); | 1943 | packet_disconnect("fork failed: %.100s", strerror(errno)); |
1915 | #ifdef USE_PIPES | 1944 | #ifdef USE_PIPES |
1916 | /* We are the parent. Close the child sides of the pipes. */ | 1945 | /* We are the parent. Close the child sides of the pipes. */ |
1917 | close(pin[0]); | 1946 | close(pin[0]); |
1918 | close(pout[1]); | 1947 | close(pout[1]); |
1919 | close(perr[1]); | 1948 | close(perr[1]); |
1920 | 1949 | ||
1921 | /* Enter the interactive session. */ | 1950 | /* Enter the interactive session. */ |
1922 | server_loop(pid, pin[1], pout[0], perr[0]); | 1951 | server_loop(pid, pin[1], pout[0], perr[0]); |
1923 | /* server_loop has closed pin[1], pout[1], and perr[1]. */ | 1952 | /* server_loop has closed pin[1], pout[1], and perr[1]. */ |
1924 | #else /* USE_PIPES */ | 1953 | #else /* USE_PIPES */ |
1925 | /* We are the parent. Close the child sides of the socket pairs. */ | 1954 | /* We are the parent. Close the child sides of the socket pairs. */ |
1926 | close(inout[0]); | 1955 | close(inout[0]); |
1927 | close(err[0]); | 1956 | close(err[0]); |
1928 | 1957 | ||
1929 | /* Enter the interactive session. Note: server_loop must be able to handle | 1958 | /* Enter the interactive session. Note: server_loop must be able |
1930 | the case that fdin and fdout are the same. */ | 1959 | to handle the case that fdin and fdout are the same. */ |
1931 | server_loop(pid, inout[1], inout[1], err[1]); | 1960 | server_loop(pid, inout[1], inout[1], err[1]); |
1932 | /* server_loop has closed inout[1] and err[1]. */ | 1961 | /* server_loop has closed inout[1] and err[1]. */ |
1933 | #endif /* USE_PIPES */ | 1962 | #endif /* USE_PIPES */ |
1934 | } | 1963 | } |
1935 | 1964 | ||
1936 | struct pty_cleanup_context | 1965 | struct pty_cleanup_context { |
1937 | { | 1966 | const char *ttyname; |
1938 | const char *ttyname; | 1967 | int pid; |
1939 | int pid; | ||
1940 | }; | 1968 | }; |
1941 | 1969 | ||
1942 | /* Function to perform cleanup if we get aborted abnormally (e.g., due to a | 1970 | /* |
1943 | dropped connection). */ | 1971 | * Function to perform cleanup if we get aborted abnormally (e.g., due to a |
1944 | 1972 | * dropped connection). | |
1945 | void pty_cleanup_proc(void *context) | 1973 | */ |
1974 | void | ||
1975 | pty_cleanup_proc(void *context) | ||
1946 | { | 1976 | { |
1947 | struct pty_cleanup_context *cu = context; | 1977 | struct pty_cleanup_context *cu = context; |
1948 | 1978 | ||
1949 | debug("pty_cleanup_proc called"); | 1979 | debug("pty_cleanup_proc called"); |
1950 | 1980 | ||
1951 | /* Record that the user has logged out. */ | 1981 | /* Record that the user has logged out. */ |
1952 | record_logout(cu->pid, cu->ttyname); | 1982 | record_logout(cu->pid, cu->ttyname); |
1953 | 1983 | ||
1954 | /* Release the pseudo-tty. */ | 1984 | /* Release the pseudo-tty. */ |
1955 | pty_release(cu->ttyname); | 1985 | pty_release(cu->ttyname); |
1956 | } | 1986 | } |
1957 | 1987 | ||
1958 | /* This is called to fork and execute a command when we have a tty. This | 1988 | /* |
1959 | will call do_child from the child, and server_loop from the parent after | 1989 | * This is called to fork and execute a command when we have a tty. This |
1960 | setting up file descriptors, controlling tty, updating wtmp, utmp, | 1990 | * will call do_child from the child, and server_loop from the parent after |
1961 | lastlog, and other such operations. */ | 1991 | * setting up file descriptors, controlling tty, updating wtmp, utmp, |
1962 | 1992 | * lastlog, and other such operations. | |
1963 | void do_exec_pty(const char *command, int ptyfd, int ttyfd, | 1993 | */ |
1964 | const char *ttyname, struct passwd *pw, const char *term, | 1994 | void |
1965 | const char *display, const char *auth_proto, | 1995 | do_exec_pty(const char *command, int ptyfd, int ttyfd, |
1966 | const char *auth_data) | 1996 | const char *ttyname, struct passwd * pw, const char *term, |
1997 | const char *display, const char *auth_proto, | ||
1998 | const char *auth_data) | ||
1967 | { | 1999 | { |
1968 | int pid, fdout; | 2000 | int pid, fdout; |
1969 | const char *hostname; | 2001 | const char *hostname; |
1970 | time_t last_login_time; | 2002 | time_t last_login_time; |
1971 | char buf[100], *time_string; | 2003 | char buf[100], *time_string; |
1972 | FILE *f; | 2004 | FILE *f; |
1973 | char line[256]; | 2005 | char line[256]; |
1974 | struct stat st; | 2006 | struct stat st; |
1975 | int quiet_login; | 2007 | int quiet_login; |
1976 | struct sockaddr_in from; | 2008 | struct sockaddr_in from; |
1977 | int fromlen; | 2009 | int fromlen; |
1978 | struct pty_cleanup_context cleanup_context; | 2010 | struct pty_cleanup_context cleanup_context; |
1979 | 2011 | ||
1980 | /* Get remote host name. */ | 2012 | /* Get remote host name. */ |
1981 | hostname = get_canonical_hostname(); | 2013 | hostname = get_canonical_hostname(); |
1982 | 2014 | ||
1983 | /* Get the time when the user last logged in. Buf will be set to contain | 2015 | /* Get the time when the user last logged in. Buf will be set to |
1984 | the hostname the last login was from. */ | 2016 | contain the hostname the last login was from. */ |
1985 | if(!options.use_login) { | 2017 | if (!options.use_login) { |
1986 | last_login_time = get_last_login_time(pw->pw_uid, pw->pw_name, | 2018 | last_login_time = get_last_login_time(pw->pw_uid, pw->pw_name, |
1987 | buf, sizeof(buf)); | 2019 | buf, sizeof(buf)); |
1988 | } | ||
1989 | |||
1990 | setproctitle("%s@%s", pw->pw_name, strrchr(ttyname, '/') + 1); | ||
1991 | |||
1992 | /* Fork the child. */ | ||
1993 | if ((pid = fork()) == 0) | ||
1994 | { | ||
1995 | pid = getpid(); | ||
1996 | |||
1997 | /* Child. Reinitialize the log because the pid has changed. */ | ||
1998 | log_init(av0, options.log_level, options.log_facility, log_stderr); | ||
1999 | |||
2000 | /* Close the master side of the pseudo tty. */ | ||
2001 | close(ptyfd); | ||
2002 | |||
2003 | /* Make the pseudo tty our controlling tty. */ | ||
2004 | pty_make_controlling_tty(&ttyfd, ttyname); | ||
2005 | |||
2006 | /* Redirect stdin from the pseudo tty. */ | ||
2007 | if (dup2(ttyfd, fileno(stdin)) < 0) | ||
2008 | error("dup2 stdin failed: %.100s", strerror(errno)); | ||
2009 | |||
2010 | /* Redirect stdout to the pseudo tty. */ | ||
2011 | if (dup2(ttyfd, fileno(stdout)) < 0) | ||
2012 | error("dup2 stdin failed: %.100s", strerror(errno)); | ||
2013 | |||
2014 | /* Redirect stderr to the pseudo tty. */ | ||
2015 | if (dup2(ttyfd, fileno(stderr)) < 0) | ||
2016 | error("dup2 stdin failed: %.100s", strerror(errno)); | ||
2017 | |||
2018 | /* Close the extra descriptor for the pseudo tty. */ | ||
2019 | close(ttyfd); | ||
2020 | |||
2021 | /* Get IP address of client. This is needed because we want to record | ||
2022 | where the user logged in from. If the connection is not a socket, | ||
2023 | let the ip address be 0.0.0.0. */ | ||
2024 | memset(&from, 0, sizeof(from)); | ||
2025 | if (packet_get_connection_in() == packet_get_connection_out()) | ||
2026 | { | ||
2027 | fromlen = sizeof(from); | ||
2028 | if (getpeername(packet_get_connection_in(), | ||
2029 | (struct sockaddr *)&from, &fromlen) < 0) { | ||
2030 | debug("getpeername: %.100s", strerror(errno)); | ||
2031 | fatal_cleanup(); | ||
2032 | } | ||
2033 | } | 2020 | } |
2021 | setproctitle("%s@%s", pw->pw_name, strrchr(ttyname, '/') + 1); | ||
2022 | |||
2023 | /* Fork the child. */ | ||
2024 | if ((pid = fork()) == 0) { | ||
2025 | pid = getpid(); | ||
2026 | |||
2027 | /* Child. Reinitialize the log because the pid has | ||
2028 | changed. */ | ||
2029 | log_init(av0, options.log_level, options.log_facility, log_stderr); | ||
2030 | |||
2031 | /* Close the master side of the pseudo tty. */ | ||
2032 | close(ptyfd); | ||
2033 | |||
2034 | /* Make the pseudo tty our controlling tty. */ | ||
2035 | pty_make_controlling_tty(&ttyfd, ttyname); | ||
2036 | |||
2037 | /* Redirect stdin from the pseudo tty. */ | ||
2038 | if (dup2(ttyfd, fileno(stdin)) < 0) | ||
2039 | error("dup2 stdin failed: %.100s", strerror(errno)); | ||
2040 | |||
2041 | /* Redirect stdout to the pseudo tty. */ | ||
2042 | if (dup2(ttyfd, fileno(stdout)) < 0) | ||
2043 | error("dup2 stdin failed: %.100s", strerror(errno)); | ||
2044 | |||
2045 | /* Redirect stderr to the pseudo tty. */ | ||
2046 | if (dup2(ttyfd, fileno(stderr)) < 0) | ||
2047 | error("dup2 stdin failed: %.100s", strerror(errno)); | ||
2048 | |||
2049 | /* Close the extra descriptor for the pseudo tty. */ | ||
2050 | close(ttyfd); | ||
2051 | |||
2052 | /* Get IP address of client. This is needed because we | ||
2053 | want to record where the user logged in from. If the | ||
2054 | connection is not a socket, let the ip address be 0.0.0.0. */ | ||
2055 | memset(&from, 0, sizeof(from)); | ||
2056 | if (packet_get_connection_in() == packet_get_connection_out()) { | ||
2057 | fromlen = sizeof(from); | ||
2058 | if (getpeername(packet_get_connection_in(), | ||
2059 | (struct sockaddr *) & from, &fromlen) < 0) { | ||
2060 | debug("getpeername: %.100s", strerror(errno)); | ||
2061 | fatal_cleanup(); | ||
2062 | } | ||
2063 | } | ||
2064 | /* Record that there was a login on that terminal. */ | ||
2065 | record_login(pid, ttyname, pw->pw_name, pw->pw_uid, hostname, | ||
2066 | &from); | ||
2034 | 2067 | ||
2035 | /* Record that there was a login on that terminal. */ | 2068 | /* Check if .hushlogin exists. */ |
2036 | record_login(pid, ttyname, pw->pw_name, pw->pw_uid, hostname, | 2069 | snprintf(line, sizeof line, "%.200s/.hushlogin", pw->pw_dir); |
2037 | &from); | 2070 | quiet_login = stat(line, &st) >= 0; |
2038 | |||
2039 | /* Check if .hushlogin exists. */ | ||
2040 | snprintf(line, sizeof line, "%.200s/.hushlogin", pw->pw_dir); | ||
2041 | quiet_login = stat(line, &st) >= 0; | ||
2042 | 2071 | ||
2043 | #ifdef HAVE_LIBPAM | 2072 | #ifdef HAVE_LIBPAM |
2044 | /* output the results of the pamconv() */ | 2073 | /* output the results of the pamconv() */ |
2045 | if (!quiet_login && pamconv_msg != NULL) | 2074 | if (!quiet_login && pamconv_msg != NULL) |
2046 | fprintf(stderr, pamconv_msg); | 2075 | fprintf(stderr, pamconv_msg); |
2047 | #endif | 2076 | #endif |
2048 | |||
2049 | /* If the user has logged in before, display the time of last login. | ||
2050 | However, don't display anything extra if a command has been | ||
2051 | specified (so that ssh can be used to execute commands on a remote | ||
2052 | machine without users knowing they are going to another machine). | ||
2053 | Login(1) will do this for us as well, so check if login(1) is used */ | ||
2054 | if (command == NULL && last_login_time != 0 && !quiet_login && | ||
2055 | !options.use_login) | ||
2056 | { | ||
2057 | /* Convert the date to a string. */ | ||
2058 | time_string = ctime(&last_login_time); | ||
2059 | /* Remove the trailing newline. */ | ||
2060 | if (strchr(time_string, '\n')) | ||
2061 | *strchr(time_string, '\n') = 0; | ||
2062 | /* Display the last login time. Host if displayed if known. */ | ||
2063 | if (strcmp(buf, "") == 0) | ||
2064 | printf("Last login: %s\r\n", time_string); | ||
2065 | else | ||
2066 | printf("Last login: %s from %s\r\n", time_string, buf); | ||
2067 | } | ||
2068 | 2077 | ||
2069 | /* Print /etc/motd unless a command was specified or printing it was | 2078 | /* If the user has logged in before, display the time of |
2070 | disabled in server options or login(1) will be used. Note that | 2079 | last login. However, don't display anything extra if a |
2071 | some machines appear to print it in /etc/profile or similar. */ | 2080 | command has been specified (so that ssh can be used to |
2072 | if (command == NULL && options.print_motd && !quiet_login && | 2081 | execute commands on a remote machine without users |
2073 | !options.use_login) | 2082 | knowing they are going to another machine). Login(1) |
2074 | { | 2083 | will do this for us as well, so check if login(1) is used */ |
2075 | /* Print /etc/motd if it exists. */ | 2084 | if (command == NULL && last_login_time != 0 && !quiet_login && |
2076 | f = fopen("/etc/motd", "r"); | 2085 | !options.use_login) { |
2077 | if (f) | 2086 | /* Convert the date to a string. */ |
2078 | { | 2087 | time_string = ctime(&last_login_time); |
2079 | while (fgets(line, sizeof(line), f)) | 2088 | /* Remove the trailing newline. */ |
2080 | fputs(line, stdout); | 2089 | if (strchr(time_string, '\n')) |
2081 | fclose(f); | 2090 | *strchr(time_string, '\n') = 0; |
2082 | } | 2091 | /* Display the last login time. Host if displayed |
2092 | if known. */ | ||
2093 | if (strcmp(buf, "") == 0) | ||
2094 | printf("Last login: %s\r\n", time_string); | ||
2095 | else | ||
2096 | printf("Last login: %s from %s\r\n", time_string, buf); | ||
2097 | } | ||
2098 | /* Print /etc/motd unless a command was specified or | ||
2099 | printing it was disabled in server options or login(1) | ||
2100 | will be used. Note that some machines appear to print | ||
2101 | it in /etc/profile or similar. */ | ||
2102 | if (command == NULL && options.print_motd && !quiet_login && | ||
2103 | !options.use_login) { | ||
2104 | /* Print /etc/motd if it exists. */ | ||
2105 | f = fopen("/etc/motd", "r"); | ||
2106 | if (f) { | ||
2107 | while (fgets(line, sizeof(line), f)) | ||
2108 | fputs(line, stdout); | ||
2109 | fclose(f); | ||
2110 | } | ||
2111 | } | ||
2112 | /* Do common processing for the child, such as execing the command. */ | ||
2113 | do_child(command, pw, term, display, auth_proto, auth_data, ttyname); | ||
2114 | /* NOTREACHED */ | ||
2083 | } | 2115 | } |
2084 | 2116 | if (pid < 0) | |
2085 | /* Do common processing for the child, such as execing the command. */ | 2117 | packet_disconnect("fork failed: %.100s", strerror(errno)); |
2086 | do_child(command, pw, term, display, auth_proto, auth_data, ttyname); | 2118 | /* Parent. Close the slave side of the pseudo tty. */ |
2087 | /*NOTREACHED*/ | 2119 | close(ttyfd); |
2088 | } | 2120 | |
2089 | if (pid < 0) | 2121 | /* Create another descriptor of the pty master side for use as the |
2090 | packet_disconnect("fork failed: %.100s", strerror(errno)); | 2122 | standard input. We could use the original descriptor, but this |
2091 | /* Parent. Close the slave side of the pseudo tty. */ | 2123 | simplifies code in server_loop. The descriptor is bidirectional. */ |
2092 | close(ttyfd); | 2124 | fdout = dup(ptyfd); |
2093 | 2125 | if (fdout < 0) | |
2094 | /* Create another descriptor of the pty master side for use as the standard | 2126 | packet_disconnect("dup failed: %.100s", strerror(errno)); |
2095 | input. We could use the original descriptor, but this simplifies code | 2127 | |
2096 | in server_loop. The descriptor is bidirectional. */ | 2128 | /* Add a cleanup function to clear the utmp entry and record logout |
2097 | fdout = dup(ptyfd); | 2129 | time in case we call fatal() (e.g., the connection gets closed). */ |
2098 | if (fdout < 0) | 2130 | cleanup_context.pid = pid; |
2099 | packet_disconnect("dup failed: %.100s", strerror(errno)); | 2131 | cleanup_context.ttyname = ttyname; |
2100 | 2132 | fatal_add_cleanup(pty_cleanup_proc, (void *) &cleanup_context); | |
2101 | /* Add a cleanup function to clear the utmp entry and record logout time | 2133 | |
2102 | in case we call fatal() (e.g., the connection gets closed). */ | 2134 | /* Enter interactive session. */ |
2103 | cleanup_context.pid = pid; | 2135 | server_loop(pid, ptyfd, fdout, -1); |
2104 | cleanup_context.ttyname = ttyname; | 2136 | /* server_loop has not closed ptyfd and fdout. */ |
2105 | fatal_add_cleanup(pty_cleanup_proc, (void *)&cleanup_context); | 2137 | |
2106 | 2138 | /* Cancel the cleanup function. */ | |
2107 | /* Enter interactive session. */ | 2139 | fatal_remove_cleanup(pty_cleanup_proc, (void *) &cleanup_context); |
2108 | server_loop(pid, ptyfd, fdout, -1); | 2140 | |
2109 | /* server_loop has not closed ptyfd and fdout. */ | 2141 | /* Record that the user has logged out. */ |
2110 | 2142 | record_logout(pid, ttyname); | |
2111 | /* Cancel the cleanup function. */ | 2143 | |
2112 | fatal_remove_cleanup(pty_cleanup_proc, (void *)&cleanup_context); | 2144 | /* Release the pseudo-tty. */ |
2113 | 2145 | pty_release(ttyname); | |
2114 | /* Record that the user has logged out. */ | 2146 | |
2115 | record_logout(pid, ttyname); | 2147 | /* Close the server side of the socket pairs. We must do this |
2116 | 2148 | after the pty cleanup, so that another process doesn't get this | |
2117 | /* Release the pseudo-tty. */ | 2149 | pty while we're still cleaning up. */ |
2118 | pty_release(ttyname); | 2150 | close(ptyfd); |
2119 | 2151 | close(fdout); | |
2120 | /* Close the server side of the socket pairs. We must do this after the | ||
2121 | pty cleanup, so that another process doesn't get this pty while we're | ||
2122 | still cleaning up. */ | ||
2123 | close(ptyfd); | ||
2124 | close(fdout); | ||
2125 | } | 2152 | } |
2126 | 2153 | ||
2127 | /* Sets the value of the given variable in the environment. If the variable | 2154 | /* |
2128 | already exists, its value is overriden. */ | 2155 | * Sets the value of the given variable in the environment. If the variable |
2129 | 2156 | * already exists, its value is overriden. | |
2130 | void child_set_env(char ***envp, unsigned int *envsizep, const char *name, | 2157 | */ |
2131 | const char *value) | 2158 | void |
2159 | child_set_env(char ***envp, unsigned int *envsizep, const char *name, | ||
2160 | const char *value) | ||
2132 | { | 2161 | { |
2133 | unsigned int i, namelen; | 2162 | unsigned int i, namelen; |
2134 | char **env; | 2163 | char **env; |
2135 | 2164 | ||
2136 | /* Find the slot where the value should be stored. If the variable already | 2165 | /* Find the slot where the value should be stored. If the |
2137 | exists, we reuse the slot; otherwise we append a new slot at the end | 2166 | variable already exists, we reuse the slot; otherwise we append |
2138 | of the array, expanding if necessary. */ | 2167 | a new slot at the end of the array, expanding if necessary. */ |
2139 | env = *envp; | 2168 | env = *envp; |
2140 | namelen = strlen(name); | 2169 | namelen = strlen(name); |
2141 | for (i = 0; env[i]; i++) | 2170 | for (i = 0; env[i]; i++) |
2142 | if (strncmp(env[i], name, namelen) == 0 && env[i][namelen] == '=') | 2171 | if (strncmp(env[i], name, namelen) == 0 && env[i][namelen] == '=') |
2143 | break; | 2172 | break; |
2144 | if (env[i]) | 2173 | if (env[i]) { |
2145 | { | 2174 | /* Name already exists. Reuse the slot. */ |
2146 | /* Name already exists. Reuse the slot. */ | 2175 | xfree(env[i]); |
2147 | xfree(env[i]); | 2176 | } else { |
2148 | } | 2177 | /* New variable. Expand the array if necessary. */ |
2149 | else | 2178 | if (i >= (*envsizep) - 1) { |
2150 | { | 2179 | (*envsizep) += 50; |
2151 | /* New variable. Expand the array if necessary. */ | 2180 | env = (*envp) = xrealloc(env, (*envsizep) * sizeof(char *)); |
2152 | if (i >= (*envsizep) - 1) | 2181 | } |
2153 | { | 2182 | /* Need to set the NULL pointer at end of array beyond the new slot. */ |
2154 | (*envsizep) += 50; | 2183 | env[i + 1] = NULL; |
2155 | env = (*envp) = xrealloc(env, (*envsizep) * sizeof(char *)); | ||
2156 | } | 2184 | } |
2157 | 2185 | ||
2158 | /* Need to set the NULL pointer at end of array beyond the new | 2186 | /* Allocate space and format the variable in the appropriate slot. */ |
2159 | slot. */ | 2187 | env[i] = xmalloc(strlen(name) + 1 + strlen(value) + 1); |
2160 | env[i + 1] = NULL; | 2188 | snprintf(env[i], strlen(name) + 1 + strlen(value) + 1, "%s=%s", name, value); |
2161 | } | ||
2162 | |||
2163 | /* Allocate space and format the variable in the appropriate slot. */ | ||
2164 | env[i] = xmalloc(strlen(name) + 1 + strlen(value) + 1); | ||
2165 | snprintf(env[i], strlen(name) + 1 + strlen(value) + 1, "%s=%s", name, value); | ||
2166 | } | 2189 | } |
2167 | 2190 | ||
2168 | /* Reads environment variables from the given file and adds/overrides them | 2191 | /* |
2169 | into the environment. If the file does not exist, this does nothing. | 2192 | * Reads environment variables from the given file and adds/overrides them |
2170 | Otherwise, it must consist of empty lines, comments (line starts with '#') | 2193 | * into the environment. If the file does not exist, this does nothing. |
2171 | and assignments of the form name=value. No other forms are allowed. */ | 2194 | * Otherwise, it must consist of empty lines, comments (line starts with '#') |
2172 | 2195 | * and assignments of the form name=value. No other forms are allowed. | |
2173 | void read_environment_file(char ***env, unsigned int *envsize, | 2196 | */ |
2174 | const char *filename) | 2197 | void |
2198 | read_environment_file(char ***env, unsigned int *envsize, | ||
2199 | const char *filename) | ||
2175 | { | 2200 | { |
2176 | FILE *f; | 2201 | FILE *f; |
2177 | char buf[4096]; | 2202 | char buf[4096]; |
2178 | char *cp, *value; | 2203 | char *cp, *value; |
2179 | 2204 | ||
2180 | /* Open the environment file. */ | 2205 | /* Open the environment file. */ |
2181 | f = fopen(filename, "r"); | 2206 | f = fopen(filename, "r"); |
2182 | if (!f) | 2207 | if (!f) |
2183 | return; /* Not found. */ | 2208 | return; |
2184 | 2209 | ||
2185 | /* Process each line. */ | 2210 | /* Process each line. */ |
2186 | while (fgets(buf, sizeof(buf), f)) | 2211 | while (fgets(buf, sizeof(buf), f)) { |
2187 | { | 2212 | /* Skip leading whitespace. */ |
2188 | /* Skip leading whitespace. */ | 2213 | for (cp = buf; *cp == ' ' || *cp == '\t'; cp++); |
2189 | for (cp = buf; *cp == ' ' || *cp == '\t'; cp++) | 2214 | |
2190 | ; | 2215 | /* Ignore empty and comment lines. */ |
2191 | 2216 | if (!*cp || *cp == '#' || *cp == '\n') | |
2192 | /* Ignore empty and comment lines. */ | 2217 | continue; |
2193 | if (!*cp || *cp == '#' || *cp == '\n') | 2218 | |
2194 | continue; | 2219 | /* Remove newline. */ |
2195 | 2220 | if (strchr(cp, '\n')) | |
2196 | /* Remove newline. */ | 2221 | *strchr(cp, '\n') = '\0'; |
2197 | if (strchr(cp, '\n')) | 2222 | |
2198 | *strchr(cp, '\n') = '\0'; | 2223 | /* Find the equals sign. Its lack indicates badly |
2199 | 2224 | formatted line. */ | |
2200 | /* Find the equals sign. Its lack indicates badly formatted line. */ | 2225 | value = strchr(cp, '='); |
2201 | value = strchr(cp, '='); | 2226 | if (value == NULL) { |
2202 | if (value == NULL) | 2227 | fprintf(stderr, "Bad line in %.100s: %.200s\n", filename, buf); |
2203 | { | 2228 | continue; |
2204 | fprintf(stderr, "Bad line in %.100s: %.200s\n", filename, buf); | 2229 | } |
2205 | continue; | 2230 | /* Replace the equals sign by nul, and advance value to |
2206 | } | 2231 | the value string. */ |
2232 | *value = '\0'; | ||
2233 | value++; | ||
2207 | 2234 | ||
2208 | /* Replace the equals sign by nul, and advance value to the value | 2235 | /* Set the value in environment. */ |
2209 | string. */ | 2236 | child_set_env(env, envsize, cp, value); |
2210 | *value = '\0'; | 2237 | } |
2211 | value++; | ||
2212 | 2238 | ||
2213 | /* Set the value in environment. */ | 2239 | fclose(f); |
2214 | child_set_env(env, envsize, cp, value); | ||
2215 | } | ||
2216 | |||
2217 | fclose(f); | ||
2218 | } | 2240 | } |
2219 | 2241 | ||
2220 | /* Performs common processing for the child, such as setting up the | 2242 | /* |
2221 | environment, closing extra file descriptors, setting the user and group | 2243 | * Performs common processing for the child, such as setting up the |
2222 | ids, and executing the command or shell. */ | 2244 | * environment, closing extra file descriptors, setting the user and group |
2223 | 2245 | * ids, and executing the command or shell. | |
2224 | void do_child(const char *command, struct passwd *pw, const char *term, | 2246 | */ |
2225 | const char *display, const char *auth_proto, | 2247 | void |
2226 | const char *auth_data, const char *ttyname) | 2248 | do_child(const char *command, struct passwd * pw, const char *term, |
2249 | const char *display, const char *auth_proto, | ||
2250 | const char *auth_data, const char *ttyname) | ||
2227 | { | 2251 | { |
2228 | const char *shell, *cp = NULL; | 2252 | const char *shell, *cp = NULL; |
2229 | char buf[256]; | 2253 | char buf[256]; |
2230 | FILE *f; | 2254 | FILE *f; |
2231 | unsigned int envsize, i; | 2255 | unsigned int envsize, i; |
2232 | char **env; | 2256 | char **env; |
2233 | extern char **environ; | 2257 | extern char **environ; |
2234 | struct stat st; | 2258 | struct stat st; |
2235 | char *argv[10]; | 2259 | char *argv[10]; |
2236 | 2260 | ||
2237 | #ifndef HAVE_LIBPAM /* pam_nologin handles this */ | 2261 | #ifndef HAVE_LIBPAM /* pam_nologin handles this */ |
2238 | /* Check /etc/nologin. */ | 2262 | /* Check /etc/nologin. */ |
2239 | f = fopen("/etc/nologin", "r"); | 2263 | f = fopen("/etc/nologin", "r"); |
2240 | if (f) | 2264 | if (f) { |
2241 | { /* /etc/nologin exists. Print its contents and exit. */ | 2265 | /* /etc/nologin exists. Print its contents and exit. */ |
2242 | while (fgets(buf, sizeof(buf), f)) | 2266 | while (fgets(buf, sizeof(buf), f)) |
2243 | fputs(buf, stderr); | 2267 | fputs(buf, stderr); |
2244 | fclose(f); | 2268 | fclose(f); |
2245 | if (pw->pw_uid != 0) | 2269 | if (pw->pw_uid != 0) |
2246 | exit(254); | 2270 | exit(254); |
2247 | } | 2271 | } |
2248 | #endif /* HAVE_LIBPAM */ | 2272 | #endif /* HAVE_LIBPAM */ |
2249 | 2273 | ||
2250 | #ifdef HAVE_SETLOGIN | 2274 | #ifdef HAVE_SETLOGIN |
2251 | /* Set login name in the kernel. */ | 2275 | /* Set login name in the kernel. */ |
2252 | if (setlogin(pw->pw_name) < 0) | 2276 | if (setlogin(pw->pw_name) < 0) |
2253 | error("setlogin failed: %s", strerror(errno)); | 2277 | error("setlogin failed: %s", strerror(errno)); |
2254 | #endif /* HAVE_SETLOGIN */ | 2278 | #endif /* HAVE_SETLOGIN */ |
2255 | 2279 | ||
2256 | /* Set uid, gid, and groups. */ | 2280 | /* Set uid, gid, and groups. */ |
2257 | /* Login(1) does this as well, and it needs uid 0 for the "-h" switch, | 2281 | /* Login(1) does this as well, and it needs uid 0 for the "-h" |
2258 | so we let login(1) to this for us. */ | 2282 | switch, so we let login(1) to this for us. */ |
2259 | if(!options.use_login) { | 2283 | if (!options.use_login) { |
2260 | if (getuid() == 0 || geteuid() == 0) | 2284 | if (getuid() == 0 || geteuid() == 0) { |
2261 | { | 2285 | if (setgid(pw->pw_gid) < 0) { |
2262 | if (setgid(pw->pw_gid) < 0) | 2286 | perror("setgid"); |
2263 | { | 2287 | exit(1); |
2264 | perror("setgid"); | 2288 | } |
2265 | exit(1); | 2289 | /* Initialize the group list. */ |
2266 | } | 2290 | if (initgroups(pw->pw_name, pw->pw_gid) < 0) { |
2267 | /* Initialize the group list. */ | 2291 | perror("initgroups"); |
2268 | if (initgroups(pw->pw_name, pw->pw_gid) < 0) | 2292 | exit(1); |
2269 | { | 2293 | } |
2270 | perror("initgroups"); | 2294 | endgrent(); |
2271 | exit(1); | 2295 | |
2272 | } | 2296 | /* Permanently switch to the desired uid. */ |
2273 | endgrent(); | 2297 | permanently_set_uid(pw->pw_uid); |
2274 | 2298 | } | |
2275 | /* Permanently switch to the desired uid. */ | 2299 | if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid) |
2276 | permanently_set_uid(pw->pw_uid); | 2300 | fatal("Failed to set uids to %d.", (int) pw->pw_uid); |
2277 | } | 2301 | } |
2278 | 2302 | /* Get the shell from the password data. An empty shell field is | |
2279 | if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid) | 2303 | legal, and means /bin/sh. */ |
2280 | fatal("Failed to set uids to %d.", (int)pw->pw_uid); | 2304 | shell = (pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell; |
2281 | } | ||
2282 | |||
2283 | /* Get the shell from the password data. An empty shell field is legal, | ||
2284 | and means /bin/sh. */ | ||
2285 | shell = (pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell; | ||
2286 | 2305 | ||
2287 | #ifdef AFS | 2306 | #ifdef AFS |
2288 | /* Try to get AFS tokens for the local cell. */ | 2307 | /* Try to get AFS tokens for the local cell. */ |
2289 | if (k_hasafs()) { | 2308 | if (k_hasafs()) { |
2290 | char cell[64]; | 2309 | char cell[64]; |
2291 | 2310 | ||
2292 | if (k_afs_cell_of_file(pw->pw_dir, cell, sizeof(cell)) == 0) | 2311 | if (k_afs_cell_of_file(pw->pw_dir, cell, sizeof(cell)) == 0) |
2293 | krb_afslog(cell, 0); | 2312 | krb_afslog(cell, 0); |
2294 | 2313 | ||
2295 | krb_afslog(0, 0); | 2314 | krb_afslog(0, 0); |
2296 | } | 2315 | } |
2297 | #endif /* AFS */ | 2316 | #endif /* AFS */ |
2298 | 2317 | ||
2299 | /* Initialize the environment. In the first part we allocate space for | 2318 | /* Initialize the environment. In the first part we allocate |
2300 | all environment variables. */ | 2319 | space for all environment variables. */ |
2301 | envsize = 100; | 2320 | envsize = 100; |
2302 | env = xmalloc(envsize * sizeof(char *)); | 2321 | env = xmalloc(envsize * sizeof(char *)); |
2303 | env[0] = NULL; | 2322 | env[0] = NULL; |
2304 | 2323 | ||
2305 | if(!options.use_login) { | 2324 | if (!options.use_login) { |
2306 | /* Set basic environment. */ | 2325 | /* Set basic environment. */ |
2307 | child_set_env(&env, &envsize, "USER", pw->pw_name); | 2326 | child_set_env(&env, &envsize, "USER", pw->pw_name); |
2308 | child_set_env(&env, &envsize, "LOGNAME", pw->pw_name); | 2327 | child_set_env(&env, &envsize, "LOGNAME", pw->pw_name); |
2309 | child_set_env(&env, &envsize, "HOME", pw->pw_dir); | 2328 | child_set_env(&env, &envsize, "HOME", pw->pw_dir); |
2310 | child_set_env(&env, &envsize, "PATH", _PATH_STDPATH); | 2329 | child_set_env(&env, &envsize, "PATH", _PATH_STDPATH); |
2311 | 2330 | ||
2312 | snprintf(buf, sizeof buf, "%.200s/%.50s", | 2331 | snprintf(buf, sizeof buf, "%.200s/%.50s", |
2313 | _PATH_MAILDIR, pw->pw_name); | 2332 | _PATH_MAILDIR, pw->pw_name); |
2314 | child_set_env(&env, &envsize, "MAIL", buf); | 2333 | child_set_env(&env, &envsize, "MAIL", buf); |
2315 | 2334 | ||
2316 | /* Normal systems set SHELL by default. */ | 2335 | /* Normal systems set SHELL by default. */ |
2317 | child_set_env(&env, &envsize, "SHELL", shell); | 2336 | child_set_env(&env, &envsize, "SHELL", shell); |
2318 | } | 2337 | } |
2319 | 2338 | /* Let it inherit timezone if we have one. */ | |
2320 | /* Let it inherit timezone if we have one. */ | 2339 | if (getenv("TZ")) |
2321 | if (getenv("TZ")) | 2340 | child_set_env(&env, &envsize, "TZ", getenv("TZ")); |
2322 | child_set_env(&env, &envsize, "TZ", getenv("TZ")); | 2341 | |
2323 | 2342 | /* Set custom environment options from RSA authentication. */ | |
2324 | /* Set custom environment options from RSA authentication. */ | 2343 | while (custom_environment) { |
2325 | while (custom_environment) | 2344 | struct envstring *ce = custom_environment; |
2326 | { | 2345 | char *s = ce->s; |
2327 | struct envstring *ce = custom_environment; | 2346 | int i; |
2328 | char *s = ce->s; | 2347 | for (i = 0; s[i] != '=' && s[i]; i++); |
2329 | int i; | 2348 | if (s[i] == '=') { |
2330 | for (i = 0; s[i] != '=' && s[i]; i++) | 2349 | s[i] = 0; |
2331 | ; | 2350 | child_set_env(&env, &envsize, s, s + i + 1); |
2332 | if (s[i] == '=') | 2351 | } |
2333 | { | 2352 | custom_environment = ce->next; |
2334 | s[i] = 0; | 2353 | xfree(ce->s); |
2335 | child_set_env(&env, &envsize, s, s + i + 1); | 2354 | xfree(ce); |
2336 | } | 2355 | } |
2337 | custom_environment = ce->next; | ||
2338 | xfree(ce->s); | ||
2339 | xfree(ce); | ||
2340 | } | ||
2341 | 2356 | ||
2342 | /* Set SSH_CLIENT. */ | 2357 | /* Set SSH_CLIENT. */ |
2343 | snprintf(buf, sizeof buf, "%.50s %d %d", | 2358 | snprintf(buf, sizeof buf, "%.50s %d %d", |
2344 | get_remote_ipaddr(), get_remote_port(), options.port); | 2359 | get_remote_ipaddr(), get_remote_port(), options.port); |
2345 | child_set_env(&env, &envsize, "SSH_CLIENT", buf); | 2360 | child_set_env(&env, &envsize, "SSH_CLIENT", buf); |
2346 | 2361 | ||
2347 | /* Set SSH_TTY if we have a pty. */ | 2362 | /* Set SSH_TTY if we have a pty. */ |
2348 | if (ttyname) | 2363 | if (ttyname) |
2349 | child_set_env(&env, &envsize, "SSH_TTY", ttyname); | 2364 | child_set_env(&env, &envsize, "SSH_TTY", ttyname); |
2350 | 2365 | ||
2351 | /* Set TERM if we have a pty. */ | 2366 | /* Set TERM if we have a pty. */ |
2352 | if (term) | 2367 | if (term) |
2353 | child_set_env(&env, &envsize, "TERM", term); | 2368 | child_set_env(&env, &envsize, "TERM", term); |
2354 | 2369 | ||
2355 | /* Set DISPLAY if we have one. */ | 2370 | /* Set DISPLAY if we have one. */ |
2356 | if (display) | 2371 | if (display) |
2357 | child_set_env(&env, &envsize, "DISPLAY", display); | 2372 | child_set_env(&env, &envsize, "DISPLAY", display); |
2358 | 2373 | ||
2359 | #ifdef KRB4 | 2374 | #ifdef KRB4 |
2360 | { | 2375 | { |
2361 | extern char *ticket; | 2376 | extern char *ticket; |
2362 | 2377 | ||
2363 | if (ticket) | 2378 | if (ticket) |
2364 | child_set_env(&env, &envsize, "KRBTKFILE", ticket); | 2379 | child_set_env(&env, &envsize, "KRBTKFILE", ticket); |
2365 | } | 2380 | } |
2366 | #endif /* KRB4 */ | 2381 | #endif /* KRB4 */ |
2367 | 2382 | ||
2368 | #ifdef HAVE_LIBPAM | 2383 | #ifdef HAVE_LIBPAM |
2369 | /* Pull in any environment variables that may have been set by PAM. */ | 2384 | /* Pull in any environment variables that may have been set by PAM. */ |
2370 | { | 2385 | { |
2371 | char *equal_sign, var_name[256], var_val[256]; | 2386 | char *equals, var_name[512], var_val[512]; |
2372 | long this_var; | 2387 | char **pam_env = pam_getenvlist((pam_handle_t *)pamh); |
2373 | char **pam_env = pam_getenvlist((pam_handle_t *)pamh); | 2388 | int i; |
2374 | for(this_var = 0; pam_env && pam_env[this_var]; this_var++) | 2389 | for(i = 0; pam_env && pam_env[i]; i++) { |
2375 | { | 2390 | equals = strstr(pam_env[i], "="); |
2376 | if(strlen(pam_env[this_var]) < (sizeof(var_name) - 1)) | 2391 | if ((strlen(pam_env[i]) < (sizeof(var_name) - 1)) && (equals != NULL)) |
2377 | if((equal_sign = strstr(pam_env[this_var], "=")) != NULL) | 2392 | { |
2378 | { | 2393 | memset(var_name, '\0', sizeof(var_name)); |
2379 | memset(var_name, 0, sizeof(var_name)); | 2394 | memset(var_val, '\0', sizeof(var_val)); |
2380 | memset(var_val, 0, sizeof(var_val)); | 2395 | strncpy(var_name, pam_env[i], equals - pam_env[i]); |
2381 | strncpy(var_name, pam_env[this_var], | 2396 | strcpy(var_val, equals + 1); |
2382 | equal_sign - pam_env[this_var]); | 2397 | child_set_env(&env, &envsize, var_name, var_val); |
2383 | strcpy(var_val, equal_sign + 1); | 2398 | } |
2384 | child_set_env(&env, &envsize, var_name, var_val); | 2399 | } |
2385 | } | 2400 | } |
2386 | } | ||
2387 | } | ||
2388 | #endif /* HAVE_LIBPAM */ | 2401 | #endif /* HAVE_LIBPAM */ |
2389 | 2402 | ||
2390 | /* Set XAUTHORITY to always be a local file. */ | 2403 | /* Set XAUTHORITY to always be a local file. */ |
2391 | if (xauthfile) | 2404 | if (xauthfile) |
2392 | child_set_env(&env, &envsize, "XAUTHORITY", xauthfile); | 2405 | child_set_env(&env, &envsize, "XAUTHORITY", xauthfile); |
2393 | 2406 | ||
2394 | /* Set variable for forwarded authentication connection, if we have one. */ | 2407 | /* Set variable for forwarded authentication connection, if we |
2395 | if (auth_get_socket_name() != NULL) | 2408 | have one. */ |
2396 | child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME, | 2409 | if (auth_get_socket_name() != NULL) |
2397 | auth_get_socket_name()); | 2410 | child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME, |
2398 | 2411 | auth_get_socket_name()); | |
2399 | /* Read $HOME/.ssh/environment. */ | 2412 | |
2400 | if(!options.use_login) { | 2413 | /* Read $HOME/.ssh/environment. */ |
2401 | snprintf(buf, sizeof buf, "%.200s/.ssh/environment", pw->pw_dir); | 2414 | if (!options.use_login) { |
2402 | read_environment_file(&env, &envsize, buf); | 2415 | snprintf(buf, sizeof buf, "%.200s/.ssh/environment", pw->pw_dir); |
2403 | } | 2416 | read_environment_file(&env, &envsize, buf); |
2404 | 2417 | } | |
2405 | /* If debugging, dump the environment to stderr. */ | 2418 | /* If debugging, dump the environment to stderr. */ |
2406 | if (debug_flag) | 2419 | if (debug_flag) { |
2407 | { | 2420 | fprintf(stderr, "Environment:\n"); |
2408 | fprintf(stderr, "Environment:\n"); | 2421 | for (i = 0; env[i]; i++) |
2409 | for (i = 0; env[i]; i++) | 2422 | fprintf(stderr, " %.200s\n", env[i]); |
2410 | fprintf(stderr, " %.200s\n", env[i]); | 2423 | } |
2411 | } | 2424 | /* Close the connection descriptors; note that this is the child, |
2412 | 2425 | and the server will still have the socket open, and it is | |
2413 | /* Close the connection descriptors; note that this is the child, and the | 2426 | important that we do not shutdown it. Note that the |
2414 | server will still have the socket open, and it is important that we | 2427 | descriptors cannot be closed before building the environment, |
2415 | do not shutdown it. Note that the descriptors cannot be closed before | 2428 | as we call get_remote_ipaddr there. */ |
2416 | building the environment, as we call get_remote_ipaddr there. */ | 2429 | if (packet_get_connection_in() == packet_get_connection_out()) |
2417 | if (packet_get_connection_in() == packet_get_connection_out()) | 2430 | close(packet_get_connection_in()); |
2418 | close(packet_get_connection_in()); | 2431 | else { |
2419 | else | 2432 | close(packet_get_connection_in()); |
2420 | { | 2433 | close(packet_get_connection_out()); |
2421 | close(packet_get_connection_in()); | 2434 | } |
2422 | close(packet_get_connection_out()); | 2435 | /* Close all descriptors related to channels. They will still |
2423 | } | 2436 | remain open in the parent. */ |
2424 | /* Close all descriptors related to channels. They will still remain | 2437 | channel_close_all(); |
2425 | open in the parent. */ | 2438 | |
2426 | channel_close_all(); | 2439 | /* Close any extra file descriptors. Note that there may still be |
2427 | 2440 | descriptors left by system functions. They will be closed | |
2428 | /* Close any extra file descriptors. Note that there may still be | 2441 | later. */ |
2429 | descriptors left by system functions. They will be closed later. */ | 2442 | endpwent(); |
2430 | endpwent(); | 2443 | endhostent(); |
2431 | endhostent(); | 2444 | |
2432 | 2445 | /* Close any extra open file descriptors so that we don\'t have | |
2433 | /* Close any extra open file descriptors so that we don\'t have them | 2446 | them hanging around in clients. Note that we want to do this |
2434 | hanging around in clients. Note that we want to do this after | 2447 | after initgroups, because at least on Solaris 2.3 it leaves |
2435 | initgroups, because at least on Solaris 2.3 it leaves file descriptors | 2448 | file descriptors open. */ |
2436 | open. */ | 2449 | for (i = 3; i < 64; i++) |
2437 | for (i = 3; i < 64; i++) | 2450 | close(i); |
2438 | close(i); | 2451 | |
2439 | 2452 | /* Change current directory to the user\'s home directory. */ | |
2440 | /* Change current directory to the user\'s home directory. */ | 2453 | if (chdir(pw->pw_dir) < 0) |
2441 | if (chdir(pw->pw_dir) < 0) | 2454 | fprintf(stderr, "Could not chdir to home directory %s: %s\n", |
2442 | fprintf(stderr, "Could not chdir to home directory %s: %s\n", | 2455 | pw->pw_dir, strerror(errno)); |
2443 | pw->pw_dir, strerror(errno)); | 2456 | |
2444 | 2457 | /* Must take new environment into use so that .ssh/rc, /etc/sshrc | |
2445 | /* Must take new environment into use so that .ssh/rc, /etc/sshrc and | 2458 | and xauth are run in the proper environment. */ |
2446 | xauth are run in the proper environment. */ | 2459 | environ = env; |
2447 | environ = env; | 2460 | |
2448 | 2461 | /* Run $HOME/.ssh/rc, /etc/sshrc, or xauth (whichever is found | |
2449 | /* Run $HOME/.ssh/rc, /etc/sshrc, or xauth (whichever is found first | 2462 | first in this order). */ |
2450 | in this order). */ | 2463 | if (!options.use_login) { |
2451 | if(!options.use_login) { | 2464 | if (stat(SSH_USER_RC, &st) >= 0) { |
2452 | if (stat(SSH_USER_RC, &st) >= 0) | 2465 | if (debug_flag) |
2453 | { | 2466 | fprintf(stderr, "Running /bin/sh %s\n", SSH_USER_RC); |
2454 | if (debug_flag) | 2467 | |
2455 | fprintf(stderr, "Running /bin/sh %s\n", SSH_USER_RC); | 2468 | f = popen("/bin/sh " SSH_USER_RC, "w"); |
2456 | 2469 | if (f) { | |
2457 | f = popen("/bin/sh " SSH_USER_RC, "w"); | 2470 | if (auth_proto != NULL && auth_data != NULL) |
2458 | if (f) | 2471 | fprintf(f, "%s %s\n", auth_proto, auth_data); |
2459 | { | 2472 | pclose(f); |
2460 | if (auth_proto != NULL && auth_data != NULL) | 2473 | } else |
2461 | fprintf(f, "%s %s\n", auth_proto, auth_data); | 2474 | fprintf(stderr, "Could not run %s\n", SSH_USER_RC); |
2462 | pclose(f); | 2475 | } else if (stat(SSH_SYSTEM_RC, &st) >= 0) { |
2463 | } | 2476 | if (debug_flag) |
2464 | else | 2477 | fprintf(stderr, "Running /bin/sh %s\n", SSH_SYSTEM_RC); |
2465 | fprintf(stderr, "Could not run %s\n", SSH_USER_RC); | 2478 | |
2466 | } | 2479 | f = popen("/bin/sh " SSH_SYSTEM_RC, "w"); |
2467 | else | 2480 | if (f) { |
2468 | if (stat(SSH_SYSTEM_RC, &st) >= 0) | 2481 | if (auth_proto != NULL && auth_data != NULL) |
2469 | { | 2482 | fprintf(f, "%s %s\n", auth_proto, auth_data); |
2470 | if (debug_flag) | 2483 | pclose(f); |
2471 | fprintf(stderr, "Running /bin/sh %s\n", SSH_SYSTEM_RC); | 2484 | } else |
2472 | 2485 | fprintf(stderr, "Could not run %s\n", SSH_SYSTEM_RC); | |
2473 | f = popen("/bin/sh " SSH_SYSTEM_RC, "w"); | 2486 | } |
2474 | if (f) | ||
2475 | { | ||
2476 | if (auth_proto != NULL && auth_data != NULL) | ||
2477 | fprintf(f, "%s %s\n", auth_proto, auth_data); | ||
2478 | pclose(f); | ||
2479 | } | ||
2480 | else | ||
2481 | fprintf(stderr, "Could not run %s\n", SSH_SYSTEM_RC); | ||
2482 | } | ||
2483 | #ifdef XAUTH_PATH | 2487 | #ifdef XAUTH_PATH |
2484 | else | 2488 | else { |
2485 | { | 2489 | /* Add authority data to .Xauthority if |
2486 | /* Add authority data to .Xauthority if appropriate. */ | 2490 | appropriate. */ |
2487 | if (auth_proto != NULL && auth_data != NULL) | 2491 | if (auth_proto != NULL && auth_data != NULL) { |
2488 | { | 2492 | if (debug_flag) |
2489 | if (debug_flag) | 2493 | fprintf(stderr, "Running %.100s add %.100s %.100s %.100s\n", |
2490 | fprintf(stderr, "Running %.100s add %.100s %.100s %.100s\n", | 2494 | XAUTH_PATH, display, auth_proto, auth_data); |
2491 | XAUTH_PATH, display, auth_proto, auth_data); | 2495 | |
2492 | 2496 | f = popen(XAUTH_PATH " -q -", "w"); | |
2493 | f = popen(XAUTH_PATH " -q -", "w"); | 2497 | if (f) { |
2494 | if (f) | 2498 | fprintf(f, "add %s %s %s\n", display, auth_proto, auth_data); |
2495 | { | 2499 | fclose(f); |
2496 | fprintf(f, "add %s %s %s\n", display, auth_proto, auth_data); | 2500 | } else |
2497 | fclose(f); | 2501 | fprintf(stderr, "Could not run %s -q -\n", XAUTH_PATH); |
2498 | } | 2502 | } |
2499 | else | 2503 | } |
2500 | fprintf(stderr, "Could not run %s -q -\n", XAUTH_PATH); | ||
2501 | } | ||
2502 | } | ||
2503 | #endif /* XAUTH_PATH */ | 2504 | #endif /* XAUTH_PATH */ |
2504 | 2505 | ||
2505 | /* Get the last component of the shell name. */ | 2506 | /* Get the last component of the shell name. */ |
2506 | cp = strrchr(shell, '/'); | 2507 | cp = strrchr(shell, '/'); |
2507 | if (cp) | 2508 | if (cp) |
2508 | cp++; | 2509 | cp++; |
2509 | else | 2510 | else |
2510 | cp = shell; | 2511 | cp = shell; |
2511 | } | 2512 | } |
2512 | 2513 | /* If we have no command, execute the shell. In this case, the | |
2513 | /* If we have no command, execute the shell. In this case, the shell name | 2514 | shell name to be passed in argv[0] is preceded by '-' to |
2514 | to be passed in argv[0] is preceded by '-' to indicate that this is | 2515 | indicate that this is a login shell. */ |
2515 | a login shell. */ | 2516 | if (!command) { |
2516 | if (!command) | 2517 | if (!options.use_login) { |
2517 | { | 2518 | char buf[256]; |
2518 | if(!options.use_login) { | 2519 | |
2519 | char buf[256]; | 2520 | /* Check for mail if we have a tty and it was |
2520 | 2521 | enabled in server options. */ | |
2521 | /* Check for mail if we have a tty and it was enabled in server options. */ | 2522 | if (ttyname && options.check_mail) { |
2522 | if (ttyname && options.check_mail) { | 2523 | char *mailbox; |
2523 | char *mailbox; | 2524 | struct stat mailstat; |
2524 | struct stat mailstat; | 2525 | mailbox = getenv("MAIL"); |
2525 | mailbox = getenv("MAIL"); | 2526 | if (mailbox != NULL) { |
2526 | if(mailbox != NULL) { | 2527 | if (stat(mailbox, &mailstat) != 0 || mailstat.st_size == 0) |
2527 | if(stat(mailbox, &mailstat) != 0 || mailstat.st_size == 0) { | 2528 | printf("No mail.\n"); |
2528 | printf("No mail.\n"); | 2529 | else if (mailstat.st_mtime < mailstat.st_atime) |
2529 | } else if(mailstat.st_mtime < mailstat.st_atime) { | 2530 | printf("You have mail.\n"); |
2530 | printf("You have mail.\n"); | 2531 | else |
2531 | } else { | 2532 | printf("You have new mail.\n"); |
2532 | printf("You have new mail.\n"); | 2533 | } |
2533 | } | 2534 | } |
2534 | } | 2535 | /* Start the shell. Set initial character to '-'. */ |
2535 | } | 2536 | buf[0] = '-'; |
2536 | /* Start the shell. Set initial character to '-'. */ | 2537 | strncpy(buf + 1, cp, sizeof(buf) - 1); |
2537 | buf[0] = '-'; | 2538 | buf[sizeof(buf) - 1] = 0; |
2538 | strncpy(buf + 1, cp, sizeof(buf) - 1); | 2539 | |
2539 | buf[sizeof(buf) - 1] = 0; | 2540 | /* Execute the shell. */ |
2540 | /* Execute the shell. */ | 2541 | argv[0] = buf; |
2541 | argv[0] = buf; | 2542 | argv[1] = NULL; |
2542 | argv[1] = NULL; | 2543 | execve(shell, argv, env); |
2543 | execve(shell, argv, env); | 2544 | |
2544 | /* Executing the shell failed. */ | 2545 | /* Executing the shell failed. */ |
2545 | perror(shell); | 2546 | perror(shell); |
2546 | exit(1); | 2547 | exit(1); |
2547 | 2548 | ||
2548 | } else { | 2549 | } else { |
2549 | /* Launch login(1). */ | 2550 | /* Launch login(1). */ |
2550 | 2551 | ||
2551 | execl(LOGIN_PROGRAM, "login", "-h", get_remote_ipaddr(), "-p", "-f", "--", pw->pw_name, NULL); | 2552 | execl(LOGIN_PROGRAM, "login", "-h", get_remote_ipaddr(), |
2552 | 2553 | "-p", "-f", "--", pw->pw_name, NULL); | |
2553 | /* Login couldn't be executed, die. */ | 2554 | |
2554 | 2555 | /* Login couldn't be executed, die. */ | |
2555 | perror("login"); | 2556 | |
2556 | exit(1); | 2557 | perror("login"); |
2557 | } | 2558 | exit(1); |
2558 | } | 2559 | } |
2559 | 2560 | } | |
2560 | /* Execute the command using the user's shell. This uses the -c option | 2561 | /* Execute the command using the user's shell. This uses the -c |
2561 | to execute the command. */ | 2562 | option to execute the command. */ |
2562 | argv[0] = (char *)cp; | 2563 | argv[0] = (char *) cp; |
2563 | argv[1] = "-c"; | 2564 | argv[1] = "-c"; |
2564 | argv[2] = (char *)command; | 2565 | argv[2] = (char *) command; |
2565 | argv[3] = NULL; | 2566 | argv[3] = NULL; |
2566 | execve(shell, argv, env); | 2567 | execve(shell, argv, env); |
2567 | perror(shell); | 2568 | perror(shell); |
2568 | exit(1); | 2569 | exit(1); |
2569 | } | 2570 | } |
diff --git a/tildexpand.c b/tildexpand.c index e4b57091e..92422ee3f 100644 --- a/tildexpand.c +++ b/tildexpand.c | |||
@@ -1,70 +1,62 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | |
3 | tildexpand.c | 3 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
4 | 4 | * All rights reserved | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Created: Wed Jul 12 01:07:36 1995 ylo |
6 | 6 | */ | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
8 | All rights reserved | ||
9 | |||
10 | Created: Wed Jul 12 01:07:36 1995 ylo | ||
11 | |||
12 | */ | ||
13 | 7 | ||
14 | #include "includes.h" | 8 | #include "includes.h" |
15 | RCSID("$Id: tildexpand.c,v 1.1 1999/10/27 03:42:46 damien Exp $"); | 9 | RCSID("$Id: tildexpand.c,v 1.2 1999/11/24 13:26:23 damien Exp $"); |
16 | 10 | ||
17 | #include "xmalloc.h" | 11 | #include "xmalloc.h" |
18 | #include "ssh.h" | 12 | #include "ssh.h" |
19 | 13 | ||
20 | /* Expands tildes in the file name. Returns data allocated by xmalloc. | 14 | /* |
21 | Warning: this calls getpw*. */ | 15 | * Expands tildes in the file name. Returns data allocated by xmalloc. |
22 | 16 | * Warning: this calls getpw*. | |
23 | char *tilde_expand_filename(const char *filename, uid_t my_uid) | 17 | */ |
18 | char * | ||
19 | tilde_expand_filename(const char *filename, uid_t my_uid) | ||
24 | { | 20 | { |
25 | const char *cp; | 21 | const char *cp; |
26 | unsigned int userlen; | 22 | unsigned int userlen; |
27 | char *expanded; | 23 | char *expanded; |
28 | struct passwd *pw; | 24 | struct passwd *pw; |
29 | char user[100]; | 25 | char user[100]; |
30 | 26 | ||
31 | /* Return immediately if no tilde. */ | 27 | /* Return immediately if no tilde. */ |
32 | if (filename[0] != '~') | 28 | if (filename[0] != '~') |
33 | return xstrdup(filename); | 29 | return xstrdup(filename); |
34 | 30 | ||
35 | /* Skip the tilde. */ | 31 | /* Skip the tilde. */ |
36 | filename++; | 32 | filename++; |
37 | 33 | ||
38 | /* Find where the username ends. */ | 34 | /* Find where the username ends. */ |
39 | cp = strchr(filename, '/'); | 35 | cp = strchr(filename, '/'); |
40 | if (cp) | 36 | if (cp) |
41 | userlen = cp - filename; /* Have something after username. */ | 37 | userlen = cp - filename; /* Something after username. */ |
42 | else | 38 | else |
43 | userlen = strlen(filename); /* Nothign after username. */ | 39 | userlen = strlen(filename); /* Nothing after username. */ |
44 | if (userlen == 0) | 40 | if (userlen == 0) |
45 | pw = getpwuid(my_uid); /* Own home directory. */ | 41 | pw = getpwuid(my_uid); /* Own home directory. */ |
46 | else | 42 | else { |
47 | { | 43 | /* Tilde refers to someone elses home directory. */ |
48 | /* Tilde refers to someone elses home directory. */ | 44 | if (userlen > sizeof(user) - 1) |
49 | if (userlen > sizeof(user) - 1) | 45 | fatal("User name after tilde too long."); |
50 | fatal("User name after tilde too long."); | 46 | memcpy(user, filename, userlen); |
51 | memcpy(user, filename, userlen); | 47 | user[userlen] = 0; |
52 | user[userlen] = 0; | 48 | pw = getpwnam(user); |
53 | pw = getpwnam(user); | 49 | } |
54 | } | 50 | /* Check that we found the user. */ |
55 | 51 | if (!pw) | |
56 | /* Check that we found the user. */ | 52 | fatal("Unknown user %100s.", user); |
57 | if (!pw) | 53 | |
58 | fatal("Unknown user %100s.", user); | 54 | /* If referring to someones home directory, return it now. */ |
59 | 55 | if (!cp) { /* Only home directory specified */ | |
60 | /* If referring to someones home directory, return it now. */ | 56 | return xstrdup(pw->pw_dir); |
61 | if (!cp) | 57 | } |
62 | { /* Only home directory specified */ | 58 | /* Build a path combining the specified directory and path. */ |
63 | return xstrdup(pw->pw_dir); | 59 | expanded = xmalloc(strlen(pw->pw_dir) + strlen(cp + 1) + 2); |
64 | } | 60 | sprintf(expanded, "%s/%s", pw->pw_dir, cp + 1); |
65 | 61 | return expanded; | |
66 | /* Build a path combining the specified directory and path. */ | ||
67 | expanded = xmalloc(strlen(pw->pw_dir) + strlen(cp + 1) + 2); | ||
68 | sprintf(expanded, "%s/%s", pw->pw_dir, cp + 1); | ||
69 | return expanded; | ||
70 | } | 62 | } |
diff --git a/ttymodes.c b/ttymodes.c index cbb7f2f64..6810ce26c 100644 --- a/ttymodes.c +++ b/ttymodes.c | |||
@@ -1,233 +1,229 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | |
3 | ttymodes.c | 3 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
4 | 4 | * All rights reserved | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Created: Tue Mar 21 15:59:15 1995 ylo |
6 | 6 | * Encoding and decoding of terminal modes in a portable way. | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * Much of the format is defined in ttymodes.h; it is included multiple times |
8 | All rights reserved | 8 | * into this file with the appropriate macro definitions to generate the |
9 | 9 | * suitable code. | |
10 | Created: Tue Mar 21 15:59:15 1995 ylo | 10 | */ |
11 | |||
12 | Encoding and decoding of terminal modes in a portable way. | ||
13 | Much of the format is defined in ttymodes.h; it is included multiple times | ||
14 | into this file with the appropriate macro definitions to generate the | ||
15 | suitable code. | ||
16 | |||
17 | */ | ||
18 | 11 | ||
19 | #include "includes.h" | 12 | #include "includes.h" |
20 | RCSID("$Id: ttymodes.c,v 1.1 1999/10/27 03:42:46 damien Exp $"); | 13 | RCSID("$Id: ttymodes.c,v 1.2 1999/11/24 13:26:23 damien Exp $"); |
21 | 14 | ||
22 | #include "packet.h" | 15 | #include "packet.h" |
23 | #include "ssh.h" | 16 | #include "ssh.h" |
24 | 17 | ||
25 | #define TTY_OP_END 0 | 18 | #define TTY_OP_END 0 |
26 | #define TTY_OP_ISPEED 192 /* int follows */ | 19 | #define TTY_OP_ISPEED 192 /* int follows */ |
27 | #define TTY_OP_OSPEED 193 /* int follows */ | 20 | #define TTY_OP_OSPEED 193 /* int follows */ |
28 | 21 | ||
29 | /* Converts POSIX speed_t to a baud rate. The values of the constants | 22 | /* |
30 | for speed_t are not themselves portable. */ | 23 | * Converts POSIX speed_t to a baud rate. The values of the |
31 | 24 | * constants for speed_t are not themselves portable. | |
32 | static int speed_to_baud(speed_t speed) | 25 | */ |
26 | static int | ||
27 | speed_to_baud(speed_t speed) | ||
33 | { | 28 | { |
34 | switch (speed) | 29 | switch (speed) { |
35 | { | 30 | case B0: |
36 | case B0: | 31 | return 0; |
37 | return 0; | 32 | case B50: |
38 | case B50: | 33 | return 50; |
39 | return 50; | 34 | case B75: |
40 | case B75: | 35 | return 75; |
41 | return 75; | 36 | case B110: |
42 | case B110: | 37 | return 110; |
43 | return 110; | 38 | case B134: |
44 | case B134: | 39 | return 134; |
45 | return 134; | 40 | case B150: |
46 | case B150: | 41 | return 150; |
47 | return 150; | 42 | case B200: |
48 | case B200: | 43 | return 200; |
49 | return 200; | 44 | case B300: |
50 | case B300: | 45 | return 300; |
51 | return 300; | 46 | case B600: |
52 | case B600: | 47 | return 600; |
53 | return 600; | 48 | case B1200: |
54 | case B1200: | 49 | return 1200; |
55 | return 1200; | 50 | case B1800: |
56 | case B1800: | 51 | return 1800; |
57 | return 1800; | 52 | case B2400: |
58 | case B2400: | 53 | return 2400; |
59 | return 2400; | 54 | case B4800: |
60 | case B4800: | 55 | return 4800; |
61 | return 4800; | 56 | case B9600: |
62 | case B9600: | 57 | return 9600; |
63 | return 9600; | ||
64 | 58 | ||
65 | #ifdef B19200 | 59 | #ifdef B19200 |
66 | case B19200: | 60 | case B19200: |
67 | return 19200; | 61 | return 19200; |
68 | #else /* B19200 */ | 62 | #else /* B19200 */ |
69 | #ifdef EXTA | 63 | #ifdef EXTA |
70 | case EXTA: | 64 | case EXTA: |
71 | return 19200; | 65 | return 19200; |
72 | #endif /* EXTA */ | 66 | #endif /* EXTA */ |
73 | #endif /* B19200 */ | 67 | #endif /* B19200 */ |
74 | 68 | ||
75 | #ifdef B38400 | 69 | #ifdef B38400 |
76 | case B38400: | 70 | case B38400: |
77 | return 38400; | 71 | return 38400; |
78 | #else /* B38400 */ | 72 | #else /* B38400 */ |
79 | #ifdef EXTB | 73 | #ifdef EXTB |
80 | case EXTB: | 74 | case EXTB: |
81 | return 38400; | 75 | return 38400; |
82 | #endif /* EXTB */ | 76 | #endif /* EXTB */ |
83 | #endif /* B38400 */ | 77 | #endif /* B38400 */ |
84 | 78 | ||
85 | #ifdef B7200 | 79 | #ifdef B7200 |
86 | case B7200: | 80 | case B7200: |
87 | return 7200; | 81 | return 7200; |
88 | #endif /* B7200 */ | 82 | #endif /* B7200 */ |
89 | #ifdef B14400 | 83 | #ifdef B14400 |
90 | case B14400: | 84 | case B14400: |
91 | return 14400; | 85 | return 14400; |
92 | #endif /* B14400 */ | 86 | #endif /* B14400 */ |
93 | #ifdef B28800 | 87 | #ifdef B28800 |
94 | case B28800: | 88 | case B28800: |
95 | return 28800; | 89 | return 28800; |
96 | #endif /* B28800 */ | 90 | #endif /* B28800 */ |
97 | #ifdef B57600 | 91 | #ifdef B57600 |
98 | case B57600: | 92 | case B57600: |
99 | return 57600; | 93 | return 57600; |
100 | #endif /* B57600 */ | 94 | #endif /* B57600 */ |
101 | #ifdef B76800 | 95 | #ifdef B76800 |
102 | case B76800: | 96 | case B76800: |
103 | return 76800; | 97 | return 76800; |
104 | #endif /* B76800 */ | 98 | #endif /* B76800 */ |
105 | #ifdef B115200 | 99 | #ifdef B115200 |
106 | case B115200: | 100 | case B115200: |
107 | return 115200; | 101 | return 115200; |
108 | #endif /* B115200 */ | 102 | #endif /* B115200 */ |
109 | #ifdef B230400 | 103 | #ifdef B230400 |
110 | case B230400: | 104 | case B230400: |
111 | return 230400; | 105 | return 230400; |
112 | #endif /* B230400 */ | 106 | #endif /* B230400 */ |
113 | default: | 107 | default: |
114 | return 9600; | 108 | return 9600; |
115 | } | 109 | } |
116 | } | 110 | } |
117 | 111 | ||
118 | /* Converts a numeric baud rate to a POSIX speed_t. */ | 112 | /* |
119 | 113 | * Converts a numeric baud rate to a POSIX speed_t. | |
120 | static speed_t baud_to_speed(int baud) | 114 | */ |
115 | static speed_t | ||
116 | baud_to_speed(int baud) | ||
121 | { | 117 | { |
122 | switch (baud) | 118 | switch (baud) { |
123 | { | 119 | case 0: |
124 | case 0: | 120 | return B0; |
125 | return B0; | 121 | case 50: |
126 | case 50: | 122 | return B50; |
127 | return B50; | 123 | case 75: |
128 | case 75: | 124 | return B75; |
129 | return B75; | 125 | case 110: |
130 | case 110: | 126 | return B110; |
131 | return B110; | 127 | case 134: |
132 | case 134: | 128 | return B134; |
133 | return B134; | 129 | case 150: |
134 | case 150: | 130 | return B150; |
135 | return B150; | 131 | case 200: |
136 | case 200: | 132 | return B200; |
137 | return B200; | 133 | case 300: |
138 | case 300: | 134 | return B300; |
139 | return B300; | 135 | case 600: |
140 | case 600: | 136 | return B600; |
141 | return B600; | 137 | case 1200: |
142 | case 1200: | 138 | return B1200; |
143 | return B1200; | 139 | case 1800: |
144 | case 1800: | 140 | return B1800; |
145 | return B1800; | 141 | case 2400: |
146 | case 2400: | 142 | return B2400; |
147 | return B2400; | 143 | case 4800: |
148 | case 4800: | 144 | return B4800; |
149 | return B4800; | 145 | case 9600: |
150 | case 9600: | 146 | return B9600; |
151 | return B9600; | ||
152 | 147 | ||
153 | #ifdef B19200 | 148 | #ifdef B19200 |
154 | case 19200: | 149 | case 19200: |
155 | return B19200; | 150 | return B19200; |
156 | #else /* B19200 */ | 151 | #else /* B19200 */ |
157 | #ifdef EXTA | 152 | #ifdef EXTA |
158 | case 19200: | 153 | case 19200: |
159 | return EXTA; | 154 | return EXTA; |
160 | #endif /* EXTA */ | 155 | #endif /* EXTA */ |
161 | #endif /* B19200 */ | 156 | #endif /* B19200 */ |
162 | 157 | ||
163 | #ifdef B38400 | 158 | #ifdef B38400 |
164 | case 38400: | 159 | case 38400: |
165 | return B38400; | 160 | return B38400; |
166 | #else /* B38400 */ | 161 | #else /* B38400 */ |
167 | #ifdef EXTB | 162 | #ifdef EXTB |
168 | case 38400: | 163 | case 38400: |
169 | return EXTB; | 164 | return EXTB; |
170 | #endif /* EXTB */ | 165 | #endif /* EXTB */ |
171 | #endif /* B38400 */ | 166 | #endif /* B38400 */ |
172 | 167 | ||
173 | #ifdef B7200 | 168 | #ifdef B7200 |
174 | case 7200: | 169 | case 7200: |
175 | return B7200; | 170 | return B7200; |
176 | #endif /* B7200 */ | 171 | #endif /* B7200 */ |
177 | #ifdef B14400 | 172 | #ifdef B14400 |
178 | case 14400: | 173 | case 14400: |
179 | return B14400; | 174 | return B14400; |
180 | #endif /* B14400 */ | 175 | #endif /* B14400 */ |
181 | #ifdef B28800 | 176 | #ifdef B28800 |
182 | case 28800: | 177 | case 28800: |
183 | return B28800; | 178 | return B28800; |
184 | #endif /* B28800 */ | 179 | #endif /* B28800 */ |
185 | #ifdef B57600 | 180 | #ifdef B57600 |
186 | case 57600: | 181 | case 57600: |
187 | return B57600; | 182 | return B57600; |
188 | #endif /* B57600 */ | 183 | #endif /* B57600 */ |
189 | #ifdef B76800 | 184 | #ifdef B76800 |
190 | case 76800: | 185 | case 76800: |
191 | return B76800; | 186 | return B76800; |
192 | #endif /* B76800 */ | 187 | #endif /* B76800 */ |
193 | #ifdef B115200 | 188 | #ifdef B115200 |
194 | case 115200: | 189 | case 115200: |
195 | return B115200; | 190 | return B115200; |
196 | #endif /* B115200 */ | 191 | #endif /* B115200 */ |
197 | #ifdef B230400 | 192 | #ifdef B230400 |
198 | case 230400: | 193 | case 230400: |
199 | return B230400; | 194 | return B230400; |
200 | #endif /* B230400 */ | 195 | #endif /* B230400 */ |
201 | default: | 196 | default: |
202 | return B9600; | 197 | return B9600; |
203 | } | 198 | } |
204 | } | 199 | } |
205 | 200 | ||
206 | /* Encodes terminal modes for the terminal referenced by fd in a portable | 201 | /* |
207 | manner, and appends the modes to a packet being constructed. */ | 202 | * Encodes terminal modes for the terminal referenced by fd |
208 | 203 | * in a portable manner, and appends the modes to a packet | |
209 | void tty_make_modes(int fd) | 204 | * being constructed. |
205 | */ | ||
206 | void | ||
207 | tty_make_modes(int fd) | ||
210 | { | 208 | { |
211 | struct termios tio; | 209 | struct termios tio; |
212 | int baud; | 210 | int baud; |
213 | 211 | ||
214 | /* Get the modes. */ | 212 | /* Get the modes. */ |
215 | if (tcgetattr(fd, &tio) < 0) | 213 | if (tcgetattr(fd, &tio) < 0) { |
216 | { | 214 | packet_put_char(TTY_OP_END); |
217 | packet_put_char(TTY_OP_END); | 215 | log("tcgetattr: %.100s", strerror(errno)); |
218 | log("tcgetattr: %.100s", strerror(errno)); | 216 | return; |
219 | return; | 217 | } |
220 | } | 218 | /* Store input and output baud rates. */ |
221 | 219 | baud = speed_to_baud(cfgetospeed(&tio)); | |
222 | /* Store input and output baud rates. */ | 220 | packet_put_char(TTY_OP_OSPEED); |
223 | baud = speed_to_baud(cfgetospeed(&tio)); | 221 | packet_put_int(baud); |
224 | packet_put_char(TTY_OP_OSPEED); | 222 | baud = speed_to_baud(cfgetispeed(&tio)); |
225 | packet_put_int(baud); | 223 | packet_put_char(TTY_OP_ISPEED); |
226 | baud = speed_to_baud(cfgetispeed(&tio)); | 224 | packet_put_int(baud); |
227 | packet_put_char(TTY_OP_ISPEED); | 225 | |
228 | packet_put_int(baud); | 226 | /* Store values of mode flags. */ |
229 | |||
230 | /* Store values of mode flags. */ | ||
231 | #define TTYCHAR(NAME, OP) \ | 227 | #define TTYCHAR(NAME, OP) \ |
232 | packet_put_char(OP); packet_put_char(tio.c_cc[NAME]); | 228 | packet_put_char(OP); packet_put_char(tio.c_cc[NAME]); |
233 | #define TTYMODE(NAME, FIELD, OP) \ | 229 | #define TTYMODE(NAME, FIELD, OP) \ |
@@ -244,48 +240,50 @@ void tty_make_modes(int fd) | |||
244 | #undef SGTTYMODE | 240 | #undef SGTTYMODE |
245 | #undef SGTTYMODEN | 241 | #undef SGTTYMODEN |
246 | 242 | ||
247 | /* Mark end of mode data. */ | 243 | /* Mark end of mode data. */ |
248 | packet_put_char(TTY_OP_END); | 244 | packet_put_char(TTY_OP_END); |
249 | } | 245 | } |
250 | 246 | ||
251 | /* Decodes terminal modes for the terminal referenced by fd in a portable | 247 | /* |
252 | manner from a packet being read. */ | 248 | * Decodes terminal modes for the terminal referenced by fd in a portable |
253 | 249 | * manner from a packet being read. | |
254 | void tty_parse_modes(int fd, int *n_bytes_ptr) | 250 | */ |
251 | void | ||
252 | tty_parse_modes(int fd, int *n_bytes_ptr) | ||
255 | { | 253 | { |
256 | struct termios tio; | 254 | struct termios tio; |
257 | int opcode, baud; | 255 | int opcode, baud; |
258 | int n_bytes = 0; | 256 | int n_bytes = 0; |
259 | int failure = 0; | 257 | int failure = 0; |
260 | 258 | ||
261 | /* Get old attributes for the terminal. We will modify these flags. | 259 | /* |
262 | I am hoping that if there are any machine-specific modes, they will | 260 | * Get old attributes for the terminal. We will modify these |
263 | initially have reasonable values. */ | 261 | * flags. I am hoping that if there are any machine-specific |
264 | if (tcgetattr(fd, &tio) < 0) | 262 | * modes, they will initially have reasonable values. |
265 | failure = -1; | 263 | */ |
266 | 264 | if (tcgetattr(fd, &tio) < 0) | |
267 | for (;;) | 265 | failure = -1; |
268 | { | 266 | |
269 | n_bytes += 1; | 267 | for (;;) { |
270 | opcode = packet_get_char(); | 268 | n_bytes += 1; |
271 | switch (opcode) | 269 | opcode = packet_get_char(); |
272 | { | 270 | switch (opcode) { |
273 | case TTY_OP_END: | 271 | case TTY_OP_END: |
274 | goto set; | 272 | goto set; |
275 | 273 | ||
276 | case TTY_OP_ISPEED: | 274 | case TTY_OP_ISPEED: |
277 | n_bytes += 4; | 275 | n_bytes += 4; |
278 | baud = packet_get_int(); | 276 | baud = packet_get_int(); |
279 | if (failure != -1 && cfsetispeed(&tio, baud_to_speed(baud)) < 0) | 277 | if (failure != -1 && cfsetispeed(&tio, baud_to_speed(baud)) < 0) |
280 | error("cfsetispeed failed for %d", baud); | 278 | error("cfsetispeed failed for %d", baud); |
281 | break; | 279 | break; |
282 | 280 | ||
283 | case TTY_OP_OSPEED: | 281 | case TTY_OP_OSPEED: |
284 | n_bytes += 4; | 282 | n_bytes += 4; |
285 | baud = packet_get_int(); | 283 | baud = packet_get_int(); |
286 | if (failure != -1 && cfsetospeed(&tio, baud_to_speed(baud)) < 0) | 284 | if (failure != -1 && cfsetospeed(&tio, baud_to_speed(baud)) < 0) |
287 | error("cfsetospeed failed for %d", baud); | 285 | error("cfsetospeed failed for %d", baud); |
288 | break; | 286 | break; |
289 | 287 | ||
290 | #define TTYCHAR(NAME, OP) \ | 288 | #define TTYCHAR(NAME, OP) \ |
291 | case OP: \ | 289 | case OP: \ |
@@ -312,48 +310,51 @@ void tty_parse_modes(int fd, int *n_bytes_ptr) | |||
312 | #undef SGTTYMODE | 310 | #undef SGTTYMODE |
313 | #undef SGTTYMODEN | 311 | #undef SGTTYMODEN |
314 | 312 | ||
315 | default: | 313 | default: |
316 | debug("Ignoring unsupported tty mode opcode %d (0x%x)", | 314 | debug("Ignoring unsupported tty mode opcode %d (0x%x)", |
317 | opcode, opcode); | 315 | opcode, opcode); |
318 | /* Opcodes 0 to 127 are defined to have a one-byte argument. */ | 316 | /* |
319 | if (opcode >= 0 && opcode < 128) | 317 | * Opcodes 0 to 127 are defined to have |
320 | { | 318 | * a one-byte argument. |
321 | n_bytes += 1; | 319 | */ |
322 | (void)packet_get_char(); | 320 | if (opcode >= 0 && opcode < 128) { |
323 | break; | 321 | n_bytes += 1; |
324 | } | 322 | (void) packet_get_char(); |
325 | else | 323 | break; |
326 | { | 324 | } else { |
327 | /* Opcodes 128 to 159 are defined to have an integer argument. */ | 325 | /* |
328 | if (opcode >= 128 && opcode < 160) | 326 | * Opcodes 128 to 159 are defined to have |
329 | { | 327 | * an integer argument. |
330 | n_bytes += 4; | 328 | */ |
331 | (void)packet_get_int(); | 329 | if (opcode >= 128 && opcode < 160) { |
332 | break; | 330 | n_bytes += 4; |
331 | (void) packet_get_int(); | ||
332 | break; | ||
333 | } | ||
334 | } | ||
335 | /* | ||
336 | * It is a truly undefined opcode (160 to 255). | ||
337 | * We have no idea about its arguments. So we | ||
338 | * must stop parsing. Note that some data may be | ||
339 | * left in the packet; hopefully there is nothing | ||
340 | * more coming after the mode data. | ||
341 | */ | ||
342 | log("parse_tty_modes: unknown opcode %d", opcode); | ||
343 | packet_integrity_check(0, 1, SSH_CMSG_REQUEST_PTY); | ||
344 | goto set; | ||
333 | } | 345 | } |
334 | } | ||
335 | /* It is a truly undefined opcode (160 to 255). We have no idea | ||
336 | about its arguments. So we must stop parsing. Note that some | ||
337 | data may be left in the packet; hopefully there is nothing more | ||
338 | coming after the mode data. */ | ||
339 | log("parse_tty_modes: unknown opcode %d", opcode); | ||
340 | packet_integrity_check(0, 1, SSH_CMSG_REQUEST_PTY); | ||
341 | goto set; | ||
342 | } | 346 | } |
343 | } | ||
344 | 347 | ||
345 | set: | 348 | set: |
346 | if (*n_bytes_ptr != n_bytes) | 349 | if (*n_bytes_ptr != n_bytes) { |
347 | { | 350 | *n_bytes_ptr = n_bytes; |
348 | *n_bytes_ptr = n_bytes; | 351 | return; /* Don't process bytes passed */ |
349 | return; /* Don't process bytes passed */ | 352 | } |
350 | } | 353 | if (failure == -1) |
354 | return; /* Packet parsed ok but tty stuff failed */ | ||
351 | 355 | ||
352 | if (failure == -1) | 356 | /* Set the new modes for the terminal. */ |
353 | return; /* Packet parsed ok but tty stuff failed */ | 357 | if (tcsetattr(fd, TCSANOW, &tio) < 0) |
354 | 358 | log("Setting tty modes failed: %.100s", strerror(errno)); | |
355 | /* Set the new modes for the terminal. */ | 359 | return; |
356 | if (tcsetattr(fd, TCSANOW, &tio) < 0) | ||
357 | log("Setting tty modes failed: %.100s", strerror(errno)); | ||
358 | return; | ||
359 | } | 360 | } |
diff --git a/ttymodes.h b/ttymodes.h index 2a33eb78d..e9301f281 100644 --- a/ttymodes.h +++ b/ttymodes.h | |||
@@ -1,138 +1,140 @@ | |||
1 | /* | 1 | /* |
2 | * | ||
3 | * ttymodes.h | ||
4 | * | ||
5 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | * SGTTY stuff contributed by Janne Snabb <snabb@niksula.hut.fi> | ||
7 | * | ||
8 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
9 | * All rights reserved | ||
10 | * | ||
11 | * Created: Tue Mar 21 15:42:09 1995 ylo | ||
12 | * | ||
13 | */ | ||
2 | 14 | ||
3 | ttymodes.h | 15 | /* RCSID("$Id: ttymodes.h,v 1.2 1999/11/24 13:26:23 damien Exp $"); */ |
4 | |||
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | ||
6 | SGTTY stuff contributed by Janne Snabb <snabb@niksula.hut.fi> | ||
7 | |||
8 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | ||
9 | All rights reserved | ||
10 | |||
11 | Created: Tue Mar 21 15:42:09 1995 ylo | ||
12 | |||
13 | */ | ||
14 | |||
15 | /* RCSID("$Id: ttymodes.h,v 1.1 1999/10/27 03:42:46 damien Exp $"); */ | ||
16 | 16 | ||
17 | /* The tty mode description is a stream of bytes. The stream consists of | 17 | /* The tty mode description is a stream of bytes. The stream consists of |
18 | opcode-arguments pairs. It is terminated by opcode TTY_OP_END (0). | 18 | * opcode-arguments pairs. It is terminated by opcode TTY_OP_END (0). |
19 | Opcodes 1-127 have one-byte arguments. Opcodes 128-159 have integer | 19 | * Opcodes 1-127 have one-byte arguments. Opcodes 128-159 have integer |
20 | arguments. Opcodes 160-255 are not yet defined, and cause parsing to | 20 | * arguments. Opcodes 160-255 are not yet defined, and cause parsing to |
21 | stop (they should only be used after any other data). | 21 | * stop (they should only be used after any other data). |
22 | * | ||
23 | * The client puts in the stream any modes it knows about, and the | ||
24 | * server ignores any modes it does not know about. This allows some degree | ||
25 | * of machine-independence, at least between systems that use a posix-like | ||
26 | * tty interface. The protocol can support other systems as well, but might | ||
27 | * require reimplementing as mode names would likely be different. | ||
28 | */ | ||
22 | 29 | ||
23 | The client puts in the stream any modes it knows about, and the | 30 | /* |
24 | server ignores any modes it does not know about. This allows some degree | 31 | * Some constants and prototypes are defined in packet.h; this file |
25 | of machine-independence, at least between systems that use a posix-like | 32 | * is only intended for including from ttymodes.h. |
26 | tty interface. The protocol can support other systems as well, but might | 33 | */ |
27 | require reimplementing as mode names would likely be different. */ | ||
28 | |||
29 | /* Some constants and prototypes are defined in packet.h; this file | ||
30 | is only intended for including from ttymodes.h. */ | ||
31 | 34 | ||
32 | /* termios macro */ /* sgtty macro */ | 35 | /* termios macro *//* sgtty macro */ |
33 | /* name, op */ | 36 | /* name, op */ |
34 | TTYCHAR(VINTR, 1) SGTTYCHAR(tiotc.t_intrc, 1) | 37 | TTYCHAR(VINTR, 1) SGTTYCHAR(tiotc.t_intrc, 1) |
35 | TTYCHAR(VQUIT, 2) SGTTYCHAR(tiotc.t_quitc, 2) | 38 | TTYCHAR(VQUIT, 2) SGTTYCHAR(tiotc.t_quitc, 2) |
36 | TTYCHAR(VERASE, 3) SGTTYCHAR(tio.sg_erase, 3) | 39 | TTYCHAR(VERASE, 3) SGTTYCHAR(tio.sg_erase, 3) |
37 | #if defined(VKILL) | 40 | #if defined(VKILL) |
38 | TTYCHAR(VKILL, 4) SGTTYCHAR(tio.sg_kill, 4) | 41 | TTYCHAR(VKILL, 4) SGTTYCHAR(tio.sg_kill, 4) |
39 | #endif /* VKILL */ | 42 | #endif /* VKILL */ |
40 | TTYCHAR(VEOF, 5) SGTTYCHAR(tiotc.t_eofc, 5) | 43 | TTYCHAR(VEOF, 5) SGTTYCHAR(tiotc.t_eofc, 5) |
41 | #if defined(VEOL) | 44 | #if defined(VEOL) |
42 | TTYCHAR(VEOL, 6) SGTTYCHAR(tiotc.t_brkc, 6) | 45 | TTYCHAR(VEOL, 6) SGTTYCHAR(tiotc.t_brkc, 6) |
43 | #endif /* VEOL */ | 46 | #endif /* VEOL */ |
44 | #ifdef VEOL2 /* n/a */ | 47 | #ifdef VEOL2 /* n/a */ |
45 | TTYCHAR(VEOL2, 7) | 48 | TTYCHAR(VEOL2, 7) |
46 | #endif /* VEOL2 */ | 49 | #endif /* VEOL2 */ |
47 | TTYCHAR(VSTART, 8) SGTTYCHAR(tiotc.t_startc, 8) | 50 | TTYCHAR(VSTART, 8) SGTTYCHAR(tiotc.t_startc, 8) |
48 | TTYCHAR(VSTOP, 9) SGTTYCHAR(tiotc.t_stopc, 9) | 51 | TTYCHAR(VSTOP, 9) SGTTYCHAR(tiotc.t_stopc, 9) |
49 | #if defined(VSUSP) | 52 | #if defined(VSUSP) |
50 | TTYCHAR(VSUSP, 10) SGTTYCHAR(tioltc.t_suspc, 10) | 53 | TTYCHAR(VSUSP, 10) SGTTYCHAR(tioltc.t_suspc, 10) |
51 | #endif /* VSUSP */ | 54 | #endif /* VSUSP */ |
52 | #if defined(VDSUSP) | 55 | #if defined(VDSUSP) |
53 | TTYCHAR(VDSUSP, 11) SGTTYCHAR(tioltc.t_dsuspc, 11) | 56 | TTYCHAR(VDSUSP, 11) SGTTYCHAR(tioltc.t_dsuspc, 11) |
54 | #endif /* VDSUSP */ | 57 | #endif /* VDSUSP */ |
55 | #if defined(VREPRINT) | 58 | #if defined(VREPRINT) |
56 | TTYCHAR(VREPRINT, 12) SGTTYCHAR(tioltc.t_rprntc, 12) | 59 | TTYCHAR(VREPRINT, 12) SGTTYCHAR(tioltc.t_rprntc, 12) |
57 | #endif /* VREPRINT */ | 60 | #endif /* VREPRINT */ |
58 | #if defined(VWERASE) | 61 | #if defined(VWERASE) |
59 | TTYCHAR(VWERASE, 13) SGTTYCHAR(tioltc.t_werasc, 13) | 62 | TTYCHAR(VWERASE, 13) SGTTYCHAR(tioltc.t_werasc, 13) |
60 | #endif /* VWERASE */ | 63 | #endif /* VWERASE */ |
61 | #if defined(VLNEXT) | 64 | #if defined(VLNEXT) |
62 | TTYCHAR(VLNEXT, 14) SGTTYCHAR(tioltc.t_lnextc, 14) | 65 | TTYCHAR(VLNEXT, 14) SGTTYCHAR(tioltc.t_lnextc, 14) |
63 | #endif /* VLNEXT */ | 66 | #endif /* VLNEXT */ |
64 | #if defined(VFLUSH) | 67 | #if defined(VFLUSH) |
65 | TTYCHAR(VFLUSH, 15) SGTTYCHAR(tioltc.t_flushc, 15) | 68 | TTYCHAR(VFLUSH, 15) SGTTYCHAR(tioltc.t_flushc, 15) |
66 | #endif /* VFLUSH */ | 69 | #endif /* VFLUSH */ |
67 | #ifdef VSWTCH | 70 | #ifdef VSWTCH |
68 | TTYCHAR(VSWTCH, 16) /* n/a */ | 71 | TTYCHAR(VSWTCH, 16) /* n/a */ |
69 | #endif /* VSWTCH */ | 72 | #endif /* VSWTCH */ |
70 | #if defined(VSTATUS) | 73 | #if defined(VSTATUS) |
71 | TTYCHAR(VSTATUS, 17) SGTTYCHAR(tiots.tc_statusc, 17) | 74 | TTYCHAR(VSTATUS, 17) SGTTYCHAR(tiots.tc_statusc, 17) |
72 | #endif /* VSTATUS */ | 75 | #endif /* VSTATUS */ |
73 | #ifdef VDISCARD | 76 | #ifdef VDISCARD |
74 | TTYCHAR(VDISCARD, 18) /* n/a */ | 77 | TTYCHAR(VDISCARD, 18) /* n/a */ |
75 | #endif /* VDISCARD */ | 78 | #endif /* VDISCARD */ |
76 | 79 | ||
77 | /* name, field, op */ | 80 | /* name, field, op */ |
78 | TTYMODE(IGNPAR, c_iflag, 30) /* n/a */ | 81 | TTYMODE(IGNPAR, c_iflag, 30) /* n/a */ |
79 | TTYMODE(PARMRK, c_iflag, 31) /* n/a */ | 82 | TTYMODE(PARMRK, c_iflag, 31) /* n/a */ |
80 | TTYMODE(INPCK, c_iflag, 32) SGTTYMODEN(ANYP, tio.sg_flags, 32) | 83 | TTYMODE(INPCK, c_iflag, 32) SGTTYMODEN(ANYP, tio.sg_flags, 32) |
81 | TTYMODE(ISTRIP, c_iflag, 33) SGTTYMODEN(LPASS8, tiolm, 33) | 84 | TTYMODE(ISTRIP, c_iflag, 33) SGTTYMODEN(LPASS8, tiolm, 33) |
82 | TTYMODE(INLCR, c_iflag, 34) /* n/a */ | 85 | TTYMODE(INLCR, c_iflag, 34) /* n/a */ |
83 | TTYMODE(IGNCR, c_iflag, 35) /* n/a */ | 86 | TTYMODE(IGNCR, c_iflag, 35) /* n/a */ |
84 | TTYMODE(ICRNL, c_iflag, 36) SGTTYMODE(CRMOD, tio.sg_flags, 36) | 87 | TTYMODE(ICRNL, c_iflag, 36) SGTTYMODE(CRMOD, tio.sg_flags, 36) |
85 | #if defined(IUCLC) | 88 | #if defined(IUCLC) |
86 | TTYMODE(IUCLC, c_iflag, 37) SGTTYMODE(LCASE, tio.sg_flags, 37) | 89 | TTYMODE(IUCLC, c_iflag, 37) SGTTYMODE(LCASE, tio.sg_flags, 37) |
87 | #endif | 90 | #endif |
88 | TTYMODE(IXON, c_iflag, 38) /* n/a */ | 91 | TTYMODE(IXON, c_iflag, 38) /* n/a */ |
89 | TTYMODE(IXANY, c_iflag, 39) SGTTYMODEN(LDECCTQ, tiolm, 39) | 92 | TTYMODE(IXANY, c_iflag, 39) SGTTYMODEN(LDECCTQ, tiolm, 39) |
90 | TTYMODE(IXOFF, c_iflag, 40) SGTTYMODE(TANDEM, tio.sg_flags, 40) | 93 | TTYMODE(IXOFF, c_iflag, 40) SGTTYMODE(TANDEM, tio.sg_flags, 40) |
91 | #ifdef IMAXBEL | 94 | #ifdef IMAXBEL |
92 | TTYMODE(IMAXBEL,c_iflag, 41) /* n/a */ | 95 | TTYMODE(IMAXBEL, c_iflag, 41) /* n/a */ |
93 | #endif /* IMAXBEL */ | 96 | #endif /* IMAXBEL */ |
94 | 97 | ||
95 | TTYMODE(ISIG, c_lflag, 50) /* n/a */ | 98 | TTYMODE(ISIG, c_lflag, 50) /* n/a */ |
96 | TTYMODE(ICANON, c_lflag, 51) SGTTYMODEN(CBREAK, tio.sg_flags, 51) | 99 | TTYMODE(ICANON, c_lflag, 51) SGTTYMODEN(CBREAK, tio.sg_flags, 51) |
97 | #ifdef XCASE | 100 | #ifdef XCASE |
98 | TTYMODE(XCASE, c_lflag, 52) /* n/a */ | 101 | TTYMODE(XCASE, c_lflag, 52) /* n/a */ |
99 | #endif | 102 | #endif |
100 | TTYMODE(ECHO, c_lflag, 53) SGTTYMODE(ECHO, tio.sg_flags, 53) | 103 | TTYMODE(ECHO, c_lflag, 53) SGTTYMODE(ECHO, tio.sg_flags, 53) |
101 | TTYMODE(ECHOE, c_lflag, 54) SGTTYMODE(LCRTERA, tiolm, 54) | 104 | TTYMODE(ECHOE, c_lflag, 54) SGTTYMODE(LCRTERA, tiolm, 54) |
102 | TTYMODE(ECHOK, c_lflag, 55) SGTTYMODE(LCRTKIL, tiolm, 55) | 105 | TTYMODE(ECHOK, c_lflag, 55) SGTTYMODE(LCRTKIL, tiolm, 55) |
103 | TTYMODE(ECHONL, c_lflag, 56) /* n/a */ | 106 | TTYMODE(ECHONL, c_lflag, 56) /* n/a */ |
104 | TTYMODE(NOFLSH, c_lflag, 57) SGTTYMODE(LNOFLSH, tiolm, 57) | 107 | TTYMODE(NOFLSH, c_lflag, 57) SGTTYMODE(LNOFLSH, tiolm, 57) |
105 | TTYMODE(TOSTOP, c_lflag, 58) SGTTYMODE(LTOSTOP, tiolm, 58) | 108 | TTYMODE(TOSTOP, c_lflag, 58) SGTTYMODE(LTOSTOP, tiolm, 58) |
106 | #ifdef IEXTEN | 109 | #ifdef IEXTEN |
107 | TTYMODE(IEXTEN, c_lflag, 59) /* n/a */ | 110 | TTYMODE(IEXTEN, c_lflag, 59) /* n/a */ |
108 | #endif /* IEXTEN */ | 111 | #endif /* IEXTEN */ |
109 | #if defined(ECHOCTL) | 112 | #if defined(ECHOCTL) |
110 | TTYMODE(ECHOCTL,c_lflag, 60) SGTTYMODE(LCTLECH, tiolm, 60) | 113 | TTYMODE(ECHOCTL, c_lflag, 60) SGTTYMODE(LCTLECH, tiolm, 60) |
111 | #endif /* ECHOCTL */ | 114 | #endif /* ECHOCTL */ |
112 | #ifdef ECHOKE | 115 | #ifdef ECHOKE |
113 | TTYMODE(ECHOKE, c_lflag, 61) /* n/a */ | 116 | TTYMODE(ECHOKE, c_lflag, 61) /* n/a */ |
114 | #endif /* ECHOKE */ | 117 | #endif /* ECHOKE */ |
115 | #if defined(PENDIN) | 118 | #if defined(PENDIN) |
116 | TTYMODE(PENDIN, c_lflag, 62) SGTTYMODE(LPENDIN, tiolm, 62) | 119 | TTYMODE(PENDIN, c_lflag, 62) SGTTYMODE(LPENDIN, tiolm, 62) |
117 | #endif /* PENDIN */ | 120 | #endif /* PENDIN */ |
118 | 121 | ||
119 | TTYMODE(OPOST, c_oflag, 70) /* n/a */ | 122 | TTYMODE(OPOST, c_oflag, 70) /* n/a */ |
120 | #if defined(OLCUC) | 123 | #if defined(OLCUC) |
121 | TTYMODE(OLCUC, c_oflag, 71) SGTTYMODE(LCASE, tio.sg_flags, 71) | 124 | TTYMODE(OLCUC, c_oflag, 71) SGTTYMODE(LCASE, tio.sg_flags, 71) |
122 | #endif | 125 | #endif |
123 | TTYMODE(ONLCR, c_oflag, 72) SGTTYMODE(CRMOD, tio.sg_flags, 72) | 126 | TTYMODE(ONLCR, c_oflag, 72) SGTTYMODE(CRMOD, tio.sg_flags, 72) |
124 | #ifdef OCRNL | 127 | #ifdef OCRNL |
125 | TTYMODE(OCRNL, c_oflag, 73) /* n/a */ | 128 | TTYMODE(OCRNL, c_oflag, 73) /* n/a */ |
126 | #endif | 129 | #endif |
127 | #ifdef ONOCR | 130 | #ifdef ONOCR |
128 | TTYMODE(ONOCR, c_oflag, 74) /* n/a */ | 131 | TTYMODE(ONOCR, c_oflag, 74) /* n/a */ |
129 | #endif | 132 | #endif |
130 | #ifdef ONLRET | 133 | #ifdef ONLRET |
131 | TTYMODE(ONLRET, c_oflag, 75) /* n/a */ | 134 | TTYMODE(ONLRET, c_oflag, 75) /* n/a */ |
132 | #endif | 135 | #endif |
133 | 136 | ||
134 | TTYMODE(CS7, c_cflag, 90) /* n/a */ | 137 | TTYMODE(CS7, c_cflag, 90) /* n/a */ |
135 | TTYMODE(CS8, c_cflag, 91) SGTTYMODE(LPASS8, tiolm, 91) | 138 | TTYMODE(CS8, c_cflag, 91) SGTTYMODE(LPASS8, tiolm, 91) |
136 | TTYMODE(PARENB, c_cflag, 92) /* n/a */ | 139 | TTYMODE(PARENB, c_cflag, 92) /* n/a */ |
137 | TTYMODE(PARODD, c_cflag, 93) SGTTYMODE(ODDP, tio.sg_flags, 93) | 140 | TTYMODE(PARODD, c_cflag, 93) SGTTYMODE(ODDP, tio.sg_flags, 93) |
138 | |||
@@ -1,32 +1,25 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | |
3 | uidswap.c | 3 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
4 | 4 | * All rights reserved | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Created: Sat Sep 9 01:56:14 1995 ylo |
6 | 6 | * Code for uid-swapping. | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | */ |
8 | All rights reserved | ||
9 | |||
10 | Created: Sat Sep 9 01:56:14 1995 ylo | ||
11 | |||
12 | Code for uid-swapping. | ||
13 | |||
14 | */ | ||
15 | 8 | ||
16 | #include "includes.h" | 9 | #include "includes.h" |
17 | RCSID("$Id: uidswap.c,v 1.1 1999/10/27 03:42:46 damien Exp $"); | 10 | RCSID("$Id: uidswap.c,v 1.2 1999/11/24 13:26:23 damien Exp $"); |
18 | 11 | ||
19 | #include "ssh.h" | 12 | #include "ssh.h" |
20 | #include "uidswap.h" | 13 | #include "uidswap.h" |
21 | 14 | ||
22 | /* Note: all these functions must work in all of the following cases: | 15 | /* |
23 | 16 | * Note: all these functions must work in all of the following cases: | |
24 | 1. euid=0, ruid=0 | 17 | * 1. euid=0, ruid=0 |
25 | 2. euid=0, ruid!=0 | 18 | * 2. euid=0, ruid!=0 |
26 | 3. euid!=0, ruid!=0 | 19 | * 3. euid!=0, ruid!=0 |
27 | 20 | * Additionally, they must work regardless of whether the system has | |
28 | Additionally, they must work regardless of whether the system has | 21 | * POSIX saved uids or not. |
29 | POSIX saved uids or not. */ | 22 | */ |
30 | 23 | ||
31 | #ifdef _POSIX_SAVED_IDS | 24 | #ifdef _POSIX_SAVED_IDS |
32 | /* Lets assume that posix saved ids also work with seteuid, even though that | 25 | /* Lets assume that posix saved ids also work with seteuid, even though that |
@@ -37,59 +30,57 @@ RCSID("$Id: uidswap.c,v 1.1 1999/10/27 03:42:46 damien Exp $"); | |||
37 | /* Saved effective uid. */ | 30 | /* Saved effective uid. */ |
38 | static uid_t saved_euid = 0; | 31 | static uid_t saved_euid = 0; |
39 | 32 | ||
40 | /* Temporarily changes to the given uid. If the effective user id is not | 33 | /* |
41 | root, this does nothing. This call cannot be nested. */ | 34 | * Temporarily changes to the given uid. If the effective user |
42 | 35 | * id is not root, this does nothing. This call cannot be nested. | |
43 | void temporarily_use_uid(uid_t uid) | 36 | */ |
37 | void | ||
38 | temporarily_use_uid(uid_t uid) | ||
44 | { | 39 | { |
45 | #ifdef SAVED_IDS_WORK_WITH_SETEUID | 40 | #ifdef SAVED_IDS_WORK_WITH_SETEUID |
41 | /* Save the current euid. */ | ||
42 | saved_euid = geteuid(); | ||
46 | 43 | ||
47 | /* Save the current euid. */ | 44 | /* Set the effective uid to the given (unprivileged) uid. */ |
48 | saved_euid = geteuid(); | 45 | if (seteuid(uid) == -1) |
49 | 46 | debug("seteuid %d: %.100s", (int) uid, strerror(errno)); | |
50 | /* Set the effective uid to the given (unprivileged) uid. */ | ||
51 | if (seteuid(uid) == -1) | ||
52 | debug("seteuid %d: %.100s", (int)uid, strerror(errno)); | ||
53 | |||
54 | #else /* SAVED_IDS_WORK_WITH_SETUID */ | 47 | #else /* SAVED_IDS_WORK_WITH_SETUID */ |
48 | /* Propagate the privileged uid to all of our uids. */ | ||
49 | if (setuid(geteuid()) < 0) | ||
50 | debug("setuid %d: %.100s", (int) geteuid(), strerror(errno)); | ||
55 | 51 | ||
56 | /* Propagate the privileged uid to all of our uids. */ | 52 | /* Set the effective uid to the given (unprivileged) uid. */ |
57 | if (setuid(geteuid()) < 0) | 53 | if (seteuid(uid) == -1) |
58 | debug("setuid %d: %.100s", (int)geteuid(), strerror(errno)); | 54 | debug("seteuid %d: %.100s", (int) uid, strerror(errno)); |
59 | |||
60 | /* Set the effective uid to the given (unprivileged) uid. */ | ||
61 | if (seteuid(uid) == -1) | ||
62 | debug("seteuid %d: %.100s", (int)uid, strerror(errno)); | ||
63 | |||
64 | #endif /* SAVED_IDS_WORK_WITH_SETEUID */ | 55 | #endif /* SAVED_IDS_WORK_WITH_SETEUID */ |
65 | |||
66 | } | 56 | } |
67 | 57 | ||
68 | /* Restores to the original uid. */ | 58 | /* |
69 | 59 | * Restores to the original uid. | |
70 | void restore_uid() | 60 | */ |
61 | void | ||
62 | restore_uid() | ||
71 | { | 63 | { |
72 | #ifdef SAVED_IDS_WORK_WITH_SETEUID | 64 | #ifdef SAVED_IDS_WORK_WITH_SETEUID |
73 | 65 | /* Set the effective uid back to the saved uid. */ | |
74 | /* Set the effective uid back to the saved uid. */ | 66 | if (seteuid(saved_euid) < 0) |
75 | if (seteuid(saved_euid) < 0) | 67 | debug("seteuid %d: %.100s", (int) saved_euid, strerror(errno)); |
76 | debug("seteuid %d: %.100s", (int)saved_euid, strerror(errno)); | ||
77 | |||
78 | #else /* SAVED_IDS_WORK_WITH_SETEUID */ | 68 | #else /* SAVED_IDS_WORK_WITH_SETEUID */ |
79 | 69 | /* We are unable to restore the real uid to its unprivileged | |
80 | /* We are unable to restore the real uid to its unprivileged value. */ | 70 | value. */ |
81 | /* Propagate the real uid (usually more privileged) to effective uid | 71 | /* Propagate the real uid (usually more privileged) to effective |
82 | as well. */ | 72 | uid as well. */ |
83 | setuid(getuid()); | 73 | setuid(getuid()); |
84 | |||
85 | #endif /* SAVED_IDS_WORK_WITH_SETEUID */ | 74 | #endif /* SAVED_IDS_WORK_WITH_SETEUID */ |
86 | } | 75 | } |
87 | 76 | ||
88 | /* Permanently sets all uids to the given uid. This cannot be called while | 77 | /* |
89 | temporarily_use_uid is effective. */ | 78 | * Permanently sets all uids to the given uid. This cannot be |
90 | 79 | * called while temporarily_use_uid is effective. | |
91 | void permanently_set_uid(uid_t uid) | 80 | */ |
81 | void | ||
82 | permanently_set_uid(uid_t uid) | ||
92 | { | 83 | { |
93 | if (setuid(uid) < 0) | 84 | if (setuid(uid) < 0) |
94 | debug("setuid %d: %.100s", (int)uid, strerror(errno)); | 85 | debug("setuid %d: %.100s", (int) uid, strerror(errno)); |
95 | } | 86 | } |
@@ -1,30 +1,36 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * | |
3 | uidswap.h | 3 | * uidswap.h |
4 | 4 | * | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
6 | 6 | * | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
8 | All rights reserved | 8 | * All rights reserved |
9 | 9 | * | |
10 | Created: Sat Sep 9 01:43:15 1995 ylo | 10 | * Created: Sat Sep 9 01:43:15 1995 ylo |
11 | Last modified: Sat Sep 9 02:34:04 1995 ylo | 11 | * Last modified: Sat Sep 9 02:34:04 1995 ylo |
12 | 12 | * | |
13 | */ | 13 | */ |
14 | 14 | ||
15 | #ifndef UIDSWAP_H | 15 | #ifndef UIDSWAP_H |
16 | #define UIDSWAP_H | 16 | #define UIDSWAP_H |
17 | 17 | ||
18 | /* Temporarily changes to the given uid. If the effective user id is not | 18 | /* |
19 | root, this does nothing. This call cannot be nested. */ | 19 | * Temporarily changes to the given uid. If the effective user id is not |
20 | void temporarily_use_uid(uid_t uid); | 20 | * root, this does nothing. This call cannot be nested. |
21 | */ | ||
22 | void temporarily_use_uid(uid_t uid); | ||
21 | 23 | ||
22 | /* Restores the original effective user id after temporarily_use_uid(). | 24 | /* |
23 | This should only be called while temporarily_use_uid is effective. */ | 25 | * Restores the original effective user id after temporarily_use_uid(). |
24 | void restore_uid(); | 26 | * This should only be called while temporarily_use_uid is effective. |
27 | */ | ||
28 | void restore_uid(); | ||
25 | 29 | ||
26 | /* Permanently sets all uids to the given uid. This cannot be called while | 30 | /* |
27 | temporarily_use_uid is effective. This must also clear any saved uids. */ | 31 | * Permanently sets all uids to the given uid. This cannot be called while |
28 | void permanently_set_uid(uid_t uid); | 32 | * temporarily_use_uid is effective. This must also clear any saved uids. |
33 | */ | ||
34 | void permanently_set_uid(uid_t uid); | ||
29 | 35 | ||
30 | #endif /* UIDSWAP_H */ | 36 | #endif /* UIDSWAP_H */ |
@@ -1,56 +1,53 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | |
3 | xmalloc.c | 3 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
4 | 4 | * All rights reserved | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Created: Mon Mar 20 21:23:10 1995 ylo |
6 | 6 | * Versions of malloc and friends that check their results, and never return | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * failure (they call fatal if they encounter an error). |
8 | All rights reserved | 8 | */ |
9 | |||
10 | Created: Mon Mar 20 21:23:10 1995 ylo | ||
11 | |||
12 | Versions of malloc and friends that check their results, and never return | ||
13 | failure (they call fatal if they encounter an error). | ||
14 | |||
15 | */ | ||
16 | 9 | ||
17 | #include "includes.h" | 10 | #include "includes.h" |
18 | RCSID("$Id: xmalloc.c,v 1.1 1999/10/27 03:42:46 damien Exp $"); | 11 | RCSID("$Id: xmalloc.c,v 1.2 1999/11/24 13:26:23 damien Exp $"); |
19 | 12 | ||
20 | #include "ssh.h" | 13 | #include "ssh.h" |
21 | 14 | ||
22 | void *xmalloc(size_t size) | 15 | void * |
16 | xmalloc(size_t size) | ||
23 | { | 17 | { |
24 | void *ptr = malloc(size); | 18 | void *ptr = malloc(size); |
25 | if (ptr == NULL) | 19 | if (ptr == NULL) |
26 | fatal("xmalloc: out of memory (allocating %d bytes)", (int)size); | 20 | fatal("xmalloc: out of memory (allocating %d bytes)", (int) size); |
27 | return ptr; | 21 | return ptr; |
28 | } | 22 | } |
29 | 23 | ||
30 | void *xrealloc(void *ptr, size_t new_size) | 24 | void * |
25 | xrealloc(void *ptr, size_t new_size) | ||
31 | { | 26 | { |
32 | void *new_ptr; | 27 | void *new_ptr; |
33 | 28 | ||
34 | if (ptr == NULL) | 29 | if (ptr == NULL) |
35 | fatal("xrealloc: NULL pointer given as argument"); | 30 | fatal("xrealloc: NULL pointer given as argument"); |
36 | new_ptr = realloc(ptr, new_size); | 31 | new_ptr = realloc(ptr, new_size); |
37 | if (new_ptr == NULL) | 32 | if (new_ptr == NULL) |
38 | fatal("xrealloc: out of memory (new_size %d bytes)", (int)new_size); | 33 | fatal("xrealloc: out of memory (new_size %d bytes)", (int) new_size); |
39 | return new_ptr; | 34 | return new_ptr; |
40 | } | 35 | } |
41 | 36 | ||
42 | void xfree(void *ptr) | 37 | void |
38 | xfree(void *ptr) | ||
43 | { | 39 | { |
44 | if (ptr == NULL) | 40 | if (ptr == NULL) |
45 | fatal("xfree: NULL pointer given as argument"); | 41 | fatal("xfree: NULL pointer given as argument"); |
46 | free(ptr); | 42 | free(ptr); |
47 | } | 43 | } |
48 | 44 | ||
49 | char *xstrdup(const char *str) | 45 | char * |
46 | xstrdup(const char *str) | ||
50 | { | 47 | { |
51 | int len = strlen(str) + 1; | 48 | int len = strlen(str) + 1; |
52 | 49 | ||
53 | char *cp = xmalloc(len); | 50 | char *cp = xmalloc(len); |
54 | strlcpy(cp, str, len); | 51 | strlcpy(cp, str, len); |
55 | return cp; | 52 | return cp; |
56 | } | 53 | } |
@@ -1,34 +1,34 @@ | |||
1 | /* | 1 | /* |
2 | 2 | * | |
3 | xmalloc.h | 3 | * xmalloc.h |
4 | 4 | * | |
5 | Author: Tatu Ylonen <ylo@cs.hut.fi> | 5 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
6 | 6 | * | |
7 | Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 7 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
8 | All rights reserved | 8 | * All rights reserved |
9 | 9 | * | |
10 | Created: Mon Mar 20 22:09:17 1995 ylo | 10 | * Created: Mon Mar 20 22:09:17 1995 ylo |
11 | 11 | * | |
12 | Versions of malloc and friends that check their results, and never return | 12 | * Versions of malloc and friends that check their results, and never return |
13 | failure (they call fatal if they encounter an error). | 13 | * failure (they call fatal if they encounter an error). |
14 | 14 | * | |
15 | */ | 15 | */ |
16 | 16 | ||
17 | /* RCSID("$Id: xmalloc.h,v 1.1 1999/10/27 03:42:46 damien Exp $"); */ | 17 | /* RCSID("$Id: xmalloc.h,v 1.2 1999/11/24 13:26:23 damien Exp $"); */ |
18 | 18 | ||
19 | #ifndef XMALLOC_H | 19 | #ifndef XMALLOC_H |
20 | #define XMALLOC_H | 20 | #define XMALLOC_H |
21 | 21 | ||
22 | /* Like malloc, but calls fatal() if out of memory. */ | 22 | /* Like malloc, but calls fatal() if out of memory. */ |
23 | void *xmalloc(size_t size); | 23 | void *xmalloc(size_t size); |
24 | 24 | ||
25 | /* Like realloc, but calls fatal() if out of memory. */ | 25 | /* Like realloc, but calls fatal() if out of memory. */ |
26 | void *xrealloc(void *ptr, size_t new_size); | 26 | void *xrealloc(void *ptr, size_t new_size); |
27 | 27 | ||
28 | /* Frees memory allocated using xmalloc or xrealloc. */ | 28 | /* Frees memory allocated using xmalloc or xrealloc. */ |
29 | void xfree(void *ptr); | 29 | void xfree(void *ptr); |
30 | 30 | ||
31 | /* Allocates memory using xmalloc, and copies the string into that memory. */ | 31 | /* Allocates memory using xmalloc, and copies the string into that memory. */ |
32 | char *xstrdup(const char *str); | 32 | char *xstrdup(const char *str); |
33 | 33 | ||
34 | #endif /* XMALLOC_H */ | 34 | #endif /* XMALLOC_H */ |