Project

General

Profile

Feature #14915 » 0001-Deprecate-String-crypt-move-implementation-to-string.patch

jeremyevans0 (Jeremy Evans), 07/16/2018 05:47 PM

View differences:

common.mk
869 869

  
870 870
acosh.$(OBJEXT): {$(VPATH)}acosh.c
871 871
alloca.$(OBJEXT): {$(VPATH)}alloca.c {$(VPATH)}config.h
872
crypt.$(OBJEXT): {$(VPATH)}crypt.c {$(VPATH)}crypt.h {$(VPATH)}missing/des_tables.c
873 872
dup2.$(OBJEXT): {$(VPATH)}dup2.c
874 873
erf.$(OBJEXT): {$(VPATH)}erf.c
875 874
explicit_bzero.$(OBJEXT): {$(VPATH)}explicit_bzero.c
......
944 943
common-srcs: $(srcs_vpath)parse.c $(srcs_vpath)lex.c $(srcs_vpath)enc/trans/newline.c $(srcs_vpath)id.c \
945 944
	     srcs-lib srcs-ext incs
946 945

  
947
missing-srcs: $(srcdir)/missing/des_tables.c
948

  
949 946
srcs: common-srcs missing-srcs srcs-enc
950 947

  
951 948
EXT_SRCS = $(srcdir)/ext/ripper/ripper.c \
......
2755 2752
string.$(OBJEXT): $(hdrdir)/ruby/ruby.h
2756 2753
string.$(OBJEXT): $(top_srcdir)/include/ruby.h
2757 2754
string.$(OBJEXT): {$(VPATH)}config.h
2758
string.$(OBJEXT): {$(VPATH)}crypt.h
2759 2755
string.$(OBJEXT): {$(VPATH)}debug_counter.h
2760 2756
string.$(OBJEXT): {$(VPATH)}defines.h
2761 2757
string.$(OBJEXT): {$(VPATH)}encindex.h
configure.ac
760 760
		    test -d "$d" && RUBY_APPEND_OPTIONS(LDFLAGS, "-L$d")
761 761
		done
762 762
		ac_cv_type_getgroups=gid_t # getgroups() on Rosetta fills garbage
763
		ac_cv_lib_crypt_crypt=no
764 763
		ac_cv_func_fdatasync=no # Mac OS X wrongly reports it has fdatasync()
765 764
		ac_cv_func_vfork=no
766 765
		AS_IF([test $gcc_major -lt 4 -o \( $gcc_major -eq 4 -a $gcc_minor -lt 3 \)], [
767 766
		    ac_cv_func___builtin_setjmp=no
768 767
		])
769 768
		with_setjmp_type=sigsetjmp # to hijack SIGCHLD handler
770
		AC_CACHE_CHECK(for broken crypt with 8bit chars, rb_cv_broken_crypt,
771
		    [AC_TRY_RUN([
772
#include <stdio.h>
773
#include <unistd.h>
774
#include <string.h>
775

  
776
void
777
broken_crypt(const char *salt, const char *buf1, const char *buf2)
778
{
779
#if 0
780
    printf("%.2x%.2x: %s -> %s\n", (unsigned char)salt[0], (unsigned char)salt[1],
781
	   buf1+2, buf2+2);
782
#endif
783
}
784

  
785
int
786
main()
787
{
788
    int i;
789
    char salt[2], buf[256], *s;
790
    for (i = 0; i < 128*128; i++) {
791
	salt[0] = 0x80 | (i & 0x7f);
792
	salt[1] = 0x80 | (i >> 7);
793
	strcpy(buf, crypt("", salt));
794
	if (strcmp(buf, s = crypt("", salt))) {
795
	    broken_crypt(salt, buf, s);
796
	    return 1;
797
	}
798
    }
799
    salt[0] = salt[1] = ' ';
800
    strcpy(buf, crypt("", salt));
801
    salt[0] = salt[1] = 0x80 | ' ';
802
    if (strcmp(buf, s = crypt("", salt))) {
803
	broken_crypt(salt, buf, s);
804
	return 1;
805
    }
806
    return 0;
807
}
808
],
809
		    rb_cv_broken_crypt=no,
810
		    rb_cv_broken_crypt=yes,
811
		    rb_cv_broken_crypt=yes)])
