【问题标题】:Regex Verification of Line in /etc/passwd/etc/passwd 中行的正则表达式验证
【发布时间】:2011-05-06 08:31:36
【问题描述】:

我需要验证 /etc/passwd 文件是否有效,并且认为正则表达式是验证不是 cmets 的行的好主意。我将如何验证这样的行:

root:*:0:0:System Administrator:/var/root:/bin/sh 

经过一番研究,第 5 个字段(系统管理员)可以包含其他数据,例如电子邮件和地址,第二个字段可以包含除 : 之外的任何内容,最后 2 个字段是完整路径。

任何线索我将如何为此创建一个正则表达式?

【问题讨论】:

  • passwd 手册页指出:“密码文件绝不应手动编辑;应该使用 vipw (8)。”

标签: regex perl unix


【解决方案1】:

不想开玩笑 - Passwd::Unix 可能是你最好的选择。

【讨论】:

  • 你肯定会得到 my 的支持。一些更高层次的抽象似乎很关键。无论如何,我真的,真的对触摸 passwd 文件感到紧张。 Its manpage 表示永远不要使用任何东西来改变它,除了vipw,其来源是very careful。这太重要了,不能冒险搞砸。
【解决方案2】:

需要使用 Perl 吗?检查密码文件的常规方法是使用 awk 作为数据库查询语言。例如:

awk -F: '$3 ~ /pattern/'

当然,您可以改用perl -lane。但如果你使用 Perl,你可能应该使用标准的 User::pwent 模块。

【讨论】:

  • User::pwent 不会最终隐藏任何缺陷吗? (我不确定 为什么 OP 正在尝试验证 /etc/passwd;这似乎是一件奇怪的事情)
  • 哦,对了,它会的。如果你在做 /etc/passwd 验证,你应该比这更小心!!我很确定 passwd (5) 也不允许使用 /^#/ cmets,尽管它确实更适合 YP。
【解决方案3】:

想要一个正则表达式?好吧,那我你一个正则表达式:它在$is_valid_pwent_rx 变量中。

享受吧。

重要提示:不得被误解为对正常密码文件的语义检查器。它只是一个语法检查器

目前为 OpenBSD 配置。

#!/usr/bin/env perl

use 5.010;
use strict;
use warnings;

our $PASSWD = "/etc/passwd";

our $Errors = 0;

sub is_valid_pwent(_);
sub main();

#########################################################

main();
exit($Errors != 0);

#########################################################

sub main() {

    open(PASSWD)        || die "can't open $PASSWD: $!";

    while (my $line = <PASSWD>) {
        chomp $line;
        ## NEXT LINE IS WRONG: NO "COMMENTS" ALLOWED!!!
        next if $line =~ /^#/;
        next if is_valid_pwent($line);   
        say "$0: Invalid entry at $PASSWD $.: $line";
        $Errors++;
    }

    close(PASSWD)       || die "can't close $PASSWD: $!";

    say "$0: $PASSWD appears ok." unless $Errors;
}

#########################################################

