对Linux svn保存的明文密码加密

需求来源:

     随着GitHub/GitLab的兴起,svn已经渐渐的没落了,从公司当初的源代码管理服务器,逐渐演变成公司的ftp服务器。

    最近需要部署gitlab的CI单元测试模块,而软件版本都在svn上有备份,我就希望从代码的提交->到版本的打包->再到版本的单元测试->安装包上传svn准备发布,整个过程自动化完成。这样会大大的提高生产力。

遇到的问题:

    因为,Linux上,svn的客户端用的是subversion,而对于Ubuntu16.04 Server版本,subversion是将用户名和用户密码,用明文保存在/home/guoke/.subversion/auth/svn.simple/a8rg45s3...文件里面的,内容类似:

K 8
passtype
V 6
simple
K 8
password
V 7
1234567
K 15
svn:realmstring
V 43
<http://192.168.5.73:9808> VisualSVN Server
K 8
username
V 5
guoke
END

可以看到svn的密码是'1234567',这个就比较危险了,如果是自己用的机器还好,如果是大家公用的机器,那就不好了。因为我想在git-runner的docker容器内部,自动下载/上传svn文件,并且不想明文保存密码。于是在网上搜了一圈,发现大家对此都没办法。那我能怎么办?我还能怎么办?改subversion的源代码呗。

   先从网上下载一个http://archive.ubuntu.com/ubuntu/pool/main/s/subversion/subversion_1.9.3.orig.tar.gz源码包。

解压后,就是编译三部曲:

cd subversion-1.9.3
sh get-deps.sh
./configure --prefix=/home/guoke/subversion-1.9.3/build_dir_debug --with-apr=/home/guoke/subversion-1.9.3/apr/build_dir --with-apr-util=/home/guoke/subversion-1.9.3/apr-util/build_dir --with-serf --enable-debug
make
make install

编译好后,随便用命令测试一下,

./svn export --username guoke --password 1234567 http://192.168.5.73:9808/svn/RD/test.txt

发现用户名和密码会被成功保存到/home/guoke/.subversion/auth/svn.simple/目录下面。

自己编译的svn客户端已经跑通了,接下来就是漫长的阅读源代码和查找读取密码文件的函数位置,功夫不负苦心人,结果在

./subversion-1.9.3/subversion/libsvn_subr/simple_providers.c找到了svn_auth__simple_password_get函数:


/* Implementation of svn_auth__password_get_t that retrieves
   the plaintext password from CREDS. */
svn_error_t *
svn_auth__simple_password_get(svn_boolean_t *done,
                              const char **password,
                              apr_hash_t *creds,
                              const char *realmstring,
                              const char *username,
                              apr_hash_t *parameters,
                              svn_boolean_t non_interactive,
                              apr_pool_t *pool)
{
  svn_string_t *str;

  *done = FALSE;

  str = svn_hash_gets(creds, SVN_CONFIG_AUTHN_USERNAME_KEY);
  if (str && username && strcmp(str->data, username) == 0)
    {
      str = svn_hash_gets(creds, SVN_CONFIG_AUTHN_PASSWORD_KEY);
      if (str && str->data)
        {
          *password = str->data;
          *done = TRUE;
        }
    }

  return SVN_NO_ERROR;
}

解决问题:

     接下来就是修改代码了,因为我只想加密保存在配置文件里面的密码,而不加密命令行输入的密码,所以不能直接修改svn_auth__simple_password_get这个函数,得先找到谁调用了这个函数,并且在什么情况下会调用这个函数,最后找到了

svn_auth__simple_creds_cache_get这个函数,


svn_error_t *
svn_auth__simple_creds_cache_get(void **credentials,
                                 void **iter_baton,
                                 void *provider_baton,
                                 apr_hash_t *parameters,
                                 const char *realmstring,
                                 svn_auth__password_get_t password_get,
                                 const char *passtype,
                                 apr_pool_t *pool)
{
...

      /* If we don't have a username and a password yet, we try the
         auth cache */
      //如果用户在命令行中未传入用户名和密码,则在~/.subversion/auth/svn.simple/下面查找用户之前保存的密码
      if (! (username && password)) 
        {
          if (! username)
            if (!simple_username_get(&username, creds_hash, realmstring,
                                     non_interactive))
              username = NULL;

          if (username && ! password)
            {
              if (! have_passtype)
                password = NULL;
              else
                {
                  svn_boolean_t done;

                  SVN_ERR(password_get(&done, &password, creds_hash,
                                       realmstring, username, parameters,
                                       non_interactive, pool)); //从配置文件读取密码
                  if (!done)
                    password = NULL;

                  //运行到此处说明读取到了密码,则可以在此处,将加密的密码进行解码

                  /* If the auth data didn't contain a password type,
                     force a write to upgrade the format of the auth
                     data file. */
                  if (password && ! have_passtype)
                    need_to_save = TRUE;
                }
            }
        }
...
}