812
		AS_IF([test "$rb_cv_broken_crypt" = yes], [
813
		   AC_DEFINE(BROKEN_CRYPT, 1)
814
		])
815 769
		POSTLINK=""
816 770
		AC_CHECK_PROGS(codesign, codesign)
817 771
		AC_CHECK_PROGS(dsymutil, dsymutil)
......
899 853
		ac_cv_func_link=yes
900 854
		ac_cv_func_readlink=yes
901 855
		ac_cv_func_symlink=yes
902
		ac_cv_lib_crypt_crypt=no
903 856
		ac_cv_func_getpgrp_void=no
904 857
		ac_cv_func_memcmp_working=yes
905 858
		ac_cv_lib_dl_dlopen=no
......
952 905
[	LIBS="-lm $LIBS"])
953 906
: ${ORIG_LIBS=$LIBS}
954 907

  
955
AC_CHECK_LIB(crypt, crypt)      # glibc (GNU/Linux, GNU/Hurd, GNU/kFreeBSD)
956 908
AC_CHECK_LIB(dl, dlopen)	# Dynamic linking for SunOS/Solaris and SYSV
957 909
AC_CHECK_LIB(dld, shl_load)	# Dynamic linking for HP-UX
958 910
AC_CHECK_LIB(socket, shutdown)  # SunOS/Solaris
......
1664 1616

  
1665 1617
AC_REPLACE_FUNCS(acosh)
1666 1618
AC_REPLACE_FUNCS(cbrt)
1667
AC_REPLACE_FUNCS(crypt)
1668 1619
AC_REPLACE_FUNCS(dup2)
1669 1620
AC_REPLACE_FUNCS(erf)
1670 1621
AC_REPLACE_FUNCS(explicit_bzero)
......
1745 1696
AC_CHECK_FUNCS(chsize)
1746 1697
AC_CHECK_FUNCS(clock_gettime)
1747 1698
AC_CHECK_FUNCS(cosh)
1748
AC_CHECK_FUNCS(crypt_r)
1749 1699
AC_CHECK_FUNCS(daemon)
1750 1700
AC_CHECK_FUNCS(dirfd)
1751 1701
AC_CHECK_FUNCS(dl_iterate_phdr)
......
1895 1845
    AS_IF([test "$rb_cv_getcwd_malloc" = no], [AC_DEFINE(NO_GETCWD_MALLOC, 1)])
1896 1846
])
1897 1847

  
1898
AS_IF([test "$ac_cv_func_crypt_r" = yes],
1899
    [AC_CHECK_HEADERS(crypt.h)])
1900
AS_IF([test "$ac_cv_func_crypt_r:$ac_cv_header_crypt_h" = yes:yes],
1901
    [AC_CHECK_MEMBERS([struct crypt_data.initialized], [], [],
1902
		      [AC_INCLUDES_DEFAULT([@%:@include <crypt.h>])])])
1903

  
1904 1848
RUBY_CHECK_BUILTIN_FUNC(__builtin_alloca_with_align, [__builtin_alloca_with_align(1, 4096)])
1905 1849
RUBY_CHECK_BUILTIN_FUNC(__builtin_assume_aligned, [__builtin_assume_aligned((void*)32, 32)])
1906 1850
RUBY_CHECK_BUILTIN_FUNC(__builtin_bswap16, [__builtin_bswap16(0)])
ext/string/crypt/crypt.c
1
#include <errno.h>
2

  
3
#include "ruby.h"
4
#include "ruby/encoding.h"
5

  
6
#if defined HAVE_CRYPT_R
7
# if defined HAVE_CRYPT_H
8
# include <crypt.h>
9
# endif
10
#elif !defined HAVE_CRYPT
11
# include "missing/crypt.h"
12
# include "missing/crypt.c"
13
# define HAVE_CRYPT_R 1
14
#endif
15

  
16
static void
17
mustnot_wchar(VALUE str)
18
{
19
    rb_encoding *enc = rb_enc_get(str);
20
    if (rb_enc_mbminlen(enc) > 1) {
21
	rb_raise(rb_eArgError, "wide char encoding: %s", rb_enc_name(enc));
22
    }
23
}
24

  
25
/*
26
 *  call-seq:
27
 *     str.crypt(salt_str)   -> new_str
28
 *
29
 *  Applies a one-way cryptographic hash to <i>str</i> by invoking the
30
 *  standard library function <code>crypt(3)</code> with the given
31
 *  salt string.  While the format and the result are system and
32
 *  implementation dependent, using a salt matching the regular
33
 *  expression <code>\A[a-zA-Z0-9./]{2}</code> should work on most
34
 *  most platforms, but on those platforms it uses insecure DES
35
 *  encryption, and only the first two characters of the salt string
36
 *  are significant.
37
 *
38
 *  This method is for use in system specific scripts, so if you want
39
 *  a cross-platform hash function consider using Digest or OpenSSL
40
 *  instead.
41
 */
