Deleted Added
full compact
security.c (178826) security.c (233294)
1/*
1/*
2 * Copyright (c) 1998-2002, 2005 Kungliga Tekniska H�gskolan
2 * Copyright (c) 1998-2002, 2005 Kungliga Tekniska H�gskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 *
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
16 *
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
17 * 3. Neither the name of the Institute nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifdef FTP_SERVER
35#include "ftpd_locl.h"
36#else
37#include "ftp_locl.h"
38#endif
39
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifdef FTP_SERVER
35#include "ftpd_locl.h"
36#else
37#include "ftp_locl.h"
38#endif
39
40RCSID("$Id: security.c 21225 2007-06-20 10:16:02Z lha $");
40RCSID("$Id$");
41
42static enum protection_level command_prot;
43static enum protection_level data_prot;
44static size_t buffer_size;
45
46struct buffer {
47 void *data;
48 size_t size;

--- 20 unchanged lines hidden (view full) ---

69 int i;
70 for(i = 0; i < sizeof(level_names) / sizeof(level_names[0]); i++)
71 if(level_names[i].level == level)
72 return level_names[i].name;
73 return "unknown";
74}
75
76#ifndef FTP_SERVER /* not used in server */
41
42static enum protection_level command_prot;
43static enum protection_level data_prot;
44static size_t buffer_size;
45
46struct buffer {
47 void *data;
48 size_t size;

--- 20 unchanged lines hidden (view full) ---

69 int i;
70 for(i = 0; i < sizeof(level_names) / sizeof(level_names[0]); i++)
71 if(level_names[i].level == level)
72 return level_names[i].name;
73 return "unknown";
74}
75
76#ifndef FTP_SERVER /* not used in server */
77static enum protection_level
77static enum protection_level
78name_to_level(const char *name)
79{
80 int i;
81 for(i = 0; i < sizeof(level_names) / sizeof(level_names[0]); i++)
82 if(!strncasecmp(level_names[i].name, name, strlen(name)))
83 return level_names[i].level;
78name_to_level(const char *name)
79{
80 int i;
81 for(i = 0; i < sizeof(level_names) / sizeof(level_names[0]); i++)
82 if(!strncasecmp(level_names[i].name, name, strlen(name)))
83 return level_names[i].level;
84 return (enum protection_level)-1;
84 return prot_invalid;
85}
86#endif
87
88#ifdef FTP_SERVER
89
90static struct sec_server_mech *mechs[] = {
91#ifdef KRB5
92 &gss_server_mech,
93#endif
85}
86#endif
87
88#ifdef FTP_SERVER
89
90static struct sec_server_mech *mechs[] = {
91#ifdef KRB5
92 &gss_server_mech,
93#endif
94#ifdef KRB4
95 &krb4_server_mech,
96#endif
97 NULL
98};
99
100static struct sec_server_mech *mech;
101
102#else
103
104static struct sec_client_mech *mechs[] = {
105#ifdef KRB5
106 &gss_client_mech,
107#endif
94 NULL
95};
96
97static struct sec_server_mech *mech;
98
99#else
100
101static struct sec_client_mech *mechs[] = {
102#ifdef KRB5
103 &gss_client_mech,
104#endif
108#ifdef KRB4
109 &krb4_client_mech,
110#endif
111 NULL
112};
113
114static struct sec_client_mech *mech;
115
116#endif
117
118static void *app_data;

--- 105 unchanged lines hidden (view full) ---

224
225 if(sec_complete == 0 || data_prot == 0)
226 return read(fd, dataptr, length);
227
228 if(in_buffer.eof_flag){
229 in_buffer.eof_flag = 0;
230 return 0;
231 }
105 NULL
106};
107
108static struct sec_client_mech *mech;
109
110#endif
111
112static void *app_data;

--- 105 unchanged lines hidden (view full) ---

