Develop and Download Open Source Software

Browse Subversion Repository

Annotation of /branches/ssh_chacha20poly1305/ttssh2/ttxssh/auth.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2841 - (hide annotations) (download) (as text)
Fri Aug 26 16:26:02 2005 UTC (18 years, 7 months ago) by yutakakn
Original Path: ttssh2/trunk/ttxssh/auth.c
File MIME type: text/x-csrc
File size: 31421 byte(s)
自動ログイン時にSSH認証ダイアログを最小化するようにした。

1 yutakakn 2728 /*
2     Copyright (c) 1998-2001, Robert O'Callahan
3     All rights reserved.
4    
5     Redistribution and use in source and binary forms, with or without modification,
6     are permitted provided that the following conditions are met:
7    
8     Redistributions of source code must retain the above copyright notice, this list of
9     conditions and the following disclaimer.
10    
11     Redistributions in binary form must reproduce the above copyright notice, this list
12     of conditions and the following disclaimer in the documentation and/or other materials
13     provided with the distribution.
14    
15     The name of Robert O'Callahan may not be used to endorse or promote products derived from
16     this software without specific prior written permission.
17    
18     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
19     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20     OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21     THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22     EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23     SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25     OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26     SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27     */
28    
29     #include "ttxssh.h"
30     #include "util.h"
31 yutakakn 2800 #include "ssh.h"
32 yutakakn 2728
33     #include <io.h>
34     #include <fcntl.h>
35     #include <stdlib.h>
36     #include <errno.h>
37    
38     #include "resource.h"
39     #include "keyfiles.h"
40    
41     #define AUTH_START_USER_AUTH_ON_ERROR_END 1
42    
43     #define MAX_AUTH_CONTROL IDC_SSHUSETIS
44    
45     static void destroy_malloced_string(char FAR * FAR * str)
46     {
47     if (*str != NULL) {
48     memset(*str, 0, strlen(*str));
49     free(*str);
50     *str = NULL;
51     }
52     }
53    
54     static int auth_types_to_control_IDs[] = {
55     -1, IDC_SSHUSERHOSTS, IDC_SSHUSERSA, IDC_SSHUSEPASSWORD,
56     IDC_SSHUSERHOSTS, IDC_SSHUSETIS, -1
57     };
58    
59     static LRESULT CALLBACK password_wnd_proc(HWND control, UINT msg,
60     WPARAM wParam, LPARAM lParam)
61     {
62     switch (msg) {
63     case WM_CHAR:
64     if ((GetKeyState(VK_CONTROL) & 0x8000) != 0) {
65     char chars[] = { (char) wParam, 0 };
66    
67     SendMessage(control, EM_REPLACESEL, (WPARAM) TRUE,
68     (LPARAM) (char FAR *) chars);
69     return 0;
70     }
71     }
72    
73     return CallWindowProc((WNDPROC) GetWindowLong(control, GWL_USERDATA),
74     control, msg, wParam, lParam);
75     }
76    
77     static void init_password_control(HWND dlg)
78     {
79     HWND passwordControl = GetDlgItem(dlg, IDC_SSHPASSWORD);
80    
81     SetWindowLong(passwordControl, GWL_USERDATA,
82     SetWindowLong(passwordControl, GWL_WNDPROC,
83     (LONG) password_wnd_proc));
84    
85     SetFocus(passwordControl);
86     }
87    
88     static void set_auth_options_status(HWND dlg, int controlID)
89     {
90     BOOL RSA_enabled = controlID == IDC_SSHUSERSA;
91     BOOL rhosts_enabled = controlID == IDC_SSHUSERHOSTS;
92     BOOL TIS_enabled = controlID == IDC_SSHUSETIS;
93     int i;
94    
95     CheckRadioButton(dlg, IDC_SSHUSEPASSWORD, MAX_AUTH_CONTROL, controlID);
96    
97     EnableWindow(GetDlgItem(dlg, IDC_SSHPASSWORDCAPTION), !TIS_enabled);
98     EnableWindow(GetDlgItem(dlg, IDC_SSHPASSWORD), !TIS_enabled);
99    
100     for (i = IDC_CHOOSERSAFILE; i <= IDC_RSAFILENAME; i++) {
101     EnableWindow(GetDlgItem(dlg, i), RSA_enabled);
102     }
103    
104     for (i = IDC_LOCALUSERNAMELABEL; i <= IDC_HOSTRSAFILENAME; i++) {
105     EnableWindow(GetDlgItem(dlg, i), rhosts_enabled);
106     }
107     }
108    
109     static void init_auth_machine_banner(PTInstVar pvar, HWND dlg)
110     {
111     char buf[1024] = "Logging in to ";
112    
113     if (strlen(buf) + strlen(SSH_get_host_name(pvar)) < sizeof(buf) - 2) {
114     strcat(buf, SSH_get_host_name(pvar));
115     }
116     SetDlgItemText(dlg, IDC_SSHAUTHBANNER, buf);
117     }
118    
119     static void update_server_supported_types(PTInstVar pvar, HWND dlg)
120     {
121     int supported_methods = pvar->auth_state.supported_types;
122     int cur_control = -1;
123     int control;
124     HWND focus = GetFocus();
125    
126     if (supported_methods == 0) {
127     return;
128     }
129    
130     for (control = IDC_SSHUSEPASSWORD; control <= MAX_AUTH_CONTROL;
131     control++) {
132     BOOL enabled = FALSE;
133     int method;
134     HWND item = GetDlgItem(dlg, control);
135    
136     if (item != NULL) {
137     for (method = 0; method <= SSH_AUTH_MAX; method++) {
138     if (auth_types_to_control_IDs[method] == control
139     && (supported_methods & (1 << method)) != 0) {
140     enabled = TRUE;
141     }
142     }
143    
144     EnableWindow(item, enabled);
145    
146     if (IsDlgButtonChecked(dlg, control)) {
147     cur_control = control;
148     }
149     }
150     }
151    
152     if (cur_control >= 0) {
153     if (!IsWindowEnabled(GetDlgItem(dlg, cur_control))) {
154     do {
155     cur_control++;
156     if (cur_control > MAX_AUTH_CONTROL) {
157     cur_control = IDC_SSHUSEPASSWORD;
158     }
159     } while (!IsWindowEnabled(GetDlgItem(dlg, cur_control)));
160    
161     set_auth_options_status(dlg, cur_control);
162    
163     if (focus != NULL && !IsWindowEnabled(focus)) {
164     SetFocus(GetDlgItem(dlg, cur_control));
165     }
166     }
167     }
168     }
169    
170     static void init_auth_dlg(PTInstVar pvar, HWND dlg)
171     {
172     int default_method = pvar->session_settings.DefaultAuthMethod;
173    
174     init_auth_machine_banner(pvar, dlg);
175     init_password_control(dlg);
176    
177     if (pvar->auth_state.failed_method != SSH_AUTH_NONE) {
178     /* must be retrying a failed attempt */
179     SetDlgItemText(dlg, IDC_SSHAUTHBANNER2,
180     "Authentication failed. Please retry.");
181     SetWindowText(dlg, "Retrying SSH Authentication");
182     default_method = pvar->auth_state.failed_method;
183     }
184    
185     set_auth_options_status(dlg,
186     auth_types_to_control_IDs[default_method]);
187    
188     if (default_method == SSH_AUTH_TIS) {
189     /* we disabled the password control, so fix the focus */
190     SetFocus(GetDlgItem(dlg, IDC_SSHUSETIS));
191     }
192    
193     if (pvar->auth_state.user != NULL) {
194     SetDlgItemText(dlg, IDC_SSHUSERNAME, pvar->auth_state.user);
195     EnableWindow(GetDlgItem(dlg, IDC_SSHUSERNAME), FALSE);
196     EnableWindow(GetDlgItem(dlg, IDC_SSHUSERNAMELABEL), FALSE);
197     } else if (pvar->session_settings.DefaultUserName[0] != 0) {
198     SetDlgItemText(dlg, IDC_SSHUSERNAME,
199     pvar->session_settings.DefaultUserName);
200     } else {
201     SetFocus(GetDlgItem(dlg, IDC_SSHUSERNAME));
202     }
203    
204     SetDlgItemText(dlg, IDC_RSAFILENAME,
205     pvar->session_settings.DefaultRSAPrivateKeyFile);
206     SetDlgItemText(dlg, IDC_HOSTRSAFILENAME,
207     pvar->session_settings.DefaultRhostsHostPrivateKeyFile);
208     SetDlgItemText(dlg, IDC_LOCALUSERNAME,
209     pvar->session_settings.DefaultRhostsLocalUserName);
210    
211     update_server_supported_types(pvar, dlg);
212 yutakakn 2739
213 yutakakn 2784 // SSH2 autologin
214 yutakakn 2739 // ���[�U�A�p�X���[�h�A�F�����\�b�h���������������A������������OK�{�^�������������B
215 yutakakn 2784 //
216     // (2004.12.1 yutaka)
217     // (2005.1.26 yutaka) ���J���F���T�|�[�g
218 yutakakn 2739 if (pvar->ssh2_autologin == 1) {
219     SetDlgItemText(dlg, IDC_SSHUSERNAME, pvar->ssh2_username);
220     EnableWindow(GetDlgItem(dlg, IDC_SSHUSERNAME), FALSE);
221     EnableWindow(GetDlgItem(dlg, IDC_SSHUSERNAMELABEL), FALSE);
222    
223     SetDlgItemText(dlg, IDC_SSHPASSWORD, pvar->ssh2_password);
224     EnableWindow(GetDlgItem(dlg, IDC_SSHPASSWORD), FALSE);
225     EnableWindow(GetDlgItem(dlg, IDC_SSHPASSWORDCAPTION), FALSE);
226 yutakakn 2841 //20050822���� start T.Takahashi
227     ShowWindow(dlg,SW_MINIMIZE);
228     //20050822���� end T.Takahashi
229 yutakakn 2739
230 yutakakn 2784 if (pvar->ssh2_authmethod == SSH_AUTH_PASSWORD) {
231 yutakakn 2739 CheckRadioButton(dlg, IDC_SSHUSEPASSWORD, MAX_AUTH_CONTROL, IDC_SSHUSEPASSWORD);
232 yutakakn 2784
233     } else if (pvar->ssh2_authmethod == SSH_AUTH_RSA) {
234     CheckRadioButton(dlg, IDC_SSHUSEPASSWORD, MAX_AUTH_CONTROL, IDC_SSHUSERSA);
235    
236     SetDlgItemText(dlg, IDC_RSAFILENAME, pvar->ssh2_keyfile);
237     EnableWindow(GetDlgItem(dlg, IDC_CHOOSERSAFILE), FALSE);
238     EnableWindow(GetDlgItem(dlg, IDC_RSAFILENAME), FALSE);
239    
240 yutakakn 2739 } else {
241     // TODO
242    
243     }
244     }
245    
246 yutakakn 2800 #if 1
247 yutakakn 2799 // �p�X���[�h�F���������O���Akeyboard-interactive���\�b�h�������������A���x������
248     // ���X�����B(2005.3.12 yutaka)
249     if (pvar->settings.ssh2_keyboard_interactive == 1) {
250     SetDlgItemText(dlg, IDC_SSHUSEPASSWORD, "Use p&lain password to log in (with keyboard-interactive)");
251     }
252    
253 yutakakn 2800 if (pvar->settings.ssh_protocol_version == 1) {
254     SetDlgItemText(dlg, IDC_SSHUSETIS, "Use challenge/response to log in(&TIS)");
255     } else {
256 yutakakn 2804 SetDlgItemText(dlg, IDC_SSHUSETIS, "Use challenge/response to log in(&keyboard-interactive)");
257 yutakakn 2800 }
258     #endif
259    
260 yutakakn 2728 }
261    
262     static char FAR *alloc_control_text(HWND ctl)
263     {
264     int len = GetWindowTextLength(ctl);
265     char FAR *result = malloc(len + 1);
266    
267     if (result != NULL) {
268     GetWindowText(ctl, result, len + 1);
269     result[len] = 0;
270     }
271    
272     return result;
273     }
274    
275     static int get_key_file_name(HWND parent, char FAR * buf, int bufsize)
276     {
277     #ifdef TERATERM32
278     OPENFILENAME params;
279     char fullname_buf[2048] = "identity";
280    
281 yutakakn 2762 ZeroMemory(&params, sizeof(params));
282 yutakakn 2728 params.lStructSize = sizeof(OPENFILENAME);
283     params.hwndOwner = parent;
284 yutakakn 2762 // �t�B���^������ (2004.12.19 yutaka)
285 yutakakn 2818 // 3�t�@�C���t�B���^������ (2005.4.26 yutaka)
286     params.lpstrFilter = "identity files\0identity;id_rsa;id_dsa\0identity(RSA1)\0identity\0id_rsa(SSH2)\0id_rsa\0id_dsa(SSH2)\0id_dsa\0all(*.*)\0*.*\0\0";
287 yutakakn 2728 params.lpstrCustomFilter = NULL;
288     params.nFilterIndex = 0;
289     buf[0] = 0;
290     params.lpstrFile = fullname_buf;
291     params.nMaxFile = sizeof(fullname_buf);
292     params.lpstrFileTitle = NULL;
293     params.lpstrInitialDir = NULL;
294 yutakakn 2818 params.lpstrTitle = "Choose a file with the RSA/DSA private key";
295 yutakakn 2728 params.Flags =
296     OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY;
297     params.lpstrDefExt = NULL;
298    
299     if (GetOpenFileName(&params) != 0) {
300     copy_teraterm_dir_relative_path(buf, bufsize, fullname_buf);
301     return 1;
302     } else {
303     return 0;
304     }
305     #else
306     return 0;
307     #endif
308     }
309    
310     static void choose_RSA_key_file(HWND dlg)
311     {
312     char buf[1024];
313    
314     if (get_key_file_name(dlg, buf, sizeof(buf))) {
315     SetDlgItemText(dlg, IDC_RSAFILENAME, buf);
316     }
317     }
318    
319     static void choose_host_RSA_key_file(HWND dlg)
320     {
321     char buf[1024];
322    
323     if (get_key_file_name(dlg, buf, sizeof(buf))) {
324     SetDlgItemText(dlg, IDC_HOSTRSAFILENAME, buf);
325     }
326     }
327    
328     static BOOL end_auth_dlg(PTInstVar pvar, HWND dlg)
329     {
330     int method = SSH_AUTH_PASSWORD;
331     char FAR *password =
332     alloc_control_text(GetDlgItem(dlg, IDC_SSHPASSWORD));
333     CRYPTKeyPair FAR *key_pair = NULL;
334    
335     if (IsDlgButtonChecked(dlg, IDC_SSHUSERSA)) {
336     method = SSH_AUTH_RSA;
337     } else if (IsDlgButtonChecked(dlg, IDC_SSHUSERHOSTS)) {
338     if (GetWindowTextLength(GetDlgItem(dlg, IDC_HOSTRSAFILENAME)) > 0) {
339     method = SSH_AUTH_RHOSTS_RSA;
340     } else {
341     method = SSH_AUTH_RHOSTS;
342     }
343     } else if (IsDlgButtonChecked(dlg, IDC_SSHUSETIS)) {
344     method = SSH_AUTH_TIS;
345     }
346    
347     if (method == SSH_AUTH_RSA || method == SSH_AUTH_RHOSTS_RSA) {
348     char buf[2048];
349     int file_ctl_ID =
350     method == SSH_AUTH_RSA ? IDC_RSAFILENAME : IDC_HOSTRSAFILENAME;
351    
352     buf[0] = 0;
353     GetDlgItemText(dlg, file_ctl_ID, buf, sizeof(buf));
354     if (buf[0] == 0) {
355     notify_nonfatal_error(pvar,
356 yutakakn 2762 "You must specify a file containing the RSA/DSA private key.");
357 yutakakn 2728 SetFocus(GetDlgItem(dlg, file_ctl_ID));
358     destroy_malloced_string(&password);
359     return FALSE;
360 yutakakn 2762 }
361    
362     if (SSHv1(pvar)) {
363 yutakakn 2728 BOOL invalid_passphrase = FALSE;
364    
365     key_pair = KEYFILES_read_private_key(pvar, buf, password,
366     &invalid_passphrase,
367     FALSE);
368    
369     if (key_pair == NULL) {
370     if (invalid_passphrase) {
371     HWND passwordCtl = GetDlgItem(dlg, IDC_SSHPASSWORD);
372    
373     SetFocus(passwordCtl);
374     SendMessage(passwordCtl, EM_SETSEL, 0, -1);
375     } else {
376     SetFocus(GetDlgItem(dlg, file_ctl_ID));
377     }
378     destroy_malloced_string(&password);
379     return FALSE;
380     }
381 yutakakn 2762
382     } else { // SSH2(yutaka)
383     BOOL invalid_passphrase = FALSE;
384 yutakakn 2769 char errmsg[256];
385 yutakakn 2762
386 yutakakn 2769 memset(errmsg, 0, sizeof(errmsg));
387 yutakakn 2784 //GetCurrentDirectory(sizeof(errmsg), errmsg);
388 yutakakn 2769
389 yutakakn 2762 key_pair = read_SSH2_private_key(pvar, buf, password,
390     &invalid_passphrase,
391 yutakakn 2769 FALSE,
392     errmsg,
393     sizeof(errmsg)
394     );
395 yutakakn 2762
396     if (key_pair == NULL) { // read error
397 yutakakn 2769 char buf[1024];
398     _snprintf(buf, sizeof(buf), "read error SSH2 private key file\r\n%s", errmsg);
399     notify_nonfatal_error(pvar, buf);
400 yutakakn 2762 destroy_malloced_string(&password);
401     return FALSE;
402     }
403    
404 yutakakn 2728 }
405 yutakakn 2762
406 yutakakn 2728 }
407    
408     /* from here on, we cannot fail, so just munge cur_cred in place */
409     pvar->auth_state.cur_cred.method = method;
410     pvar->auth_state.cur_cred.key_pair = key_pair;
411     /* we can't change the user name once it's set. It may already have
412     been sent to the server, and it can only be sent once. */
413     if (pvar->auth_state.user == NULL) {
414     pvar->auth_state.user =
415     alloc_control_text(GetDlgItem(dlg, IDC_SSHUSERNAME));
416     }
417 yutakakn 2811
418     // ���J���F���������A�Z�b�V�������������p�X���[�h���g�����������������������������������B
419     // (2005.4.8 yutaka)
420     if (method == SSH_AUTH_PASSWORD || method == SSH_AUTH_RSA) {
421 yutakakn 2728 pvar->auth_state.cur_cred.password = password;
422     } else {
423     destroy_malloced_string(&password);
424     }
425     if (method == SSH_AUTH_RHOSTS || method == SSH_AUTH_RHOSTS_RSA) {
426     if (pvar->session_settings.DefaultAuthMethod != SSH_AUTH_RHOSTS) {
427     notify_nonfatal_error(pvar,
428     "Rhosts authentication will probably fail because it was not "
429     "the default authentication method.\n"
430     "To use Rhosts authentication "
431     "in TTSSH, you need to set it to be the default by restarting\n"
432     "TTSSH and selecting \"SSH Authentication...\" from the Setup menu"
433     "before connecting.");
434     }
435    
436     pvar->auth_state.cur_cred.rhosts_client_user =
437     alloc_control_text(GetDlgItem(dlg, IDC_LOCALUSERNAME));
438     }
439     pvar->auth_state.auth_dialog = NULL;
440    
441     GetDlgItemText(dlg, IDC_RSAFILENAME,
442     pvar->session_settings.DefaultRSAPrivateKeyFile,
443     sizeof(pvar->session_settings.
444     DefaultRSAPrivateKeyFile));
445     GetDlgItemText(dlg, IDC_HOSTRSAFILENAME,
446     pvar->session_settings.DefaultRhostsHostPrivateKeyFile,
447     sizeof(pvar->session_settings.
448     DefaultRhostsHostPrivateKeyFile));
449     GetDlgItemText(dlg, IDC_LOCALUSERNAME,
450     pvar->session_settings.DefaultRhostsLocalUserName,
451     sizeof(pvar->session_settings.
452     DefaultRhostsLocalUserName));
453    
454     if (SSHv1(pvar)) {
455     SSH_notify_user_name(pvar);
456     SSH_notify_cred(pvar);
457     } else {
458     // for SSH2(yutaka)
459     do_SSH2_userauth(pvar);
460     }
461    
462     EndDialog(dlg, 1);
463     return TRUE;
464     }
465    
466     static BOOL CALLBACK auth_dlg_proc(HWND dlg, UINT msg, WPARAM wParam,
467     LPARAM lParam)
468     {
469 yutakakn 2739 const int IDC_TIMER1 = 300;
470 yutakakn 2752 const int autologin_timeout = 10; // �~���b
471 yutakakn 2728 PTInstVar pvar;
472    
473     switch (msg) {
474     case WM_INITDIALOG:
475     pvar = (PTInstVar) lParam;
476     pvar->auth_state.auth_dialog = dlg;
477     SetWindowLong(dlg, DWL_USER, lParam);
478    
479     init_auth_dlg(pvar, dlg);
480 yutakakn 2739
481     // SSH2 autologin���L�����������A�^�C�}���d�|�����B (2004.12.1 yutaka)
482     if (pvar->ssh2_autologin == 1) {
483     SetTimer(dlg, IDC_TIMER1, autologin_timeout, 0);
484     }
485 yutakakn 2728 return FALSE; /* because we set the focus */
486    
487 yutakakn 2739 case WM_TIMER:
488 yutakakn 2752 pvar = (PTInstVar) GetWindowLong(dlg, DWL_USER);
489     // �F�������������������A�F���f�[�^�����M�����B�����������A�������B(2004.12.16 yutaka)
490     if (!(pvar->ssh_state.status_flags & STATUS_DONT_SEND_USER_NAME)) {
491     KillTimer(dlg, IDC_TIMER1);
492     SendMessage(dlg, WM_COMMAND, IDOK, 0);
493     }
494 yutakakn 2739 return TRUE;
495    
496 yutakakn 2728 case WM_COMMAND:
497     pvar = (PTInstVar) GetWindowLong(dlg, DWL_USER);
498    
499     switch (LOWORD(wParam)) {
500     case IDOK:
501 yutakakn 2783 // �F�������������������A�F���f�[�^�����M�����B�����������A�������B(2001.1.25 yutaka)
502 yutakakn 2835 if (pvar->userauth_retry_count == 0 && (pvar->ssh_state.status_flags & STATUS_DONT_SEND_USER_NAME)) {
503 yutakakn 2783 return FALSE;
504     }
505 yutakakn 2728 return end_auth_dlg(pvar, dlg);
506    
507     case IDCANCEL: /* kill the connection */
508     pvar->auth_state.auth_dialog = NULL;
509     notify_closed_connection(pvar);
510     EndDialog(dlg, 0);
511     return TRUE;
512    
513     case IDC_SSHUSEPASSWORD:
514     case IDC_SSHUSERSA:
515     case IDC_SSHUSERHOSTS:
516     case IDC_SSHUSETIS:
517     set_auth_options_status(dlg, LOWORD(wParam));
518     return TRUE;
519    
520     case IDC_CHOOSERSAFILE:
521     choose_RSA_key_file(dlg);
522     return TRUE;
523    
524     case IDC_CHOOSEHOSTRSAFILE:
525     choose_host_RSA_key_file(dlg);
526     return TRUE;
527    
528     default:
529     return FALSE;
530     }
531    
532     default:
533     return FALSE;
534     }
535     }
536    
537     char FAR *AUTH_get_user_name(PTInstVar pvar)
538     {
539     return pvar->auth_state.user;
540     }
541    
542     int AUTH_set_supported_auth_types(PTInstVar pvar, int types)
543     {
544     char buf[1024];
545    
546     _snprintf(buf, sizeof(buf),
547     "Server reports supported authentication method mask = %d",
548     types);
549     buf[sizeof(buf) - 1] = 0;
550     notify_verbose_message(pvar, buf, LOG_LEVEL_VERBOSE);
551    
552     if (SSHv1(pvar)) {
553     types &= (1 << SSH_AUTH_PASSWORD) | (1 << SSH_AUTH_RSA)
554     | (1 << SSH_AUTH_RHOSTS_RSA) | (1 << SSH_AUTH_RHOSTS)
555     | (1 << SSH_AUTH_TIS);
556     } else {
557     // for SSH2(yutaka)
558 yutakakn 2762 // types &= (1 << SSH_AUTH_PASSWORD);
559     // ���J���F�����L�������� (2004.12.18 yutaka)
560 yutakakn 2800 // TIS�������BSSH2����keyboard-interactive�����������B(2005.3.12 yutaka)
561 yutakakn 2762 types &= (1 << SSH_AUTH_PASSWORD) | (1 << SSH_AUTH_RSA)
562 yutakakn 2800 | (1 << SSH_AUTH_DSA)
563     | (1 << SSH_AUTH_TIS);
564 yutakakn 2728 }
565     pvar->auth_state.supported_types = types;
566    
567     if (types == 0) {
568     notify_fatal_error(pvar,
569     "Server does not support any of the authentication options\n"
570     "provided by TTSSH. This connection will now close.");
571     return 0;
572     } else {
573     if (pvar->auth_state.auth_dialog != NULL) {
574     update_server_supported_types(pvar,
575     pvar->auth_state.auth_dialog);
576     }
577    
578     return 1;
579     }
580     }
581    
582     static void start_user_auth(PTInstVar pvar)
583     {
584 yutakakn 2739 // �F���_�C�A���O���\�������� (2004.12.1 yutaka)
585 yutakakn 2728 PostMessage(pvar->NotificationWindow, WM_COMMAND, (WPARAM) ID_SSHAUTH,
586     (LPARAM) NULL);
587     pvar->auth_state.cur_cred.method = SSH_AUTH_NONE;
588     }
589    
590     static void try_default_auth(PTInstVar pvar)
591     {
592     if (pvar->session_settings.TryDefaultAuth) {
593     switch (pvar->session_settings.DefaultAuthMethod) {
594     case SSH_AUTH_RSA:{
595     BOOL invalid_passphrase;
596     char password[] = "";
597    
598     pvar->auth_state.cur_cred.key_pair
599     =
600     KEYFILES_read_private_key(pvar,
601     pvar->session_settings.
602     DefaultRSAPrivateKeyFile,
603     password,
604     &invalid_passphrase, TRUE);
605     if (pvar->auth_state.cur_cred.key_pair == NULL) {
606     return;
607     } else {
608     pvar->auth_state.cur_cred.method = SSH_AUTH_RSA;
609     }
610     break;
611     }
612    
613     case SSH_AUTH_RHOSTS:
614     if (pvar->session_settings.
615     DefaultRhostsHostPrivateKeyFile[0] != 0) {
616     BOOL invalid_passphrase;
617     char password[] = "";
618    
619     pvar->auth_state.cur_cred.key_pair
620     =
621     KEYFILES_read_private_key(pvar,
622     pvar->session_settings.
623     DefaultRhostsHostPrivateKeyFile,
624     password,
625     &invalid_passphrase, TRUE);
626     if (pvar->auth_state.cur_cred.key_pair == NULL) {
627     return;
628     } else {
629     pvar->auth_state.cur_cred.method = SSH_AUTH_RHOSTS_RSA;
630     }
631     } else {
632     pvar->auth_state.cur_cred.method = SSH_AUTH_RHOSTS;
633     }
634    
635     pvar->auth_state.cur_cred.rhosts_client_user =
636     _strdup(pvar->session_settings.DefaultRhostsLocalUserName);
637     break;
638    
639     case SSH_AUTH_PASSWORD:
640     pvar->auth_state.cur_cred.password = _strdup("");
641     pvar->auth_state.cur_cred.method = SSH_AUTH_PASSWORD;
642     break;
643    
644     case SSH_AUTH_TIS:
645     default:
646     return;
647     }
648    
649     pvar->auth_state.user =
650     _strdup(pvar->session_settings.DefaultUserName);
651     }
652     }
653    
654     void AUTH_notify_end_error(PTInstVar pvar)
655     {
656     if ((pvar->auth_state.flags & AUTH_START_USER_AUTH_ON_ERROR_END) != 0) {
657     start_user_auth(pvar);
658     pvar->auth_state.flags &= ~AUTH_START_USER_AUTH_ON_ERROR_END;
659     }
660     }
661    
662     void AUTH_advance_to_next_cred(PTInstVar pvar)
663     {
664     pvar->auth_state.failed_method = pvar->auth_state.cur_cred.method;
665    
666     if (pvar->auth_state.cur_cred.method == SSH_AUTH_NONE) {
667     try_default_auth(pvar);
668    
669     if (pvar->auth_state.cur_cred.method == SSH_AUTH_NONE) {
670     if (pvar->err_msg != NULL) {
671     pvar->auth_state.flags |=
672     AUTH_START_USER_AUTH_ON_ERROR_END;
673     } else {
674 yutakakn 2739 // �������F���_�C�A���O���o�������� (2004.12.1 yutaka)
675     // �R�}���h���C���w������������
676 yutakakn 2728 start_user_auth(pvar);
677     }
678     }
679     } else {
680 yutakakn 2739 // �������F���_�C�A���O���o�������� (2004.12.1 yutaka)
681     // �R�}���h���C���w������(/auth=xxxx)������
682 yutakakn 2728 start_user_auth(pvar);
683     }
684     }
685    
686     static void init_TIS_dlg(PTInstVar pvar, HWND dlg)
687     {
688     init_auth_machine_banner(pvar, dlg);
689     init_password_control(dlg);
690    
691     if (pvar->auth_state.TIS_prompt != NULL) {
692     if (strlen(pvar->auth_state.TIS_prompt) > 10000) {
693     pvar->auth_state.TIS_prompt[10000] = 0;
694     }
695     SetDlgItemText(dlg, IDC_SSHAUTHBANNER2,
696     pvar->auth_state.TIS_prompt);
697     destroy_malloced_string(&pvar->auth_state.TIS_prompt);
698     }
699     }
700    
701     static BOOL end_TIS_dlg(PTInstVar pvar, HWND dlg)
702     {
703     char FAR *password =
704     alloc_control_text(GetDlgItem(dlg, IDC_SSHPASSWORD));
705    
706     pvar->auth_state.cur_cred.method = SSH_AUTH_TIS;
707     pvar->auth_state.cur_cred.password = password;
708     pvar->auth_state.auth_dialog = NULL;
709    
710 yutakakn 2800 // add
711     if (SSHv2(pvar)) {
712     pvar->keyboard_interactive_password_input = 1;
713     handle_SSH2_userauth_inforeq(pvar);
714     }
715    
716 yutakakn 2728 SSH_notify_cred(pvar);
717    
718     EndDialog(dlg, 1);
719     return TRUE;
720     }
721    
722     static BOOL CALLBACK TIS_dlg_proc(HWND dlg, UINT msg, WPARAM wParam,
723     LPARAM lParam)
724     {
725     PTInstVar pvar;
726    
727     switch (msg) {
728     case WM_INITDIALOG:
729     pvar = (PTInstVar) lParam;
730     pvar->auth_state.auth_dialog = dlg;
731     SetWindowLong(dlg, DWL_USER, lParam);
732    
733     init_TIS_dlg(pvar, dlg);
734     return FALSE; /* because we set the focus */
735    
736     case WM_COMMAND:
737     pvar = (PTInstVar) GetWindowLong(dlg, DWL_USER);
738    
739     switch (LOWORD(wParam)) {
740     case IDOK:
741     return end_TIS_dlg(pvar, dlg);
742    
743     case IDCANCEL: /* kill the connection */
744     pvar->auth_state.auth_dialog = NULL;
745     notify_closed_connection(pvar);
746     EndDialog(dlg, 0);
747     return TRUE;
748    
749     default:
750     return FALSE;
751     }
752    
753     default:
754     return FALSE;
755     }
756     }
757    
758     void AUTH_do_cred_dialog(PTInstVar pvar)
759     {
760     if (pvar->auth_state.auth_dialog == NULL) {
761     HWND cur_active = GetActiveWindow();
762     DLGPROC dlg_proc;
763     LPCTSTR dialog_template;
764    
765     switch (pvar->auth_state.mode) {
766     case TIS_AUTH_MODE:
767     dialog_template = MAKEINTRESOURCE(IDD_SSHTISAUTH);
768     dlg_proc = TIS_dlg_proc;
769     break;
770     case GENERIC_AUTH_MODE:
771     default:
772     dialog_template = MAKEINTRESOURCE(IDD_SSHAUTH);
773     dlg_proc = auth_dlg_proc;
774     }
775    
776     if (!DialogBoxParam(hInst, dialog_template,
777     cur_active !=
778     NULL ? cur_active : pvar->NotificationWindow,
779     dlg_proc, (LPARAM) pvar) == -1) {
780     notify_fatal_error(pvar,
781     "Unable to display authentication dialog box.\n"
782     "Connection terminated.");
783     }
784     }
785     }
786    
787     static void init_default_auth_dlg(PTInstVar pvar, HWND dlg)
788     {
789     switch (pvar->settings.DefaultAuthMethod) {
790     case SSH_AUTH_RSA:
791     CheckRadioButton(dlg, IDC_SSHUSEPASSWORD, MAX_AUTH_CONTROL,
792     IDC_SSHUSERSA);
793     break;
794     case SSH_AUTH_RHOSTS:
795     case SSH_AUTH_RHOSTS_RSA:
796     CheckRadioButton(dlg, IDC_SSHUSEPASSWORD, MAX_AUTH_CONTROL,
797     IDC_SSHUSERHOSTS);
798     break;
799     case SSH_AUTH_TIS:
800     CheckRadioButton(dlg, IDC_SSHUSEPASSWORD, MAX_AUTH_CONTROL,
801     IDC_SSHUSETIS);
802     break;
803     case SSH_AUTH_PASSWORD:
804     default:
805     CheckRadioButton(dlg, IDC_SSHUSEPASSWORD, MAX_AUTH_CONTROL,
806     IDC_SSHUSEPASSWORD);
807     }
808    
809     SetDlgItemText(dlg, IDC_SSHUSERNAME, pvar->settings.DefaultUserName);
810     SetDlgItemText(dlg, IDC_RSAFILENAME,
811     pvar->settings.DefaultRSAPrivateKeyFile);
812     SetDlgItemText(dlg, IDC_HOSTRSAFILENAME,
813     pvar->settings.DefaultRhostsHostPrivateKeyFile);
814     SetDlgItemText(dlg, IDC_LOCALUSERNAME,
815     pvar->settings.DefaultRhostsLocalUserName);
816 yutakakn 2789
817     // SSH2 keyboard-interactive method (2005.2.22 yutaka)
818     if (pvar->settings.ssh2_keyboard_interactive) {
819     SendMessage(GetDlgItem(dlg, IDC_KEYBOARD_INTERACTIVE_CHECK), BM_SETCHECK, BST_CHECKED, 0);
820     }
821    
822 yutakakn 2728 }
823    
824     static BOOL end_default_auth_dlg(PTInstVar pvar, HWND dlg)
825     {
826     if (IsDlgButtonChecked(dlg, IDC_SSHUSERSA)) {
827     pvar->settings.DefaultAuthMethod = SSH_AUTH_RSA;
828     } else if (IsDlgButtonChecked(dlg, IDC_SSHUSERHOSTS)) {
829     if (GetWindowTextLength(GetDlgItem(dlg, IDC_HOSTRSAFILENAME)) > 0) {
830     pvar->settings.DefaultAuthMethod = SSH_AUTH_RHOSTS_RSA;
831     } else {
832     pvar->settings.DefaultAuthMethod = SSH_AUTH_RHOSTS;
833     }
834     } else if (IsDlgButtonChecked(dlg, IDC_SSHUSETIS)) {
835     pvar->settings.DefaultAuthMethod = SSH_AUTH_TIS;
836     } else {
837     pvar->settings.DefaultAuthMethod = SSH_AUTH_PASSWORD;
838     }
839    
840     GetDlgItemText(dlg, IDC_SSHUSERNAME, pvar->settings.DefaultUserName,
841     sizeof(pvar->settings.DefaultUserName));
842     GetDlgItemText(dlg, IDC_RSAFILENAME,
843     pvar->settings.DefaultRSAPrivateKeyFile,
844     sizeof(pvar->settings.DefaultRSAPrivateKeyFile));
845     GetDlgItemText(dlg, IDC_HOSTRSAFILENAME,
846     pvar->settings.DefaultRhostsHostPrivateKeyFile,
847     sizeof(pvar->settings.DefaultRhostsHostPrivateKeyFile));
848     GetDlgItemText(dlg, IDC_LOCALUSERNAME,
849     pvar->settings.DefaultRhostsLocalUserName,
850     sizeof(pvar->settings.DefaultRhostsLocalUserName));
851    
852 yutakakn 2789 // SSH2 keyboard-interactive method (2005.2.22 yutaka)
853     {
854     LRESULT ret;
855     ret = SendMessage(GetDlgItem(dlg, IDC_KEYBOARD_INTERACTIVE_CHECK), BM_GETCHECK, 0, 0);
856     if (ret & BST_CHECKED) {
857     pvar->settings.ssh2_keyboard_interactive = 1;
858     } else {
859     pvar->settings.ssh2_keyboard_interactive = 0;
860     }
861     }
862    
863 yutakakn 2728 EndDialog(dlg, 1);
864     return TRUE;
865     }
866    
867     static BOOL CALLBACK default_auth_dlg_proc(HWND dlg, UINT msg,
868     WPARAM wParam, LPARAM lParam)
869     {
870     PTInstVar pvar;
871    
872     switch (msg) {
873     case WM_INITDIALOG:
874     pvar = (PTInstVar) lParam;
875     SetWindowLong(dlg, DWL_USER, lParam);
876    
877     init_default_auth_dlg(pvar, dlg);
878     return TRUE; /* because we do not set the focus */
879    
880     case WM_COMMAND:
881     pvar = (PTInstVar) GetWindowLong(dlg, DWL_USER);
882    
883     switch (LOWORD(wParam)) {
884     case IDOK:
885     return end_default_auth_dlg(pvar, dlg);
886    
887     case IDCANCEL:
888     EndDialog(dlg, 0);
889     return TRUE;
890    
891     case IDC_CHOOSERSAFILE:
892     choose_RSA_key_file(dlg);
893     return TRUE;
894    
895     case IDC_CHOOSEHOSTRSAFILE:
896     choose_host_RSA_key_file(dlg);
897     return TRUE;
898    
899     default:
900     return FALSE;
901     }
902    
903     default:
904     return FALSE;
905     }
906     }
907    
908     void AUTH_init(PTInstVar pvar)
909     {
910     pvar->auth_state.failed_method = SSH_AUTH_NONE;
911     pvar->auth_state.auth_dialog = NULL;
912     pvar->auth_state.user = NULL;
913     pvar->auth_state.flags = 0;
914     pvar->auth_state.TIS_prompt = NULL;
915     pvar->auth_state.supported_types = 0;
916     pvar->auth_state.cur_cred.method = SSH_AUTH_NONE;
917     pvar->auth_state.cur_cred.password = NULL;
918     pvar->auth_state.cur_cred.rhosts_client_user = NULL;
919     pvar->auth_state.cur_cred.key_pair = NULL;
920     AUTH_set_generic_mode(pvar);
921     }
922    
923     void AUTH_set_generic_mode(PTInstVar pvar)
924     {
925     pvar->auth_state.mode = GENERIC_AUTH_MODE;
926     destroy_malloced_string(&pvar->auth_state.TIS_prompt);
927     }
928    
929     void AUTH_set_TIS_mode(PTInstVar pvar, char FAR * prompt, int len)
930     {
931     if (pvar->auth_state.cur_cred.method == SSH_AUTH_TIS) {
932     pvar->auth_state.mode = TIS_AUTH_MODE;
933    
934     destroy_malloced_string(&pvar->auth_state.TIS_prompt);
935     pvar->auth_state.TIS_prompt = malloc(len + 1);
936     memcpy(pvar->auth_state.TIS_prompt, prompt, len);
937     pvar->auth_state.TIS_prompt[len] = 0;
938     } else {
939     AUTH_set_generic_mode(pvar);
940     }
941     }
942    
943     void AUTH_do_default_cred_dialog(PTInstVar pvar)
944     {
945     HWND cur_active = GetActiveWindow();
946    
947     if (DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_SSHAUTHSETUP),
948     cur_active !=
949     NULL ? cur_active : pvar->NotificationWindow,
950     default_auth_dlg_proc, (LPARAM) pvar) == -1) {
951     notify_nonfatal_error(pvar,
952     "Unable to display authentication setup dialog box.");
953     }
954     }
955    
956     void AUTH_destroy_cur_cred(PTInstVar pvar)
957     {
958     destroy_malloced_string(&pvar->auth_state.cur_cred.password);
959     destroy_malloced_string(&pvar->auth_state.cur_cred.rhosts_client_user);
960     if (pvar->auth_state.cur_cred.key_pair != NULL) {
961     CRYPT_free_key_pair(pvar->auth_state.cur_cred.key_pair);
962     pvar->auth_state.cur_cred.key_pair = NULL;
963     }
964     }
965    
966     static char FAR *get_auth_method_name(SSHAuthMethod auth)
967     {
968     switch (auth) {
969     case SSH_AUTH_PASSWORD:
970     return "password";
971     case SSH_AUTH_RSA:
972     return "RSA";
973     case SSH_AUTH_RHOSTS:
974     return "rhosts";
975     case SSH_AUTH_RHOSTS_RSA:
976     return "rhosts with RSA";
977     case SSH_AUTH_TIS:
978     return "challenge/response (TIS)";
979     default:
980     return "unknown method";
981     }
982     }
983    
984     void AUTH_get_auth_info(PTInstVar pvar, char FAR * dest, int len)
985     {
986 yutakakn 2782 char *method = "unknown";
987    
988 yutakakn 2728 if (pvar->auth_state.user == NULL) {
989     strncpy(dest, "None", len);
990     } else if (pvar->auth_state.cur_cred.method != SSH_AUTH_NONE) {
991 yutakakn 2762 if (SSHv1(pvar)) {
992     _snprintf(dest, len, "User '%s', using %s", pvar->auth_state.user,
993     get_auth_method_name(pvar->auth_state.cur_cred.method));
994    
995 yutakakn 2800 } else {
996     // SSH2:�F�����\�b�h������ (2004.12.23 yutaka)
997     // keyboard-interactive���\�b�h������ (2005.3.12 yutaka)
998     if (pvar->auth_state.cur_cred.method == SSH_AUTH_PASSWORD ||
999     pvar->auth_state.cur_cred.method == SSH_AUTH_TIS) {
1000 yutakakn 2782 // keyboard-interactive���\�b�h������ (2005.1.24 yutaka)
1001 yutakakn 2800 if (pvar->keyboard_interactive_done == 1 ||
1002     pvar->auth_state.cur_cred.method == SSH_AUTH_TIS) {
1003 yutakakn 2782 method = "keyboard-interactive";
1004     } else {
1005     method = get_auth_method_name(pvar->auth_state.cur_cred.method);
1006     }
1007     _snprintf(dest, len, "User '%s', using %s", pvar->auth_state.user, method);
1008    
1009 yutakakn 2762 } else {
1010     if (pvar->auth_state.cur_cred.key_pair->RSA_key != NULL) {
1011     method = "RSA";
1012     } else if (pvar->auth_state.cur_cred.key_pair->DSA_key != NULL) {
1013     method = "DSA";
1014     }
1015     _snprintf(dest, len, "User '%s', using %s", pvar->auth_state.user, method);
1016     }
1017    
1018     }
1019    
1020 yutakakn 2728 } else {
1021     _snprintf(dest, len, "User '%s', using %s", pvar->auth_state.user,
1022     get_auth_method_name(pvar->auth_state.failed_method));
1023     }
1024    
1025     dest[len - 1] = 0;
1026     }
1027    
1028     void AUTH_notify_disconnecting(PTInstVar pvar)
1029     {
1030     if (pvar->auth_state.auth_dialog != NULL) {
1031     PostMessage(pvar->auth_state.auth_dialog, WM_COMMAND, IDCANCEL, 0);
1032     /* the main window might not go away if it's not enabled. (see vtwin.cpp) */
1033     EnableWindow(pvar->NotificationWindow, TRUE);
1034     }
1035     }
1036    
1037     void AUTH_end(PTInstVar pvar)
1038     {
1039     destroy_malloced_string(&pvar->auth_state.user);
1040     destroy_malloced_string(&pvar->auth_state.TIS_prompt);
1041    
1042     AUTH_destroy_cur_cred(pvar);
1043     }
1044 yutakakn 2739
1045     /*
1046     * $Log: not supported by cvs2svn $
1047 yutakakn 2841 * Revision 1.15 2005/07/15 14:58:04 yutakakn
1048     * SSH1���������x���[�U�F�������s�������A�������F�����������������o�O���C���B
1049     *
1050 yutakakn 2835 * Revision 1.14 2005/04/26 13:57:57 yutakakn
1051     * private key�t�@�C���_�C�A���O��3�t�@�C���t�B���^�����������B
1052     *
1053 yutakakn 2818 * Revision 1.13 2005/04/08 14:55:03 yutakakn
1054     * "Duplicate session"��������SSH�������O�C�����s�������������B
1055     *
1056 yutakakn 2811 * Revision 1.12 2005/03/23 12:39:35 yutakakn
1057     * SSH2�F���_�C�A���O�� Use challenge/response to log in ���A�N�Z�����[�^�L�[�������������B
1058     *
1059 yutakakn 2804 * Revision 1.11 2005/03/12 15:07:33 yutakakn
1060     * SSH2 keyboard-interactive�F����TIS�_�C�A���O�����������B
1061     *
1062 yutakakn 2800 * Revision 1.10 2005/03/12 12:08:05 yutakakn
1063     * �p�X���[�h�F�����O���s��keyboard-interactive���\�b�h���A�f�t�H���g�����l������(0)�������B
1064     * �����A�F���_�C�A���O�����x�������������L�����������X���������������B
1065     *
1066 yutakakn 2799 * Revision 1.9 2005/02/22 08:48:11 yutakakn
1067     * TTSSH setup�_�C�A���O�� HeartBeat �����������B
1068     * TTSSH authentication setup�_�C�A���O�� keyboard-interactive �����������B
1069     *
1070 yutakakn 2789 * Revision 1.8 2005/01/27 13:30:33 yutakakn
1071     * ���J���F���������O�C�����T�|�[�g�B
1072     * /auth=publickey, /keyfile �I�v�V�������V�K���������B
1073     * �����A�����������������T�|�[�g�B
1074     *
1075 yutakakn 2784 * Revision 1.7 2005/01/25 13:38:22 yutakakn
1076     * SSH�F���_�C�A���O���ARhosts/TIS���O���[�������O���AEnter�L�[�������������A
1077     * �A�v���P�[�V�����G���[���������������������B
1078     *
1079 yutakakn 2783 * Revision 1.6 2005/01/24 14:07:07 yutakakn
1080     * �Ekeyboard-interactive�F�����T�|�[�g�����B
1081     * �@�����������Ateraterm.ini�� "KeyboardInteractive" �G���g�������������B
1082     * �E�o�[�W�����_�C�A���O�� OpenSSL�o�[�W���� ������
1083     *
1084 yutakakn 2782 * Revision 1.5 2004/12/27 14:35:41 yutakakn
1085     * SSH2�����������������s�����G���[���b�Z�[�W�����������B
1086     *
1087 yutakakn 2769 * Revision 1.4 2004/12/22 17:28:14 yutakakn
1088     * SSH2���J���F��(RSA/DSA)���T�|�[�g�����B
1089     *
1090 yutakakn 2762 * Revision 1.3 2004/12/16 13:01:09 yutakakn
1091     * SSH�������O�C�����A�v���P�[�V�����G���[�������������C�������B
1092     *
1093 yutakakn 2752 * Revision 1.2 2004/12/01 15:37:49 yutakakn
1094     * SSH2�������O�C���@�\�������B
1095     * �����A�p�X���[�h�F�������������B
1096     * �E�R�}���h���C��
1097     * /ssh /auth=�F�����\�b�h /user=���[�U�� /passwd=�p�X���[�h
1098     *
1099 yutakakn 2739 */

Back to OSDN">Back to OSDN
ViewVC Help
Powered by ViewVC 1.1.26