summaryrefslogtreecommitdiffstats
path: root/ipapython/test/test_ipavalidate.py
blob: c860b91b2885b8701ae622b27af857e763986421 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#! /usr/bin/python -E
#
# Copyright (C) 2007    Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; version 2 only
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.    See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#

import sys
sys.path.insert(0, ".")

import unittest

from ipapython import ipavalidate

class TestValidate(unittest.TestCase):
    def setUp(self):
        pass

    def tearDown(self):
        pass

    def test_validEmail(self):
        self.assertEqual(True, ipavalidate.Email("test@freeipa.org"))
        self.assertEqual(True, ipavalidate.Email("", notEmpty=False))

    def test_invalidEmail(self):
        self.assertEqual(False, ipavalidate.Email("test"))
        self.assertEqual(False, ipavalidate.Email("test@freeipa"))
        self.assertEqual(False, ipavalidate.Email("test@.com"))
        self.assertEqual(False, ipavalidate.Email(""))
        self.assertEqual(False, ipavalidate.Email(None))

    def test_validPlain(self):
        self.assertEqual(True, ipavalidate.Plain("Joe User"))
        self.assertEqual(True, ipavalidate.Plain("Joe O'Malley"))
        self.assertEqual(True, ipavalidate.Plain("", notEmpty=False))
        self.assertEqual(True, ipavalidate.Plain(None, notEmpty=False))
        self.assertEqual(True, ipavalidate.Plain("JoeUser", allowSpaces=False))
        self.assertEqual(True, ipavalidate.Plain("JoeUser", allowSpaces=True))

    def test_invalidPlain(self):
        self.assertEqual(False, ipavalidate.Plain("Joe (User)"))
        self.assertEqual(False, ipavalidate.Plain("Joe C. User"))
        self.assertEqual(False, ipavalidate.Plain("", notEmpty=True))
        self.assertEqual(False, ipavalidate.Plain(None, notEmpty=True))
        self.assertEqual(False, ipavalidate.Plain("Joe User", allowSpaces=False))
        self.assertEqual(False, ipavalidate.Plain("Joe C. User"))

    def test_validString(self):
        self.assertEqual(True, ipavalidate.String("Joe User"))
        self.assertEqual(True, ipavalidate.String("Joe O'Malley"))
        self.assertEqual(True, ipavalidate.String("", notEmpty=False))
        self.assertEqual(True, ipavalidate.String(None, notEmpty=False))
        self.assertEqual(True, ipavalidate.String("Joe C. User"))

    def test_invalidString(self):
        self.assertEqual(False, ipavalidate.String("", notEmpty=True))
        self.assertEqual(False, ipavalidate.String(None, notEmpty=True))

    def test_validPath(self):
        self.assertEqual(True, ipavalidate.Path("/"))
        self.assertEqual(True, ipavalidate.Path("/home/user"))
        self.assertEqual(True, ipavalidate.Path("../home/user"))
        self.assertEqual(True, ipavalidate.Path("", notEmpty=False))
        self.assertEqual(True, ipavalidate.Path(None, notEmpty=False))

    def test_invalidPath(self):
        self.assertEqual(False, ipavalidate.Path("(foo)"))
        self.assertEqual(False, ipavalidate.Path("", notEmpty=True))
        self.assertEqual(False, ipavalidate.Path(None, notEmpty=True))

    def test_validName(self):
        self.assertEqual(True, ipavalidate.GoodName("foo"))
        self.assertEqual(True, ipavalidate.GoodName("1foo"))
        self.assertEqual(True, ipavalidate.GoodName("foo.bar"))
        self.assertEqual(True, ipavalidate.GoodName("foo.bar$"))

    def test_invalidName(self):
        self.assertEqual(False, ipavalidate.GoodName("foo bar"))
        self.assertEqual(False, ipavalidate.GoodName("foo%bar"))
        self.assertEqual(False, ipavalidate.GoodName("*foo"))
        self.assertEqual(False, ipavalidate.GoodName("$foo.bar$"))

if __name__ == '__main__':
    unittest.main()
