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
acosh.$(OBJEXT): {$(VPATH)}acosh.c
alloca.$(OBJEXT): {$(VPATH)}alloca.c {$(VPATH)}config.h
crypt.$(OBJEXT): {$(VPATH)}crypt.c {$(VPATH)}crypt.h {$(VPATH)}missing/des_tables.c
dup2.$(OBJEXT): {$(VPATH)}dup2.c
erf.$(OBJEXT): {$(VPATH)}erf.c
explicit_bzero.$(OBJEXT): {$(VPATH)}explicit_bzero.c
......
common-srcs: $(srcs_vpath)parse.c $(srcs_vpath)lex.c $(srcs_vpath)enc/trans/newline.c $(srcs_vpath)id.c \
srcs-lib srcs-ext incs
missing-srcs: $(srcdir)/missing/des_tables.c
srcs: common-srcs missing-srcs srcs-enc
EXT_SRCS = $(srcdir)/ext/ripper/ripper.c \
......
string.$(OBJEXT): $(hdrdir)/ruby/ruby.h
string.$(OBJEXT): $(top_srcdir)/include/ruby.h
string.$(OBJEXT): {$(VPATH)}config.h
string.$(OBJEXT): {$(VPATH)}crypt.h
string.$(OBJEXT): {$(VPATH)}debug_counter.h
string.$(OBJEXT): {$(VPATH)}defines.h
string.$(OBJEXT): {$(VPATH)}encindex.h
configure.ac
test -d "$d" && RUBY_APPEND_OPTIONS(LDFLAGS, "-L$d")
done
ac_cv_type_getgroups=gid_t # getgroups() on Rosetta fills garbage
ac_cv_lib_crypt_crypt=no
ac_cv_func_fdatasync=no # Mac OS X wrongly reports it has fdatasync()
ac_cv_func_vfork=no
AS_IF([test $gcc_major -lt 4 -o \( $gcc_major -eq 4 -a $gcc_minor -lt 3 \)], [
ac_cv_func___builtin_setjmp=no
])
with_setjmp_type=sigsetjmp # to hijack SIGCHLD handler
AC_CACHE_CHECK(for broken crypt with 8bit chars, rb_cv_broken_crypt,
[AC_TRY_RUN([
#include <stdio.h>
#include <unistd.h>
#include <string.h>
void
broken_crypt(const char *salt, const char *buf1, const char *buf2)
{
#if 0
printf("%.2x%.2x: %s -> %s\n", (unsigned char)salt[0], (unsigned char)salt[1],
buf1+2, buf2+2);
#endif
}
int
main()
{
int i;
char salt[2], buf[256], *s;
for (i = 0; i < 128*128; i++) {
salt[0] = 0x80 | (i & 0x7f);
salt[1] = 0x80 | (i >> 7);
strcpy(buf, crypt("", salt));
if (strcmp(buf, s = crypt("", salt))) {
broken_crypt(salt, buf, s);
return 1;
}
}
salt[0] = salt[1] = ' ';
strcpy(buf, crypt("", salt));
salt[0] = salt[1] = 0x80 | ' ';
if (strcmp(buf, s = crypt("", salt))) {
broken_crypt(salt, buf, s);
return 1;
}
return 0;
}
],
rb_cv_broken_crypt=no,
rb_cv_broken_crypt=yes,
rb_cv_broken_crypt=yes)])
AS_IF([test "$rb_cv_broken_crypt" = yes], [
AC_DEFINE(BROKEN_CRYPT, 1)
])
POSTLINK=""
AC_CHECK_PROGS(codesign, codesign)
AC_CHECK_PROGS(dsymutil, dsymutil)
......
ac_cv_func_link=yes
ac_cv_func_readlink=yes
ac_cv_func_symlink=yes
ac_cv_lib_crypt_crypt=no
ac_cv_func_getpgrp_void=no
ac_cv_func_memcmp_working=yes
ac_cv_lib_dl_dlopen=no
......
[ LIBS="-lm $LIBS"])
: ${ORIG_LIBS=$LIBS}
AC_CHECK_LIB(crypt, crypt) # glibc (GNU/Linux, GNU/Hurd, GNU/kFreeBSD)
AC_CHECK_LIB(dl, dlopen) # Dynamic linking for SunOS/Solaris and SYSV
AC_CHECK_LIB(dld, shl_load) # Dynamic linking for HP-UX
AC_CHECK_LIB(socket, shutdown) # SunOS/Solaris
......
AC_REPLACE_FUNCS(acosh)
AC_REPLACE_FUNCS(cbrt)
AC_REPLACE_FUNCS(crypt)
AC_REPLACE_FUNCS(dup2)
AC_REPLACE_FUNCS(erf)
AC_REPLACE_FUNCS(explicit_bzero)
......
AC_CHECK_FUNCS(chsize)
AC_CHECK_FUNCS(clock_gettime)
AC_CHECK_FUNCS(cosh)
AC_CHECK_FUNCS(crypt_r)
AC_CHECK_FUNCS(daemon)
AC_CHECK_FUNCS(dirfd)
AC_CHECK_FUNCS(dl_iterate_phdr)
......
AS_IF([test "$rb_cv_getcwd_malloc" = no], [AC_DEFINE(NO_GETCWD_MALLOC, 1)])
])
AS_IF([test "$ac_cv_func_crypt_r" = yes],
[AC_CHECK_HEADERS(crypt.h)])
AS_IF([test "$ac_cv_func_crypt_r:$ac_cv_header_crypt_h" = yes:yes],
[AC_CHECK_MEMBERS([struct crypt_data.initialized], [], [],
[AC_INCLUDES_DEFAULT([@%:@include <crypt.h>])])])
RUBY_CHECK_BUILTIN_FUNC(__builtin_alloca_with_align, [__builtin_alloca_with_align(1, 4096)])
RUBY_CHECK_BUILTIN_FUNC(__builtin_assume_aligned, [__builtin_assume_aligned((void*)32, 32)])
RUBY_CHECK_BUILTIN_FUNC(__builtin_bswap16, [__builtin_bswap16(0)])
ext/string/crypt/crypt.c
#include <errno.h>
#include "ruby.h"
#include "ruby/encoding.h"
#if defined HAVE_CRYPT_R
# if defined HAVE_CRYPT_H
# include <crypt.h>
# endif
#elif !defined HAVE_CRYPT
# include "missing/crypt.h"
# include "missing/crypt.c"
# define HAVE_CRYPT_R 1
#endif
static void
mustnot_wchar(VALUE str)
{
rb_encoding *enc = rb_enc_get(str);
if (rb_enc_mbminlen(enc) > 1) {
rb_raise(rb_eArgError, "wide char encoding: %s", rb_enc_name(enc));
}
}
/*
* call-seq:
* str.crypt(salt_str) -> new_str
*
* Applies a one-way cryptographic hash to <i>str</i> by invoking the
* standard library function <code>crypt(3)</code> with the given
* salt string. While the format and the result are system and
* implementation dependent, using a salt matching the regular
* expression <code>\A[a-zA-Z0-9./]{2}</code> should work on most
* most platforms, but on those platforms it uses insecure DES
* encryption, and only the first two characters of the salt string
* are significant.
*
* This method is for use in system specific scripts, so if you want
* a cross-platform hash function consider using Digest or OpenSSL
* instead.
*/
static VALUE
rb_string_crypt(VALUE str, VALUE salt)
{
#ifdef HAVE_CRYPT_R
VALUE databuf;
struct crypt_data *data;
# define CRYPT_END() ALLOCV_END(databuf)
#else
extern char *crypt(const char *, const char *);
# define CRYPT_END() (void)0
#endif
VALUE result;
const char *s, *saltp;
char *res;
#ifdef BROKEN_CRYPT
char salt_8bit_clean[3];
#endif
StringValue(salt);
mustnot_wchar(str);
mustnot_wchar(salt);
if (RSTRING_LEN(salt) < 2) {
short_salt:
rb_raise(rb_eArgError, "salt too short (need >=2 bytes)");
}
s = StringValueCStr(str);
saltp = RSTRING_PTR(salt);
if (!saltp[0] || !saltp[1]) goto short_salt;
#ifdef BROKEN_CRYPT
if (!ISASCII((unsigned char)saltp[0]) || !ISASCII((unsigned char)saltp[1])) {
salt_8bit_clean[0] = saltp[0] & 0x7f;
salt_8bit_clean[1] = saltp[1] & 0x7f;
salt_8bit_clean[2] = '\0';
saltp = salt_8bit_clean;
}
#endif
#ifdef HAVE_CRYPT_R
data = ALLOCV(databuf, sizeof(struct crypt_data));
# ifdef HAVE_STRUCT_CRYPT_DATA_INITIALIZED
data->initialized = 0;
# endif
res = crypt_r(s, saltp, data);
#else
res = crypt(s, saltp);
#endif
if (!res) {
int err = errno;
CRYPT_END();
rb_syserr_fail(err, "crypt");
}
result = rb_str_new_cstr(res);
CRYPT_END();
FL_SET_RAW(result, OBJ_TAINTED_RAW(str) | OBJ_TAINTED_RAW(salt));
return result;
}
void Init_crypt(void) {
rb_eval_string("class String; remove_method(:crypt) if instance_methods(false).include?(:crypt); end");
rb_define_method(rb_cString, "crypt", rb_string_crypt, 1);
}
ext/string/crypt/extconf.rb
require 'mkmf'
if /darwin/ =~ RUBY_PLATFORM && try_run(<<END)
#include <stdio.h>
#include <unistd.h>
#include <string.h>
void
broken_crypt(const char *salt, const char *buf1, const char *buf2)
{
#if 0
printf("%.2x%.2x: %s -> %s\n", (unsigned char)salt[0], (unsigned char)salt[1],
buf1+2, buf2+2);
#endif
}
int
main()
{
int i;
char salt[2], buf[256], *s;
for (i = 0; i < 128*128; i++) {
salt[0] = 0x80 | (i & 0x7f);
salt[1] = 0x80 | (i >> 7);
strcpy(buf, crypt("", salt));
if (strcmp(buf, s = crypt("", salt))) {
broken_crypt(salt, buf, s);
return 1;
}
}
salt[0] = salt[1] = ' ';
strcpy(buf, crypt("", salt));
salt[0] = salt[1] = 0x80 | ' ';
if (strcmp(buf, s = crypt("", salt))) {
broken_crypt(salt, buf, s);
return 1;
}
return 0;
}
END
$defs << "-DBROKEN_CRYPT"
end
if have_header('crypt.h')
have_struct_member("struct crypt_data", "initialized", "crypt.h")
end
have_library('crypt', 'crypt')
have_func('crypt_r')
have_func('crypt')
create_makefile 'string/crypt'
missing/crypt.c → ext/string/crypt/missing/crypt.c
static void des_setkey_r(const unsigned char *key, struct crypt_data *data);
static void des_cipher_r(const unsigned char *in, unsigned char *out, long salt, int num_iter, struct crypt_data *data);
#ifdef USE_NONREENTRANT_CRYPT
static struct crypt_data default_crypt_data;
#endif
#ifdef USE_NONREENTRANT_CRYPT
/*
* Return a pointer to static data consisting of the "setting"
* followed by an encryption produced by the "key" and "setting".
*/
char *
crypt(const char *key, const char *setting)
{
return crypt_r(key, setting, &default_crypt_data);
}
#endif
/*
* Return a pointer to data consisting of the "setting" followed by an
* encryption produced by the "key" and "setting".
......
}
#endif
/*
* "setkey" routine (for backwards compatibility)
*/
#ifdef USE_NONREENTRANT_CRYPT
void
setkey(const char *key)
{
setkey_r(key, &default_crypt_data);
}
#endif
void
setkey_r(const char *key, struct crypt_data *data)
{
register int i, j, k;
C_block keyblock;
for (i = 0; i < 8; i++) {
k = 0;
for (j = 0; j < 8; j++) {
k <<= 1;
k |= (unsigned char)*key++;
}
keyblock.b[i] = k;
}
des_setkey_r(keyblock.b, data);
}
/*
* "encrypt" routine (for backwards compatibility)
*/
#ifdef USE_NONREENTRANT_CRYPT
void
encrypt(char *block, int flag)
{
encrypt_r(block, flag, &default_crypt_data);
}
#endif
void
encrypt_r(char *block, int flag, struct crypt_data *data)
{
register int i, j, k;
C_block cblock;
for (i = 0; i < 8; i++) {
k = 0;
for (j = 0; j < 8; j++) {
k <<= 1;
k |= (unsigned char)*block++;
}
cblock.b[i] = k;
}
des_cipher_r(cblock.b, cblock.b, 0L, (flag ? -1: 1), data);
for (i = 7; i >= 0; i--) {
k = cblock.b[i];
for (j = 7; j >= 0; j--) {
*--block = k&01;
k >>= 1;
}
}
}
#ifdef DEBUG
STATIC void
prtab(const char *s, const unsigned char *t, int num_rows)
missing/crypt.h → ext/string/crypt/missing/crypt.h
char cryptresult[1+4+4+11+1]; /* encrypted result */
};
char *crypt(const char *key, const char *setting);
void setkey(const char *key);
void encrypt(char *block, int flag);
char *crypt_r(const char *key, const char *setting, struct crypt_data *data);
void setkey_r(const char *key, struct crypt_data *data);
void encrypt_r(char *block, int flag, struct crypt_data *data);
#endif /* CRYPT_H */
string.c
#define BEG(no) (regs->beg[(no)])
#define END(no) (regs->end[(no)])
#include <errno.h>
#include <math.h>
#include <ctype.h>
......
#include <unistd.h>
#endif
#if defined HAVE_CRYPT_R
# if defined HAVE_CRYPT_H
# include <crypt.h>
# endif
#elif !defined HAVE_CRYPT
# include "missing/crypt.h"
# define HAVE_CRYPT_R 1
#endif
#define STRING_ENUMERATORS_WANTARRAY 0 /* next major */
#undef rb_str_new
......
}
}
static void
mustnot_wchar(VALUE str)
{
rb_encoding *enc = STR_ENC_GET(str);
if (rb_enc_mbminlen(enc) > 1) {
rb_raise(rb_eArgError, "wide char encoding: %s", rb_enc_name(enc));
}
}
static int fstring_cmp(VALUE a, VALUE b);
static VALUE register_fstring(VALUE str);
......
*
* Applies a one-way cryptographic hash to <i>str</i> by invoking the
* standard library function <code>crypt(3)</code> with the given
* salt string. While the format and the result are system and
* implementation dependent, using a salt matching the regular
* expression <code>\A[a-zA-Z0-9./]{2}</code> should be valid and
* safe on any platform, in which only the first two characters are
* significant.
*
* This method is for use in system specific scripts, so if you want
* a cross-platform hash function consider using Digest or OpenSSL
* instead.
* salt string.
*
* This core method is deprecated, require "string/crypt" to continue
* using it.
*/
static VALUE
rb_str_crypt(VALUE str, VALUE salt)
{
#ifdef HAVE_CRYPT_R
VALUE databuf;
struct crypt_data *data;
# define CRYPT_END() ALLOCV_END(databuf)
#else
extern char *crypt(const char *, const char *);
# define CRYPT_END() (void)0
#endif
VALUE result;
const char *s, *saltp;
char *res;
#ifdef BROKEN_CRYPT
char salt_8bit_clean[3];
#endif
StringValue(salt);
mustnot_wchar(str);
mustnot_wchar(salt);
if (RSTRING_LEN(salt) < 2) {
short_salt:
rb_raise(rb_eArgError, "salt too short (need >=2 bytes)");
}
s = StringValueCStr(str);
saltp = RSTRING_PTR(salt);
if (!saltp[0] || !saltp[1]) goto short_salt;
#ifdef BROKEN_CRYPT
if (!ISASCII((unsigned char)saltp[0]) || !ISASCII((unsigned char)saltp[1])) {
salt_8bit_clean[0] = saltp[0] & 0x7f;
salt_8bit_clean[1] = saltp[1] & 0x7f;
salt_8bit_clean[2] = '\0';
saltp = salt_8bit_clean;
}
#endif
#ifdef HAVE_CRYPT_R
data = ALLOCV(databuf, sizeof(struct crypt_data));
# ifdef HAVE_STRUCT_CRYPT_DATA_INITIALIZED
data->initialized = 0;
# endif
res = crypt_r(s, saltp, data);
#else
res = crypt(s, saltp);
#endif
if (!res) {
int err = errno;
CRYPT_END();
rb_syserr_fail(err, "crypt");
}
result = rb_str_new_cstr(res);
CRYPT_END();
FL_SET_RAW(result, OBJ_TAINTED_RAW(str) | OBJ_TAINTED_RAW(salt));
return result;
rb_warn("The String#crypt core method is deprecated. " \
"require \"string/crypt\" to continue using this method.");
rb_require("string/crypt");
return rb_funcall(str, rb_intern("crypt"), 1, salt);
}
test/ruby/test_m17n_comb.rb
end
end
private def crypt_result(str, salt)
assert_warn(/\A\z|The String#crypt core method is deprecated/) do
str.crypt(salt)
end
end
private def confirm_crypt_result(str, salt)
if b(salt).length < 2
assert_raise(ArgumentError) { str.crypt(salt) }
assert_raise(ArgumentError) { crypt_result(str, salt) }
return
end
t = str.crypt(salt)
t = crypt_result(str, salt)
assert_equal(b(str).crypt(b(salt)), t, "#{encdump(str)}.crypt(#{encdump(salt)})")
assert_encoding('ASCII-8BIT', t.encoding)
end
test/ruby/test_string.rb
skip "This sometimes fails with -DMJIT_FORCE_ENABLE. This seems important to be fixed..."
end
assert_equal(S('aaGUC/JkO9/Sc'), S("mypassword").crypt(S("aa")))
assert_warn(/\A\z|The String#crypt core method is deprecated/) do
assert_equal(S('aaGUC/JkO9/Sc'), S("mypassword").crypt(S("aa")))
end
assert_not_equal(S('aaGUC/JkO9/Sc'), S("mypassword").crypt(S("ab")))
assert_raise(ArgumentError) {S("mypassword").crypt(S(""))}
assert_raise(ArgumentError) {S("mypassword").crypt(S("\0a"))}
test/webrick/test_httpauth.rb
Tempfile.create("test_webrick_auth") {|tmpfile|
tmpfile.close
tmp_pass = WEBrick::HTTPAuth::Htpasswd.new(tmpfile.path)
tmp_pass.set_passwd(realm, "webrick", "supersecretpassword")
assert_warn(/\A\z|The String#crypt core method is deprecated/) do
tmp_pass.set_passwd(realm, "webrick", "supersecretpassword")
end
tmp_pass.set_passwd(realm, "foo", "supersecretpassword")
tmp_pass.flush
......
Tempfile.create("test_webrick_auth") {|tmpfile|
tmpfile.close
tmp_pass = WEBrick::HTTPAuth::Htpasswd.new(tmpfile.path)
tmp_pass.set_passwd(realm, "webrick", "supersecretpassword")
assert_warn(/\A\z|The String#crypt core method is deprecated/) do
tmp_pass.set_passwd(realm, "webrick", "supersecretpassword")
end
tmp_pass.set_passwd(realm, "foo", "supersecretpassword")
tmp_pass.flush
(1-1/5)