ftp关键技术一:账户验证

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/hyj_zkdzslh/article/details/80367054
 
  

ftp关键技术一:账户验证

对于Linux端的ftp服务的而言,账户即为Linux端的用户。

一般步骤是:

  • 以root用户权限启动ftp服务

  • 获取客户端的验证信息

  • 从系统获取用户名对应的加密后的密码

  • 对客户端发过来的密码进行对应的加密,并对比

如何验证是否以root用户启动服务?

我们可以通过getuid()函数获取当前程序运行的uid。

一般root用户的uid =0。

所以可以通过以下方式验证是否以root用户启动:

    if (getuid() != 0)
    {
        fprintf(stderr, "miniftp must start be as root\n");
        exit(EXIT_FAILURE);
    }
系统获取用户名对应的加密后的密码

UNIX系统口令文件定义包含在<pwd.h>中定义的passwd的结构中。

           struct passwd {
               char   *pw_name;       /* username */
               char   *pw_passwd;     /* user password */
               uid_t   pw_uid;        /* user ID */
               gid_t   pw_gid;        /* group ID */
               char   *pw_gecos;      /* user information */
               char   *pw_dir;        /* home directory */
               char   *pw_shell;      /* shell program */
           };

在这里就不在赘述其他东西,主要关心uid和pw_passwd,分别是用户id和用户所对应的密码。

于此同时在其中定义了两个函数需要我们去关注

       struct passwd *getpwnam(const char *name);
        //通过用户名获取passwd   
       struct passwd *getpwuid(uid_t uid);
        //通过uid获取passwd

初次之外需要我们注意的是这些函数需要运行在root权限下,这就是为什么要验证是否root启动服务原因之一。

另外如果输入的用户名或者uid是错误的话,返回的passwd是一个NULL

于是我们便可以这样设计ftp的验证

static void do_user(session_t *sess)
{
    //略去了接受和分割命令细节,这里sess->arg是用户名
    struct passwd *pw  = getpwnam(sess->arg);
    if (pw == NULL)
    {
        ftp_reply(sess, FTP_LOGINERR, "Login incorrect");
        //发送FTP_LOGINERR命令
        return;
    }
    sess->uid = pw->pw_uid;
    ftp_reply(sess, FTP_GIVEPWORD, "Please specify the password.");  
    //发送FTP_GIVEPWORD命令
}

在这里将username和passwd分开验证可以优化一下体验,至少知道是什么输错了,由于是分开设计的,所以我们需要保存一个用户信息,因为uid是最好操作的,所以我们在sess(用户信息)中存储了一个uid

对客户端发过来的密码进行对应的加密,并对比

这里可能有读者会问,为什么要将客户端的密码进行加密对比,而不是将系统的密码解密对比呢?

为了安全考虑,Linux的加密口令是经单向加密算法处理过的用户口令副本。因此此算法是单向的,所以不能从加密猜测到原来的口令。

基于此linux设计了一个叫阴影口令的文件。该文件至少包含用户名和加密口令,与该口令相关的其他信息也存放其中。

在<shadow.h>文件中定义了

           struct spwd {
               char *sp_namp;     /* Login name */
               char *sp_pwdp;     /* Encrypted password */
               long  sp_lstchg;   /* Date of last change
                                     (measured in days since
                                     1970-01-01 00:00:00 +0000 (UTC)) */
               long  sp_min;      /* Min # of days between changes */
               long  sp_max;      /* Max # of days between changes */
               long  sp_warn;     /* # of days before password expires
                                     to warn user to change it */
               long  sp_inact;    /* # of days after password expires
                                     until account is disabled */
               long  sp_expire;   /* Date when account expires
                                     (measured in days since
                                     1970-01-01 00:00:00 +0000 (UTC)) */
               unsigned long sp_flag;  /* Reserved */
           };
​

在这个文件中,我们主要用到sp_pwdp这个字段,sp_pwdp是指加密口令,通过这个加密口令我们可以通过crypt函数对获取客户端发送的密码进行加密。

同样定义了函数

       struct spwd *getspnam(const char *name);

当我们获得了spwd的后,我们还需要对客户端发送的pwsswd进行加密

在<unistd.h>中定义了一个函数

   char *crypt(const char *key, const char *salt);

key:要加密的明文。

salt:密钥。

salt 默认使用DES加密方法。DES加密时,salt只能取两个字符,多出的字符会被丢弃。

需要注意的是这个函数在编译的时候需要链接lcrypt。

ps:这个函数多用于md5 SHA-256等加密,感兴趣的可以学学,我在这里就不赘述了。

于是我们便可以这样写do_pass函数来验证客户端的密码

static void do_pass(session_t *sess)
{
    struct passwd *pw = getpwuid(sess->uid);   //获取pw_name
    if (pw == NULL)
    {
        ftp_reply(sess, FTP_LOGINERR, "Login incorrect");
        return;
    }
    struct spwd *sp = getspnam(pw->pw_name);  //获取sp_pwdp
    if (sp == NULL)
    {
        ftp_reply(sess, FTP_LOGINERR, "Login incorrect");
        return;
    }
    char *encrypted_pw = crypt(sess->arg, sp->sp_pwdp);   //加密
    if (strcmp(encrypted_pw, sp->sp_pwdp) != 0)    //比对加密后的密码和sp_pwdp
    {
        ftp_reply(sess, FTP_LOGINERR, "Login incorrect.");
        return;
    }
   
    setegid(pw->pw_gid);                      //更改gid
    seteuid(pw->pw_uid);                      //更改uid
    chdir(pw->pw_dir);                        //更改Dir
    ftp_reply(sess, FTP_LOGINOK, "Login success.");
}

推荐一下个人博客:https://angeltears.github.io/

完整的项目在 :https://github.com/angeltears/ftp

欢迎大家讨论和分享问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值