n649'>649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711
/*
   SSSD

   User tools

   Copyright (C) Stephen Gallagher <sgallagh@redhat.com>	2009

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <pwd.h>
#include <pcre.h>
#include <errno.h>
#include <talloc.h>
#include <pwd.h>
#include <grp.h>

#include "confdb/confdb.h"
#include "util/strtonum.h"
#include "util/util.h"
#include "util/safe-format-string.h"
#include "responder/common/responder.h"

#ifdef HAVE_LIBPCRE_LESSER_THAN_7
#define NAME_DOMAIN_PATTERN_OPTIONS (PCRE_EXTENDED)
#else
#define NAME_DOMAIN_PATTERN_OPTIONS (PCRE_DUPNAMES | PCRE_EXTENDED)
#endif

/* Function returns given realm name as new uppercase string */
char *get_uppercase_realm(TALLOC_CTX *memctx, const char *name)
{
    char *realm;
    char *c;

    realm = talloc_strdup(memctx, name);
    if (!realm) {
        return NULL;
    }

    c = realm;
    while(*c != '\0') {
        *c = toupper(*c);
        c++;
    }

    return realm;
}


static int sss_names_ctx_destructor(struct sss_names_ctx *snctx)
{
    if (snctx->re) {
        pcre_free(snctx->re);
        snctx->re = NULL;
    }
    return 0;
}

#define IPA_AD_DEFAULT_RE "(((?P<domain>[^\\\\]+)\\\\(?P<name>.+$))|" \
                         "((?P<name>[^@]+)@(?P<domain>.+$))|" \
                         "(^(?P<name>[^@\\\\]+)$))"

static errno_t get_id_provider_default_re(TALLOC_CTX *mem_ctx,
                                          struct confdb_ctx *cdb,
                                          const char *conf_path,
                                          char **re_pattern)
{
#ifdef HAVE_LIBPCRE_LESSER_THAN_7
    DEBUG(SSSDBG_MINOR_FAILURE,
          "The libpcre version on this system is too old. Only "
           "the user@DOMAIN name fully qualified name format will "
           "be supported\n");
    *re_pattern = NULL;
    return EOK;
#else
    int ret;
    size_t c;
    char *id_provider = NULL;

    struct provider_default_re {
        const char *name;
        const char *re;
    } provider_default_re[] = {{"ipa", IPA_AD_DEFAULT_RE},
                               {"ad", IPA_AD_DEFAULT_RE},
                               {NULL, NULL}};

    ret = confdb_get_string(cdb, mem_ctx, conf_path, CONFDB_DOMAIN_ID_PROVIDER,
                            NULL, &id_provider);
    if (ret != EOK) {
        DEBUG(SSSDBG_OP_FAILURE, "Failed to read ID provider " \
                                  "from conf db.\n");
        goto done;
    }

    if (id_provider == NULL) {
        *re_pattern = NULL;
    } else {
        for (c = 0; provider_default_re[c].name != NULL; c++) {
            if (strcmp(id_provider, provider_default_re[c].name) == 0) {
                *re_pattern = talloc_strdup(mem_ctx, provider_default_re[c].re);
                if (*re_pattern == NULL) {
                    DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
                    ret = ENOMEM;
                    goto done;
                }
                break;
            }
        }
    }

    ret = EOK;

done:
    talloc_free(id_provider);
    return ret;
#endif
}

static errno_t sss_fqnames_init(struct sss_names_ctx *nctx, const char *fq_fmt)
{
    char *fq;

    nctx->fq_fmt = talloc_strdup(nctx, fq_fmt);
    if (nctx->fq_fmt == NULL) {
        return ENOMEM;
    }

    DEBUG(SSSDBG_CONF_SETTINGS, "Using fq format [%s].\n", nctx->fq_fmt);

    /* Fail if the name specifier is missing, or if the format is
     * invalid */
    fq = sss_tc_fqname2 (nctx, nctx, "unused.example.com", "unused", "the-test-user");
    if (fq == NULL) {
        DEBUG(SSSDBG_OP_FAILURE,
              "The fq format is invalid [%s]", nctx->fq_fmt);
        return EINVAL;
    } else if (strstr (fq, "the-test-user") == NULL) {
        DEBUG(SSSDBG_OP_FAILURE,
              "Username pattern not found in [%s]\n", nctx->fq_fmt);
        return ENOENT;
    }

    talloc_free (fq);
    return EOK;
}