42

  
43
static VALUE
44
rb_string_crypt(VALUE str, VALUE salt)
45
{
46
#ifdef HAVE_CRYPT_R
47
    VALUE databuf;
48
    struct crypt_data *data;
49
#   define CRYPT_END() ALLOCV_END(databuf)
50
#else
51
    extern char *crypt(const char *, const char *);
52
#   define CRYPT_END() (void)0
53
#endif
54
    VALUE result;
55
    const char *s, *saltp;
56
    char *res;
57
#ifdef BROKEN_CRYPT
58
    char salt_8bit_clean[3];
59
#endif
60

  
61
    StringValue(salt);
62
    mustnot_wchar(str);
63
    mustnot_wchar(salt);
64
    if (RSTRING_LEN(salt) < 2) {
65
      short_salt:
66
	rb_raise(rb_eArgError, "salt too short (need >=2 bytes)");
67
    }
68

  
69
    s = StringValueCStr(str);
70
    saltp = RSTRING_PTR(salt);
71
    if (!saltp[0] || !saltp[1]) goto short_salt;
72
#ifdef BROKEN_CRYPT
73
    if (!ISASCII((unsigned char)saltp[0]) || !ISASCII((unsigned char)saltp[1])) {
74
	salt_8bit_clean[0] = saltp[0] & 0x7f;
75
	salt_8bit_clean[1] = saltp[1] & 0x7f;
76
	salt_8bit_clean[2] = '\0';
77
	saltp = salt_8bit_clean;
78
    }
79
#endif
80
#ifdef HAVE_CRYPT_R
81
    data = ALLOCV(databuf, sizeof(struct crypt_data));
82
# ifdef HAVE_STRUCT_CRYPT_DATA_INITIALIZED
83
    data->initialized = 0;
84
# endif
85
    res = crypt_r(s, saltp, data);
86
#else
87
    res = crypt(s, saltp);
88
#endif
89
    if (!res) {
90
	int err = errno;
91
	CRYPT_END();
92
	rb_syserr_fail(err, "crypt");
93
    }
94
    result = rb_str_new_cstr(res);
95
    CRYPT_END();
96
    FL_SET_RAW(result, OBJ_TAINTED_RAW(str) | OBJ_TAINTED_RAW(salt));
97
    return result;
98
}
99

  
100
void Init_crypt(void) {
101
    rb_eval_string("class String; remove_method(:crypt) if instance_methods(false).include?(:crypt); end");
102
    rb_define_method(rb_cString, "crypt", rb_string_crypt, 1);
103
}
ext/string/crypt/extconf.rb
1
require 'mkmf'
2

  
3
if /darwin/ =~ RUBY_PLATFORM && try_run(<<END)
4
#include <stdio.h>
5
#include <unistd.h>
6
#include <string.h>
7

  
8
void
9
broken_crypt(const char *salt, const char *buf1, const char *buf2)
10
{
11
#if 0
12
    printf("%.2x%.2x: %s -> %s\n", (unsigned char)salt[0], (unsigned char)salt[1],
13
	   buf1+2, buf2+2);
14
#endif
15
}
16

  
17
int
18
main()
19
{
20
    int i;
21
    char salt[2], buf[256], *s;
22
    for (i = 0; i < 128*128; i++) {
23
	salt[0] = 0x80 | (i & 0x7f);
24
	salt[1] = 0x80 | (i >> 7);
25
	strcpy(buf, crypt("", salt));
26
	if (strcmp(buf, s = crypt("", salt))) {
27
	    broken_crypt(salt, buf, s);
28
	    return 1;
29
	}
30
    }
31
    salt[0] = salt[1] = ' ';
32
    strcpy(buf, crypt("", salt));
33
    salt[0] = salt[1] = 0x80 | ' ';
34
    if (strcmp(buf, s = crypt("", salt))) {
35
	broken_crypt(salt, buf, s);
36
	return 1;
37
    }
38
    return 0;
39
}
40
END
41
  $defs << "-DBROKEN_CRYPT"