INIT {

    state $is_valid_pwent_rx = qr{

      ^ (?&any_pwent)  $

###############################################

      (?(DEFINE)

      (?<any_pwent>     (?&yp_pwent) | (?&pwent) )

# The `+' token may also be alone in the name field, which causes all users
# from the passwd.byname and passwd.byuid YP maps to be included.
#
# If the entry contains non-empty uid or gid fields, the specified numbers
# will override the information retrieved from the YP maps.  Additionally,
# if the gecos, dir, or shell entries contain text, it will override the
# information included via YP.  On some systems, the passwd field may also
# be overridden.  It is recommended that the standard way to enable YP
# passwd support in /etc/master.passwd is:
#
#     +:*::::::::   

        (?<yp_pwent>
                       (?&PLUS)         # substitute in YP
         : (?&EMPTY) | (?&pw_passwd)    # user's encrypted password.
         : (?&EMPTY) | (?&pw_uid)       # user's login user ID.
         : (?&EMPTY) | (?&pw_gid)       # user's login group ID.
         : (?&EMPTY) | (?&pw_gecos)     # Honeywell login info.
         : (?&EMPTY) | (?&pw_dir)       # user's home directory.
         : (?&EMPTY) | (?&pw_shell)     # user's login shell.
        )

# A normal password entry

        (?<pwent>

           (?&pw_name)      # user's login name.
         : (?&pw_passwd)    # user's encrypted password.
         : (?&pw_uid)       # user's login user ID.
         : (?&pw_gid)       # user's login group ID.
         : (?&pw_gecos)     # Honeywell login info.
         : (?&pw_dir)       # user's home directory.
         : (?&pw_shell)     # user's login shell.
        )

# A master password entry

        (?<master_pwent>
           (?&pw_name)      # user's login name.
         : (?&pw_passwd)    # user's encrypted password.
         : (?&pw_uid)       # user's login user ID.
         : (?&pw_gid)       # user's login group ID.
         : (?&pw_class)     # user's general classification (see login.conf(5))
         : (?&pw_change)    # password change time.
         : (?&pw_expire)    # account expiration time.
         : (?&pw_gecos)     # general information about the user.
         : (?&pw_dir)       # user's home directory.
         : (?&pw_shell)     # user's login shell.
        )

# The name field is the login used to access the computer account, and the
# uid field is the number associated with it.  They should both be unique
# across the system (and often across a group of systems) since they con-
# trol file access.
#
# While it is possible to have multiple entries with identical login names
# and/or identical user IDs, it is usually a mistake to do so.  Routines
# that manipulate these files will often return only one of the multiple
# entries, and that one by random selection.
#
# The login name may be up to 31 characters long.  For compatibility with
# legacy software, a login name should start with a letter and consist
# solely of letters, numbers, dashes and underscores.  The login name must
# never begin with a hyphen (`-'); also, it is strongly suggested that nei-
# ther uppercase characters nor dots (`.') be part of the name, as this
# tends to confuse mailers.  No field may contain a colon as this has been
# used historically to separate the fields in the user database.

        (?<pw_name>

            (?= (?&NON_COLON){1,31} )

            (?: (?&UNDERSCORE)
              | (?&LETTER)
            )

            (?: (?&LETTER)
              | (?&number)
              | (?&HYPHEN)
              | (?&UNDERSCORE)
            ){0,30}

        )

# The password field is the *encrypted* form of the password.  If the
# password field is empty, no password will be required to gain access to 
# the machine.  This is almost invariably a mistake.  By convention, ac-  
# counts that are not intended to be logged in to (e.g. bin, daemon, sshd)
# have a star (`*') in the password field.  Note that there is nothing spe-
# cial about `*', it is just one of many strings that is not a valid en-
# crypted password (see crypt(3)).  Because master.passwd contains the en-
# crypted user passwords, it should not be readable by anyone without ap-
# propriate privileges.
#
# Which type of cipher is used to encrypt the password information depends
# on the configuration in login.conf(5).  It can be different for local and
# YP passwords.

        (?<pw_passwd>
            (?&STAR)
          | (?&NON_COLON) +
          | (?&EMPTY)           # should not allow this!
        )

# The uid field is the numeric user ID assigned to this login name.
# It need not strictly be unique.

        (?<pw_uid>
            (?&number) +
        )

# The group (gid) field is the group that the user will be placed in
# upon login. Since this system supports multiple groups (see groups(1))
# this field currently has little special meaning.

        (?<pw_gid>
            (?&number) +
        )

        (?<pw_class>
            (?&EMPTY)   
          | (?&any_text)
        )

        (?<pw_change>
            (?&EMPTY)
          | (?&number)
        )

        (?<pw_expire>
            (?&EMPTY)
          | (?&number)
        )

        (?<pw_gecos>
            # (?&EMPTY) | (?&gecos_fields)
            (?&any_text)
        )

        # some have an extra field in them after hphone
        (?<gecos_fields>
            (?&gecos_name)   # User's full name.
            (?&COMMA)
            (?&gecos_office) # User's office location.
            (?&COMMA)
            (?&gecos_wphone) # User's work phone number.
            (?&COMMA)
            (?&gecos_hphone) # User's home phone number.
          )

        (?<gecos_name>      (?&gecos_text)  )
        (?<gecos_office>    (?&gecos_text)  )
        (?<gecos_wphone>    (?&gecos_text)  )
        (?<gecos_hphone>    (?&gecos_text)  )

        (?<pw_dir>
            (?&EMPTY)   # bad idea
          | (?&directory_name)
        )

        (?<pw_shell>
            (?&EMPTY)   # means "/bin/sh"
          | (?&filename)

        )

#########################

        (?<directory_name>      (?&pathname)    )
        (?<filename>            (?&pathname)    )

        (?<pathname>
            (?&SLASH)
            (?&any_text)
        )

        (?<LETTER>      [a-z]       )  # \p{Ll} && \p{ASCII}

        (?<DIGIT>       [0-9]       )  # \p{Nd} && \p{ASCII}
        (?<ZERO>         0          )
        (?<NON_ZERO>    [1-9]       )

        (?<PLUS>        \x2B        )  # PLUS SIGN
        (?<COMMA>       \x2C        )  # COMMA
        (?<HYPHEN>      \x2D        )  # HYPHEN-MINUS   
        (?<SLASH>       \x2F        )  # SOLIDUS
        (?<COLON>       \x3A        )  # COLON
        (?<STAR>        \x2A        )  # ASTERISK
        (?<UNDERSCORE>  \x5F        )  # LOW LINE

        (?<NON_COLON> [^\x3A]       )

        (?<EMPTY> (?# this space intentionally left blank) )

        (?<number>
            (?&ZERO)
          | (?&NON_ZERO) (?&DIGIT) *
        )

        (?<any_text>
            (?&NON_COLON) *
        )

        (?<gecos_text>  
            (?:
                (?! (?&COMMA) )
                (?! (?&COLON) )
                .
            ) *
        )

      )

    }x;

    sub is_valid_pwent(_) {
        my $pwent = shift();
        return $pwent =~ $is_valid_pwent_rx;
    }

}

【讨论】:

    【解决方案4】:

    这样的?

    ^(#.*|[a-z]*:[^:]*:[0-9]*:[0-9]*:[^:]*:/[^:]*:/[^:]*)$
    

    (假设用户名由小写字母组成)

    【讨论】:

    • 该模式不正确。它验证格式错误的密码条目。这是一件非常糟糕的事情
    • @tchrist:我不知道密码输入的具体形式,所以请自行更改。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-10
    • 2021-05-07
    • 1970-01-01
    • 2011-01-20
    • 1970-01-01
    • 2016-08-24
    相关资源
    最近更新 更多