int sss_names_init_from_args(TALLOC_CTX *mem_ctx, const char *re_pattern,
                             const char *fq_fmt, struct sss_names_ctx **out)
{
    struct sss_names_ctx *ctx;
    const char *errstr;
    int errval;
    int errpos;
    int ret;

    ctx = talloc_zero(mem_ctx, struct sss_names_ctx);
    if (!ctx) return ENOMEM;
    talloc_set_destructor(ctx, sss_names_ctx_destructor);

    ctx->re_pattern = talloc_strdup(ctx, re_pattern);
    if (ctx->re_pattern == NULL) {
        ret = ENOMEM;
        goto done;
    }

    DEBUG(SSSDBG_CONF_SETTINGS, "Using re [%s].\n", ctx->re_pattern);

    ret = sss_fqnames_init(ctx, fq_fmt);
    if (ret != EOK) {
        DEBUG(SSSDBG_OP_FAILURE, "Could not check the FQ names format"
              "[%d]: %s\n", ret, sss_strerror(ret));
        goto done;
    }

    ctx->re = pcre_compile2(ctx->re_pattern,
                            NAME_DOMAIN_PATTERN_OPTIONS,
                            &errval, &errstr, &errpos, NULL);
    if (!ctx->re) {
        DEBUG(SSSDBG_CRIT_FAILURE,
              "Invalid Regular Expression pattern at position %d."
                  " (Error: %d [%s])\n", errpos, errval, errstr);
        ret = EFAULT;
        goto done;
    }

    *out = ctx;
    ret = EOK;

done:
    if (ret != EOK) {
        talloc_free(ctx);
    }
    return ret;
}

int sss_names_init(TALLOC_CTX *mem_ctx, struct confdb_ctx *cdb,
                   const char *domain, struct sss_names_ctx **out)
{
    TALLOC_CTX *tmpctx = NULL;
    char *conf_path = NULL;
    char *re_pattern = NULL;;
    char *fq_fmt = NULL;
    int ret;

    tmpctx = talloc_new(NULL);
    if (tmpctx == NULL) {
        ret = ENOMEM;
        goto done;
    }

    if (domain != NULL) {
        conf_path = talloc_asprintf(tmpctx, CONFDB_DOMAIN_PATH_TMPL, domain);
        if (conf_path == NULL) {
            ret = ENOMEM;
            goto done;
        }

        ret = confdb_get_string(cdb, tmpctx, conf_path,
                                CONFDB_NAME_REGEX, NULL, &re_pattern);
        if (ret != EOK) goto done;
    }

    /* If not found in the domain, look in globals */
    if (re_pattern == NULL) {
        ret = confdb_get_string(cdb, tmpctx, CONFDB_MONITOR_CONF_ENTRY,
                                CONFDB_NAME_REGEX, NULL, &re_pattern);
        if (ret != EOK) goto done;
    }

    if (re_pattern == NULL && conf_path != NULL) {
        ret = get_id_provider_default_re(tmpctx, cdb, conf_path, &re_pattern);
        if (ret != EOK) {
            DEBUG(SSSDBG_OP_FAILURE, "Failed to get provider default regular " \
                                      "expression for domain [%s].\n", domain);
            goto done;
        }
    }

    if (!re_pattern) {
        re_pattern = talloc_strdup(tmpctx,
                                   "(?P<name>[^@]+)@?(?P<domain>[^@]*$)");
        if (!re_pattern) {
            ret = ENOMEM;
            goto done;
        }
#ifdef HAVE_LIBPCRE_LESSER_THAN_7
    } else {
        DEBUG(SSSDBG_OP_FAILURE,
              "This binary was build with a version of libpcre that does "
                  "not support non-unique named subpatterns.\n");
        DEBUG(SSSDBG_OP_FAILURE,
              "Please make sure that your pattern [%s] only contains "
                  "subpatterns with a unique name and uses "
                  "the Python syntax (?P<name>).\n", re_pattern);
#endif
    }

    if (conf_path != NULL) {
        ret = confdb_get_string(cdb, tmpctx, conf_path,
                                CONFDB_FULL_NAME_FORMAT, NULL, &fq_fmt);
        if (ret != EOK) goto done;
    }

    /* If not found in the domain, look in globals */
    if (fq_fmt == NULL) {
        ret = confdb_get_string(cdb, tmpctx, CONFDB_MONITOR_CONF_ENTRY,
                                CONFDB_FULL_NAME_FORMAT, NULL, &fq_fmt);
        if (ret != EOK) goto done;
    }

    if (!fq_fmt) {
        fq_fmt = talloc_strdup(tmpctx, CONFDB_DEFAULT_FULL_NAME_FORMAT);
        if (!fq_fmt) {
            ret = ENOMEM;
            goto done;
        }
    }

    ret = sss_names_init_from_args(mem_ctx, re_pattern, fq_fmt, out);

done:
    talloc_free(tmpctx);
    return ret;
}