42
end
43

  
44
if have_header('crypt.h')
45
  have_struct_member("struct crypt_data", "initialized", "crypt.h")
46
end
47

  
48
have_library('crypt', 'crypt')
49
have_func('crypt_r')
50
have_func('crypt')
51

  
52
create_makefile 'string/crypt'
missing/crypt.c → ext/string/crypt/missing/crypt.c
372 372
static void des_setkey_r(const unsigned char *key, struct crypt_data *data);
373 373
static void des_cipher_r(const unsigned char *in, unsigned char *out, long salt, int num_iter, struct crypt_data *data);
374 374

  
375
#ifdef USE_NONREENTRANT_CRYPT
376
static struct crypt_data default_crypt_data;
377
#endif
378

  
379
#ifdef USE_NONREENTRANT_CRYPT
380
/*
381
 * Return a pointer to static data consisting of the "setting"
382
 * followed by an encryption produced by the "key" and "setting".
383
 */
384
char *
385
crypt(const char *key, const char *setting)
386
{
387
	return crypt_r(key, setting, &default_crypt_data);
388
}
389
#endif
390

  
391 375
/*
392 376
 * Return a pointer to data consisting of the "setting" followed by an
393 377
 * encryption produced by the "key" and "setting".
......
792 776
}
793 777
#endif
794 778

  
795
/*
796
 * "setkey" routine (for backwards compatibility)
797
 */
798
#ifdef USE_NONREENTRANT_CRYPT
799
void
800
setkey(const char *key)
801
{
802
	setkey_r(key, &default_crypt_data);
803
}
804
#endif
805

  
806
void
807
setkey_r(const char *key, struct crypt_data *data)
808
{
809
	register int i, j, k;
810
	C_block keyblock;
811

  
812
	for (i = 0; i < 8; i++) {
813
		k = 0;
814
		for (j = 0; j < 8; j++) {
815
			k <<= 1;
816
			k |= (unsigned char)*key++;
817
		}
818
		keyblock.b[i] = k;
819
	}
820
	des_setkey_r(keyblock.b, data);
821
}
822

  
823
/*
824
 * "encrypt" routine (for backwards compatibility)
825
 */
826
#ifdef USE_NONREENTRANT_CRYPT
827
void
828
encrypt(char *block, int flag)
829
{
830
	encrypt_r(block, flag, &default_crypt_data);
831
}
832
#endif
833

  
834
void
835
encrypt_r(char *block, int flag, struct crypt_data *data)
836
{
837
	register int i, j, k;
838
	C_block cblock;
839

  
840
	for (i = 0; i < 8; i++) {
841
		k = 0;
842
		for (j = 0; j < 8; j++) {
843
			k <<= 1;
844
			k |= (unsigned char)*block++;
845
		}
846
		cblock.b[i] = k;
847
	}
848
	des_cipher_r(cblock.b, cblock.b, 0L, (flag ? -1: 1), data);
849
	for (i = 7; i >= 0; i--) {
850
		k = cblock.b[i];
851
		for (j = 7; j >= 0; j--) {
852
			*--block = k&01;
853
			k >>= 1;
854
		}
855
	}
856
}
857

  
858 779
#ifdef DEBUG
859 780
STATIC void
860 781
prtab(const char *s, const unsigned char *t, int num_rows)
missing/crypt.h → ext/string/crypt/missing/crypt.h
237 237
	char	cryptresult[1+4+4+11+1];	/* encrypted result */