此时,整个密码的读取过程已经摸清楚了,接下来就是干正事的时候了,即如何设计加密/解密算法了。我在网上随便找了两种加密算法,第一种是异或加密算法,第二种是字母表映射算法。第一种算法的优点是加密和解密算法都是一样的,实现起来超级简单,缺点是异或加密是二进制运算,如果要保存到文本文件里面,需要进行二进制和ascii码的转换,这个就比较麻烦了。第二种算法,更加超级简单,就是查表就行了,比如,我们在设置密码时,如果是a-z的26个英文字母,那么我将每个字母做一个一一映射,就是映射后的字母还在a-z这26个字母构成的集合里面,就相当于打乱了密码字母的排列顺序,比如加密前的密码为guoker,如果密码表中,g->h,u->a,o->c,k->k,e->e,r->r,加密后则为hacker。解密的时候,仍然查同样的表就可以了,这个密码表可以随意设计,只要不要tell其他people就行了,Just so simple!

下面是我用的查表法,写的一个简单的加密测试程序:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define KEY_NUM    13


void encrypt(char *plainText)
{
    int len = strlen(plainText);

    for(int i = 0; i < len; i++)
    {
        if(plainText[i] >= 97 && plainText[i] <= 122)
        {
            plainText[i] = 97 + (plainText[i] - 97 + KEY_NUM) % 26;
        }
    }
}


void decrypt(char *plainText)
{
    int len = strlen(plainText);

    for(int i = 0; i < len; i++)
    {
        if(plainText[i] >= 97 && plainText[i] <= 122)
        {
            plainText[i] = 97 + (plainText[i] - 97 + 26 - KEY_NUM) % 26;
        }
    }
}


int main(int argc, char *argv[])
{
    if(argc != 2)
    {
        printf("Usage:\n");
        printf("  %s <input_str>\n", argv[0]);
        printf("For Example:\n");
        printf("  %s guoker\n", argv[0]);
        return -1;
    }

    int len = strlen(argv[1]);
    char * plainText = (char *)malloc(len + 1);
    memcpy(plainText, argv[1], len);
    plainText[len] = '\0';

    printf("plainText='%s';\n", plainText);

    encrypt(plainText);
    printf("encrypt(): encodedStr='%s';\n", plainText);

    decrypt(plainText);
    printf("decrypt(): deodedStr='%s';\n", plainText);

    free(plainText);

    return 0;
}
/*
plainText='guoker';
encrypt(): encodedStr='thbxre';
decrypt(): deodedStr='guoker';
Press any key to continue . . .
*/

将上面加密后的密码"thbxre"保存到~/.subversion/auth/svn.simple/a8rg45s3...文件里面,然后将对应的解密代码加到svn_auth__simple_creds_cache_get函数里面,如下:


svn_error_t *
svn_auth__simple_creds_cache_get(void **credentials,
                                 void **iter_baton,
                                 void *provider_baton,
                                 apr_hash_t *parameters,
                                 const char *realmstring,
                                 svn_auth__password_get_t password_get,
                                 const char *passtype,
                                 apr_pool_t *pool)
{
...

      /* If we don't have a username and a password yet, we try the
         auth cache */
      /*如果用户在命令行中未传入用户名和密码,则在~/.subversion/auth/svn.simple/下面查找用户之前保存的密码*/
      if (! (username && password)) 
        {
          if (! username)
            if (!simple_username_get(&username, creds_hash, realmstring,
                                     non_interactive))
              username = NULL;

          if (username && ! password)
            {
              if (! have_passtype)
                password = NULL;
              else
                {
                  svn_boolean_t done;

                  SVN_ERR(password_get(&done, &password, creds_hash,
                                       realmstring, username, parameters,
                                       non_interactive, pool)); //从配置文件读取密码
                  if (!done)
                    password = NULL;

                  /*运行到此处说明读取到了密码,则可以在此处,将加密的密码进行解码*/
                  char * p = (char *)password;
                  int i;
                  for(i = 0; i < strlen(p); i++)
                    if(p[i] >= 97 && p[i] <= 122)
                      p[i] = 97 + (password[i] - 84) % 26;

                  /* If the auth data didn't contain a password type,
                     force a write to upgrade the format of the auth
                     data file. */
                  if (password && ! have_passtype)
                    need_to_save = TRUE;
                }
            }
        }
...
}

改好后,重新编译一遍,就可以了。Good Luck!

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值