int sss_parse_name(TALLOC_CTX *memctx,
                   struct sss_names_ctx *snctx,
                   const char *orig, char **_domain, char **_name)
{
    pcre *re = snctx->re;
    const char *result;
    int ovec[30];
    int origlen;
    int ret, strnum;

    origlen = strlen(orig);

    ret = pcre_exec(re, NULL, orig, origlen, 0, PCRE_NOTEMPTY, ovec, 30);
    if (ret == PCRE_ERROR_NOMATCH) {
        return ERR_REGEX_NOMATCH;
    } else if (ret < 0) {
        DEBUG(SSSDBG_MINOR_FAILURE, "PCRE Matching error, %d\n", ret);
        return EINVAL;
    }

    if (ret == 0) {
        DEBUG(SSSDBG_CRIT_FAILURE,
              "Too many matches, the pattern is invalid.\n");
    }

    strnum = ret;

    if (_name != NULL) {
        result = NULL;
        ret = pcre_get_named_substring(re, orig, ovec, strnum, "name", &result);
        if (ret < 0  || !result) {
            DEBUG(SSSDBG_OP_FAILURE, "Name not found!\n");
            return EINVAL;
        }
        *_name = talloc_strdup(memctx, result);
        pcre_free_substring(result);
        if (!*_name) return ENOMEM;
    }

    if (_domain != NULL) {
        result = NULL;
        ret = pcre_get_named_substring(re, orig, ovec, strnum, "domain",
                                       &result);
        if (ret < 0  || !result) {
            DEBUG(SSSDBG_CONF_SETTINGS, "Domain not provided!\n");
            *_domain = NULL;
        } else {
            /* ignore "" string */
            if (*result) {
                *_domain = talloc_strdup(memctx, result);
                pcre_free_substring(result);
                if (!*_domain) return ENOMEM;
            } else {
                pcre_free_substring(result);
                *_domain = NULL;
            }
        }
    }

    return EOK;
}

int sss_parse_name_const(TALLOC_CTX *memctx,
                         struct sss_names_ctx *snctx, const char *orig,
                         const char **_domain, const char **_name)
{
    char *domain;
    char *name;
    int ret;

    ret = sss_parse_name(memctx, snctx, orig,
                         (_domain == NULL) ? NULL : &domain,
                         (_name == NULL) ? NULL : &name);
    if (ret == EOK) {
        if (_domain != NULL) {
            *_domain = domain;
        }

        if (_name != NULL) {
            *_name = name;
        }
    }

    return ret;
}

static struct sss_domain_info * match_any_domain_or_subdomain_name(
                                                struct sss_domain_info *dom,
                                                const char *dmatch)
{
    if (strcasecmp(dom->name, dmatch) == 0 ||
        (dom->flat_name != NULL && strcasecmp(dom->flat_name, dmatch) == 0)) {
        return dom;
    }

    return find_domain_by_name(dom, dmatch, true);
}

int sss_parse_name_for_domains(TALLOC_CTX *memctx,
                               struct sss_domain_info *domains,
                               const char *default_domain,
                               const char *orig, char **domain, char **name)
{
    struct sss_domain_info *dom, *match = NULL;
    char *rdomain, *rname;
    char *dmatch, *nmatch;
    char *candidate_name = NULL;
    char *candidate_domain = NULL;
    bool name_mismatch = false;
    TALLOC_CTX *tmp_ctx;
    int ret;

    tmp_ctx = talloc_new(NULL);
    if (tmp_ctx == NULL) {
        return ENOMEM;
    }

    rname = NULL;
    rdomain = NULL;

    for (dom = domains; dom != NULL; dom = get_next_domain(dom, false)) {
        ret = sss_parse_name(tmp_ctx, dom->names, orig, &dmatch, &nmatch);
        if (ret == EOK) {
            /*
             * If the name matched without the domain part, make note of it.
             * All the other domain expressions must agree on the domain-less
             * name.
             */
            if (dmatch == NULL) {
                if (candidate_name == NULL) {
                    candidate_name = nmatch;
                } else if (strcasecmp(candidate_name, nmatch) != 0) {
                    name_mismatch = true;
                }

            /*
             * If a domain was returned, then it must match the name of the
             * domain that this expression was found on, or one of the
             * subdomains.
             */
            } else {
                match = match_any_domain_or_subdomain_name (dom, dmatch);
                if (match != NULL) {
                    DEBUG(SSSDBG_FUNC_DATA, "name '%s' matched expression for "
                                             "domain '%s', user is %s\n",
                                             orig, match->name, nmatch);