238 238
};
239 239

  
240
char *crypt(const char *key, const char *setting);
241
void setkey(const char *key);
242
void encrypt(char *block, int flag);
243

  
244 240
char *crypt_r(const char *key, const char *setting, struct crypt_data *data);
245
void setkey_r(const char *key, struct crypt_data *data);
246
void encrypt_r(char *block, int flag, struct crypt_data *data);
247 241

  
248 242
#endif /* CRYPT_H */
string.c
25 25
#define BEG(no) (regs->beg[(no)])
26 26
#define END(no) (regs->end[(no)])
27 27

  
28
#include <errno.h>
29 28
#include <math.h>
30 29
#include <ctype.h>
31 30

  
......
33 32
#include <unistd.h>
34 33
#endif
35 34

  
36
#if defined HAVE_CRYPT_R
37
# if defined HAVE_CRYPT_H
38
# include <crypt.h>
39
# endif
40
#elif !defined HAVE_CRYPT
41
# include "missing/crypt.h"
42
# define HAVE_CRYPT_R 1
43
#endif
44

  
45 35
#define STRING_ENUMERATORS_WANTARRAY 0 /* next major */
46 36

  
47 37
#undef rb_str_new
......
242 232
    }
243 233
}
244 234

  
245
static void
246
mustnot_wchar(VALUE str)
247
{
248
    rb_encoding *enc = STR_ENC_GET(str);
249
    if (rb_enc_mbminlen(enc) > 1) {
250
	rb_raise(rb_eArgError, "wide char encoding: %s", rb_enc_name(enc));
251
    }
252
}
253

  
254 235
static int fstring_cmp(VALUE a, VALUE b);
255 236

  
256 237
static VALUE register_fstring(VALUE str);
......
9202 9183
 *
9203 9184
 *  Applies a one-way cryptographic hash to <i>str</i> by invoking the
9204 9185
 *  standard library function <code>crypt(3)</code> with the given
9205
 *  salt string.  While the format and the result are system and
9206
 *  implementation dependent, using a salt matching the regular
9207
 *  expression <code>\A[a-zA-Z0-9./]{2}</code> should be valid and
9208
 *  safe on any platform, in which only the first two characters are
9209
 *  significant.
9210
 *
9211
 *  This method is for use in system specific scripts, so if you want
9212
 *  a cross-platform hash function consider using Digest or OpenSSL
9213
 *  instead.
9186
 *  salt string.
9187
 *  
9188
 *  This core method is deprecated, require "string/crypt" to continue
9189
 *  using it.
9214 9190
 */
9215 9191

  
9216 9192
static VALUE
9217 9193
rb_str_crypt(VALUE str, VALUE salt)
9218 9194
{
9219
#ifdef HAVE_CRYPT_R
9220
    VALUE databuf;
9221
    struct crypt_data *data;
9222
#   define CRYPT_END() ALLOCV_END(databuf)
9223
#else
9224
    extern char *crypt(const char *, const char *);
9225
#   define CRYPT_END() (void)0
9226
#endif
9227
    VALUE result;
9228
    const char *s, *saltp;
9229
    char *res;
9230
#ifdef BROKEN_CRYPT
9231
    char salt_8bit_clean[3];
9232
#endif
9233

  
9234
    StringValue(salt);
9235
    mustnot_wchar(str);
9236
    mustnot_wchar(salt);
9237
    if (RSTRING_LEN(salt) < 2) {
9238
      short_salt:
9239
	rb_raise(rb_eArgError, "salt too short (need >=2 bytes)");
9240
    }
9241

  
9242
    s = StringValueCStr(str);
9243
    saltp = RSTRING_PTR(salt);
9244
    if (!saltp[0] || !saltp[1]) goto short_salt;
9245
#ifdef BROKEN_CRYPT
9246
    if (!ISASCII((unsigned char)saltp[0]) || !ISASCII((unsigned char)saltp[1])) {
9247
	salt_8bit_clean[0] = saltp[0] & 0x7f;
9248
	salt_8bit_clean[1] = saltp[1] & 0x7f;
9249
	salt_8bit_clean[2] = '\0';
9250
	saltp = salt_8bit_clean;
9251
    }
9252
#endif
9253
#ifdef HAVE_CRYPT_R
9254
    data = ALLOCV(databuf, sizeof(struct crypt_data));
9255
# ifdef HAVE_STRUCT_CRYPT_DATA_INITIALIZED
9256
    data->initialized = 0;
9257
# endif
9258
    res = crypt_r(s, saltp, data);
9259
#else
9260
    res = crypt(s, saltp);
9261
#endif
9262
    if (!res) {
9263
	int err = errno;
9264
	CRYPT_END();
9265
	rb_syserr_fail(err, "crypt");
9266
    }
9267
    result = rb_str_new_cstr(res);
9268
    CRYPT_END();
9269
    FL_SET_RAW(result, OBJ_TAINTED_RAW(str) | OBJ_TAINTED_RAW(salt));
9270
    return result;
9195
    rb_warn("The String#crypt core method is deprecated. " \
9196
            "require \"string/crypt\" to continue using this method.");
9197
    rb_require("string/crypt");
9198
    return rb_funcall(str, rb_intern("crypt"), 1, salt);
9271 9199
}
9272 9200

  
9273 9201

  
test/ruby/test_m17n_comb.rb
771 771
    end