218
219 if(sec_complete == 0 || data_prot == 0)
220 return read(fd, dataptr, length);
221
222 if(in_buffer.eof_flag){
223 in_buffer.eof_flag = 0;
224 return 0;
225 }
232
226
233 len = buffer_read(&in_buffer, dataptr, length);
234 length -= len;
235 rx += len;
236 dataptr = (char*)dataptr + len;
227 len = buffer_read(&in_buffer, dataptr, length);
228 length -= len;
229 rx += len;
230 dataptr = (char*)dataptr + len;
237
231
238 while(length){
239 int ret;
240
241 ret = sec_get_data(fd, &in_buffer, data_prot);
242 if (ret < 0)
243 return -1;
244 if(ret == 0 && in_buffer.size == 0) {
245 if(rx)

--- 35 unchanged lines hidden (view full) ---

281 return 0;
282}
283
284int
285sec_write(int fd, char *dataptr, int length)
286{
287 int len = buffer_size;
288 int tx = 0;
232 while(length){
233 int ret;
234
235 ret = sec_get_data(fd, &in_buffer, data_prot);
236 if (ret < 0)
237 return -1;
238 if(ret == 0 && in_buffer.size == 0) {
239 if(rx)

--- 35 unchanged lines hidden (view full) ---

275 return 0;
276}
277
278int
279sec_write(int fd, char *dataptr, int length)
280{
281 int len = buffer_size;
282 int tx = 0;
289
283
290 if(data_prot == prot_clear)
291 return write(fd, dataptr, length);
292
293 len -= (*mech->overhead)(app_data, data_prot, len);
294 while(length){
295 if(length < len)
296 len = length;
297 sec_send(fd, dataptr, len);

--- 34 unchanged lines hidden (view full) ---

332}
333
334int
335sec_putc(int c, FILE *F)
336{
337 char ch = c;
338 if(data_prot == prot_clear)
339 return putc(c, F);
284 if(data_prot == prot_clear)
285 return write(fd, dataptr, length);
286
287 len -= (*mech->overhead)(app_data, data_prot, len);
288 while(length){
289 if(length < len)
290 len = length;
291 sec_send(fd, dataptr, len);

--- 34 unchanged lines hidden (view full) ---

326}
327
328int
329sec_putc(int c, FILE *F)
330{
331 char ch = c;
332 if(data_prot == prot_clear)
333 return putc(c, F);
340
334
341 buffer_write(&out_buffer, &ch, 1);
342 if(c == '\n' || out_buffer.index >= 1024 /* XXX */) {
343 sec_write(fileno(F), out_buffer.data, out_buffer.index);
344 out_buffer.index = 0;
345 }
346 return c;
347}
348
349int
350sec_read_msg(char *s, int level)
351{
352 int len;
353 char *buf;
354 int return_code;
335 buffer_write(&out_buffer, &ch, 1);
336 if(c == '\n' || out_buffer.index >= 1024 /* XXX */) {
337 sec_write(fileno(F), out_buffer.data, out_buffer.index);
338 out_buffer.index = 0;
339 }
340 return c;
341}
342
343int
344sec_read_msg(char *s, int level)
345{
346 int len;
347 char *buf;
348 int return_code;
355
349
356 buf = malloc(strlen(s));
357 len = base64_decode(s + 4, buf); /* XXX */
350 buf = malloc(strlen(s));
351 len = base64_decode(s + 4, buf); /* XXX */
358
352
359 len = (*mech->decode)(app_data, buf, len, level);
360 if(len < 0)
361 return -1;
353 len = (*mech->decode)(app_data, buf, len, level);
354 if(len < 0)
355 return -1;
362
356
363 buf[len] = '\0';
364
365 if(buf[3] == '-')
366 return_code = 0;
367 else
368 sscanf(buf, "%d", &return_code);
369 if(buf[len-1] == '\n')
370 buf[len-1] = '\0';

--- 5 unchanged lines hidden (view full) ---

376int
377sec_vfprintf(FILE *f, const char *fmt, va_list ap)
378{
379 char *buf;
380 void *enc;
381 int len;
382 if(!sec_complete)
383 return vfprintf(f, fmt, ap);
357 buf[len] = '\0';
358
359 if(buf[3] == '-')
360 return_code = 0;
361 else
362 sscanf(buf, "%d", &return_code);
363 if(buf[len-1] == '\n')
364 buf[len-1] = '\0';

--- 5 unchanged lines hidden (view full) ---

370int
371sec_vfprintf(FILE *f, const char *fmt, va_list ap)
372{
373 char *buf;
374 void *enc;
375 int len;
376 if(!sec_complete)
377 return vfprintf(f, fmt, ap);
384
378
385 if (vasprintf(&buf, fmt, ap) == -1) {
386 printf("Failed to allocate command.\n");
387 return -1;
388 }
389 len = (*mech->encode)(app_data, buf, strlen(buf), command_prot, &enc);
390 free(buf);
391 if(len < 0) {
392 printf("Failed to encode command.\n");

--- 122 unchanged lines hidden (view full) ---

515 else if(!strcasecmp(pl, "E"))
516 p = prot_confidential;
517 else if(!strcasecmp(pl, "P"))
518 p = prot_private;
519 else {
520 reply(504, "Unrecognized protection level.");
521 return;
522 }
379 if (vasprintf(&buf, fmt, ap) == -1) {
380 printf("Failed to allocate command.\n");
381 return -1;
382 }
383 len = (*mech->encode)(app_data, buf, strlen(buf), command_prot, &enc);
384 free(buf);
385 if(len < 0) {
386 printf("Failed to encode command.\n");

--- 122 unchanged lines hidden (view full) ---

509 else if(!strcasecmp(pl, "E"))
510 p = prot_confidential;
511 else if(!strcasecmp(pl, "P"))
512 p = prot_private;
513 else {
514 reply(504, "Unrecognized protection level.");
515 return;
516 }
523
517
524 if(sec_complete){
525 if((*mech->check_prot)(app_data, p)){
518 if(sec_complete){
519 if((*mech->check_prot)(app_data, p)){
526 reply(536, "%s does not support %s protection.",
520 reply(536, "%s does not support %s protection.",
527 mech->name, level_to_name(p));
528 }else{
529 data_prot = (enum protection_level)p;
530 reply(200, "Data protection is %s.", level_to_name(p));
531 }
532 }else{
533 reply(503, "Incomplete security data exchange.");
534 }

--- 16 unchanged lines hidden (view full) ---

551 void *buf;
552 size_t len, buf_size;
553 if(!sec_complete) {
554 reply(503, "Incomplete security data exchange.");
555 return;
556 }
557 buf_size = strlen(msg) + 2;
558 buf = malloc(buf_size);
521 mech->name, level_to_name(p));
522 }else{
523 data_prot = (enum protection_level)p;
524 reply(200, "Data protection is %s.", level_to_name(p));
525 }
526 }else{
527 reply(503, "Incomplete security data exchange.");
528 }

--- 16 unchanged lines hidden (view full) ---

545 void *buf;
546 size_t len, buf_size;
547 if(!sec_complete) {
548 reply(503, "Incomplete security data exchange.");
549 return;
550 }
551 buf_size = strlen(msg) + 2;
552 buf = malloc(buf_size);
553 if (buf == NULL) {
554 reply(501, "Failed to allocate %lu", (unsigned long)buf_size);
555 return;
556 }
559 len = base64_decode(msg, buf);
560 command_prot = level;
561 if(len == (size_t)-1) {
557 len = base64_decode(msg, buf);
558 command_prot = level;
559 if(len == (size_t)-1) {
560 free(buf);
562 reply(501, "Failed to base64-decode command");
563 return;
564 }
565 len = (*mech->decode)(app_data, buf, len, level);
566 if(len == (size_t)-1) {
561 reply(501, "Failed to base64-decode command");
562 return;
563 }
564 len = (*mech->decode)(app_data, buf, len, level);
565 if(len == (size_t)-1) {
566 free(buf);
567 reply(535, "Failed to decode command");
568 return;
569 }
570 ((char*)buf)[len] = '\0';
571 if(strstr((char*)buf, "\r\n") == NULL)
572 strlcat((char*)buf, "\r\n", buf_size);
573 new_ftp_command(buf);
574}

--- 48 unchanged lines hidden (view full) ---

623void
624sec_status(void)
625{
626 if(sec_complete){
627 printf("Using %s for authentication.\n", mech->name);
628 printf("Using %s command channel.\n", level_to_name(command_prot));
629 printf("Using %s data channel.\n", level_to_name(data_prot));
630 if(buffer_size > 0)
567 reply(535, "Failed to decode command");
568 return;
569 }
570 ((char*)buf)[len] = '\0';
571 if(strstr((char*)buf, "\r\n") == NULL)
572 strlcat((char*)buf, "\r\n", buf_size);
573 new_ftp_command(buf);
574}

--- 48 unchanged lines hidden (view full) ---

623void
624sec_status(void)
625{
626 if(sec_complete){
627 printf("Using %s for authentication.\n", mech->name);
628 printf("Using %s command channel.\n", level_to_name(command_prot));
629 printf("Using %s data channel.\n", level_to_name(data_prot));
630 if(buffer_size > 0)
631 printf("Protection buffer size: %lu.\n",
631 printf("Protection buffer size: %lu.\n",
632 (unsigned long)buffer_size);
633 }else{
634 printf("Not using any security mechanism.\n");
635 }
636}
637
638static int
639sec_prot_internal(int level)

--- 24 unchanged lines hidden (view full) ---

664 buffer_size = s;
665 }
666 verbose = old_verbose;
667 ret = command("PROT %c", level["CSEP"]); /* XXX :-) */
668 if(ret != COMPLETE){
669 printf("Failed to set protection level.\n");
670 return -1;
671 }
632 (unsigned long)buffer_size);
633 }else{
634 printf("Not using any security mechanism.\n");
635 }
636}
637
638static int
639sec_prot_internal(int level)

--- 24 unchanged lines hidden (view full) ---

664 buffer_size = s;
665 }
666 verbose = old_verbose;
667 ret = command("PROT %c", level["CSEP"]); /* XXX :-) */
668 if(ret != COMPLETE){
669 printf("Failed to set protection level.\n");
670 return -1;
671 }
672
672
673 data_prot = (enum protection_level)level;
674 return 0;
675}
676
677enum protection_level
678set_command_prot(enum protection_level level)
679{
680 int ret;
681 enum protection_level old = command_prot;
682 if(level != command_prot && level == prot_clear) {
683 ret = command("CCC");
684 if(ret != COMPLETE) {
685 printf("Failed to clear command channel.\n");
673 data_prot = (enum protection_level)level;
674 return 0;
675}
676
677enum protection_level
678set_command_prot(enum protection_level level)
679{
680 int ret;
681 enum protection_level old = command_prot;
682 if(level != command_prot && level == prot_clear) {
683 ret = command("CCC");
684 if(ret != COMPLETE) {
685 printf("Failed to clear command channel.\n");
686 return -1;
686 return prot_invalid;
687 }
688 }
689 command_prot = level;
690 return old;
691}
692
693void
694sec_prot(int argc, char **argv)

--- 8 unchanged lines hidden (view full) ---

703 return;
704 }
705 if(!sec_complete) {
706 printf("No security data exchange has taken place.\n");
707 code = -1;
708 return;
709 }
710 level = name_to_level(argv[argc - 1]);
687 }
688 }
689 command_prot = level;
690 return old;
691}
692
693void
694sec_prot(int argc, char **argv)

--- 8 unchanged lines hidden (view full) ---

703 return;
704 }
705 if(!sec_complete) {
706 printf("No security data exchange has taken place.\n");
707 code = -1;
708 return;
709 }
710 level = name_to_level(argv[argc - 1]);
711
711
712 if(level == -1)
713 goto usage;
712 if(level == -1)
713 goto usage;
714
714
715 if((*mech->check_prot)(app_data, level)) {
715 if((*mech->check_prot)(app_data, level)) {
716 printf("%s does not implement %s protection.\n",
716 printf("%s does not implement %s protection.\n",
717 mech->name, level_to_name(level));
718 code = -1;
719 return;
720 }
717 mech->name, level_to_name(level));
718 code = -1;
719 return;
720 }
721
721
722 if(argc == 2 || strncasecmp(argv[1], "data", strlen(argv[1])) == 0) {
723 if(sec_prot_internal(level) < 0){
724 code = -1;
725 return;
726 }
727 } else if(strncasecmp(argv[1], "command", strlen(argv[1])) == 0) {
728 if(set_command_prot(level) < 0) {
729 code = -1;

--- 24 unchanged lines hidden (view full) ---

754 }
755
756 if(argc == 1) {
757 sec_status();
758 } else {
759 level = name_to_level(argv[1]);
760 if(level == -1)
761 goto usage;
722 if(argc == 2 || strncasecmp(argv[1], "data", strlen(argv[1])) == 0) {
723 if(sec_prot_internal(level) < 0){
724 code = -1;
725 return;
726 }
727 } else if(strncasecmp(argv[1], "command", strlen(argv[1])) == 0) {
728 if(set_command_prot(level) < 0) {
729 code = -1;

--- 24 unchanged lines hidden (view full) ---

754 }
755
756 if(argc == 1) {
757 sec_status();
758 } else {
759 level = name_to_level(argv[1]);
760 if(level == -1)
761 goto usage;
762
762
763 if((*mech->check_prot)(app_data, level)) {
763 if((*mech->check_prot)(app_data, level)) {
764 printf("%s does not implement %s protection.\n",
764 printf("%s does not implement %s protection.\n",
765 mech->name, level_to_name(level));
766 code = -1;
767 return;
768 }
769 if(set_command_prot(level) < 0) {
770 code = -1;
771 return;
772 }

--- 30 unchanged lines hidden (view full) ---

803sec_login(char *host)
804{
805 int ret;
806 struct sec_client_mech **m;
807 int old_verbose = verbose;
808
809 verbose = -1; /* shut up all messages this will produce (they
810 are usually not very user friendly) */
765 mech->name, level_to_name(level));
766 code = -1;
767 return;
768 }
769 if(set_command_prot(level) < 0) {
770 code = -1;
771 return;
772 }

--- 30 unchanged lines hidden (view full) ---

803sec_login(char *host)
804{
805 int ret;
806 struct sec_client_mech **m;
807 int old_verbose = verbose;
808
809 verbose = -1; /* shut up all messages this will produce (they
810 are usually not very user friendly) */
811
811
812 for(m = mechs; *m && (*m)->name; m++) {
813 void *tmp;
814
815 tmp = realloc(app_data, (*m)->size);
816 if (tmp == NULL) {
817 warnx ("realloc %lu failed", (unsigned long)(*m)->size);
818 return -1;
819 }
820 app_data = tmp;
812 for(m = mechs; *m && (*m)->name; m++) {
813 void *tmp;
814
815 tmp = realloc(app_data, (*m)->size);
816 if (tmp == NULL) {
817 warnx ("realloc %lu failed", (unsigned long)(*m)->size);
818 return -1;
819 }
820 app_data = tmp;
821
821
822 if((*m)->init && (*(*m)->init)(app_data) != 0) {
823 printf("Skipping %s...\n", (*m)->name);
824 continue;
825 }
826 printf("Trying %s...\n", (*m)->name);
827 ret = command("AUTH %s", (*m)->name);
828 if(ret != CONTINUE){
829 if(code == 504){

--- 5 unchanged lines hidden (view full) ---

835 "security extensions.\n");
836 verbose = old_verbose;
837 return -1;
838 }
839 continue;
840 }
841
842 ret = (*(*m)->auth)(app_data, host);
822 if((*m)->init && (*(*m)->init)(app_data) != 0) {
823 printf("Skipping %s...\n", (*m)->name);
824 continue;
825 }
826 printf("Trying %s...\n", (*m)->name);
827 ret = command("AUTH %s", (*m)->name);
828 if(ret != CONTINUE){
829 if(code == 504){

--- 5 unchanged lines hidden (view full) ---

835 "security extensions.\n");
836 verbose = old_verbose;
837 return -1;
838 }
839 continue;
840 }
841
842 ret = (*(*m)->auth)(app_data, host);
843
843
844 if(ret == AUTH_CONTINUE)
845 continue;
846 else if(ret != AUTH_OK){
847 /* mechanism is supposed to output error string */
848 verbose = old_verbose;
849 return -1;
850 }
851 mech = *m;
852 sec_complete = 1;
853 if(doencrypt) {
854 command_prot = prot_private;
844 if(ret == AUTH_CONTINUE)
845 continue;
846 else if(ret != AUTH_OK){
847 /* mechanism is supposed to output error string */
848 verbose = old_verbose;
849 return -1;
850 }
851 mech = *m;
852 sec_complete = 1;
853 if(doencrypt) {
854 command_prot = prot_private;
855 request_data_prot = prot_private;
855 request_data_prot = prot_private;
856 } else {
857 command_prot = prot_safe;
858 }
859 break;
860 }
856 } else {
857 command_prot = prot_safe;
858 }
859 break;
860 }
861
861
862 verbose = old_verbose;
863 return *m == NULL;
864}
865
866void
867sec_end(void)
868{
869 if (mech != NULL) {

--- 14 unchanged lines hidden ---
862 verbose = old_verbose;
863 return *m == NULL;
864}
865
866void
867sec_end(void)
868{
869 if (mech != NULL) {

--- 14 unchanged lines hidden ---