772 772
  end
773 773

  
774
  private def crypt_result(str, salt)
775
    assert_warn(/\A\z|The String#crypt core method is deprecated/) do
776
      str.crypt(salt)
777
    end
778
  end
779

  
774 780
  private def confirm_crypt_result(str, salt)
775 781
    if b(salt).length < 2
776
      assert_raise(ArgumentError) { str.crypt(salt) }
782
      assert_raise(ArgumentError) { crypt_result(str, salt) }
777 783
      return
778 784
    end
779
    t = str.crypt(salt)
785
    t = crypt_result(str, salt)
780 786
    assert_equal(b(str).crypt(b(salt)), t, "#{encdump(str)}.crypt(#{encdump(salt)})")
781 787
    assert_encoding('ASCII-8BIT', t.encoding)
782 788
  end
test/ruby/test_string.rb
651 651
      skip "This sometimes fails with -DMJIT_FORCE_ENABLE. This seems important to be fixed..."
652 652
    end
653 653

  
654
    assert_equal(S('aaGUC/JkO9/Sc'), S("mypassword").crypt(S("aa")))
654
    assert_warn(/\A\z|The String#crypt core method is deprecated/) do
655
      assert_equal(S('aaGUC/JkO9/Sc'), S("mypassword").crypt(S("aa")))
656
    end
655 657
    assert_not_equal(S('aaGUC/JkO9/Sc'), S("mypassword").crypt(S("ab")))
656 658
    assert_raise(ArgumentError) {S("mypassword").crypt(S(""))}
657 659
    assert_raise(ArgumentError) {S("mypassword").crypt(S("\0a"))}
test/webrick/test_httpauth.rb
57 57
      Tempfile.create("test_webrick_auth") {|tmpfile|
58 58
        tmpfile.close
59 59
        tmp_pass = WEBrick::HTTPAuth::Htpasswd.new(tmpfile.path)
60
        tmp_pass.set_passwd(realm, "webrick", "supersecretpassword")
60
        assert_warn(/\A\z|The String#crypt core method is deprecated/) do
61
          tmp_pass.set_passwd(realm, "webrick", "supersecretpassword")
62
        end
61 63
        tmp_pass.set_passwd(realm, "foo", "supersecretpassword")
62 64
        tmp_pass.flush
63 65

  
......
117 119
      Tempfile.create("test_webrick_auth") {|tmpfile|
118 120
        tmpfile.close
119 121
        tmp_pass = WEBrick::HTTPAuth::Htpasswd.new(tmpfile.path)
120
        tmp_pass.set_passwd(realm, "webrick", "supersecretpassword")
122
        assert_warn(/\A\z|The String#crypt core method is deprecated/) do
123
          tmp_pass.set_passwd(realm, "webrick", "supersecretpassword")
124
        end
121 125
        tmp_pass.set_passwd(realm, "foo", "supersecretpassword")
122 126
        tmp_pass.flush
123 127

  
124
-