1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/os/localauth_rule.c - rule localauth module */
4 * Copyright (C) 1990,1991,2007,2008,2013 by the Massachusetts
5 * Institute of Technology. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
14 * * Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
24 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30 * OF THE POSSIBILITY OF SUCH DAMAGE.
34 * This module implements the RULE type for auth_to_local processing.
36 * There are three parts to each rule. The first part, if present, determines
37 * the selection string. If this is not present, the selection string defaults
38 * to the unparsed principal name without realm (which can be dangerous in
39 * multi-realm environments, but is our historical behavior). The selection
42 * "[" <ncomps> ":" <format> "]"
44 * <ncomps> is the number of expected components for this rule. If the
45 * principal does not have this many components, then this rule does
48 * <format> determines the selection string. Within <format>, $0 will
49 * be substituted with the principal's realm, $1 with its first
50 * component, $2 with its second component, and so forth.
52 * The second part is an optional regular expression surrounded by parentheses.
53 * If present, the rule will only apply if the selection string matches the
54 * regular expression. At present, the regular expression may not contain a
57 * The third part is a sequence of zero or more transformation rules, using
60 * "s/" <regexp> "/" <text> "/" ["g"]
62 * No substitutions are allowed within <text>. A "g" indicates that the
63 * substitution should be performed globally; otherwise it will be performed at
69 #include <krb5/localauth_plugin.h>
75 /* Process the match portion of a rule and update *contextp. Return
76 * KRB5_LNAME_NOTRANS if selstring doesn't match the regexp. */
77 static krb5_error_code
78 aname_do_match(const char *selstring, const char **contextp)
81 const char *startp, *endp;
86 /* If no regexp is present, leave *contextp alone and return success. */
87 if (**contextp != '(')
90 /* Find the end of the regexp and make a copy of it. */
91 startp = *contextp + 1;
92 endp = strchr(startp, ')');
94 return KRB5_CONFIG_BADFORMAT;
95 regstr = k5memdup0(startp, endp - startp, &ret);
99 /* Perform the match. */
100 ret = (regcomp(&re, regstr, REG_EXTENDED) == 0 &&
101 regexec(&re, selstring, 1, &m, 0) == 0 &&
102 m.rm_so == 0 && (size_t)m.rm_eo == strlen(selstring)) ? 0 :
106 *contextp = endp + 1;
110 /* Replace regular expression matches of regstr with repl in instr, producing
111 * *outstr. If doall is true, replace all matches for regstr. */
112 static krb5_error_code
113 do_replacement(const char *regstr, const char *repl, krb5_boolean doall,
114 const char *instr, char **outstr)
121 if (regcomp(&re, regstr, REG_EXTENDED))
122 return KRB5_LNAME_NOTRANS;
123 k5_buf_init_dynamic(&buf);
124 while (regexec(&re, instr, 1, &m, 0) == 0) {
125 k5_buf_add_len(&buf, instr, m.rm_so);
126 k5_buf_add(&buf, repl);
132 k5_buf_add(&buf, instr);
133 if (k5_buf_status(&buf) != 0)
140 * Perform any substitutions specified by *contextp, and advance *contextp past
141 * the substitution expressions. Place the result of the substitutions in
144 static krb5_error_code
145 aname_replacer(const char *string, const char **contextp, char **result)
147 krb5_error_code ret = 0;
148 const char *cp, *ep, *tp;
149 char *newstr, *rule = NULL, *repl = NULL, *current = NULL;
150 krb5_boolean doglobal;
154 current = strdup(string);
158 /* Iterate over replacement expressions, updating current for each one. */
160 while (*cp != '\0') {
161 /* Skip leading whitespace */
162 while (isspace((unsigned char)*cp))
165 /* Find the separators for an s/rule/repl/ expression. */
166 if (!(cp[0] == 's' && cp[1] == '/' && (ep = strchr(cp + 2, '/')) &&
167 (tp = strchr(ep + 1, '/')))) {
168 ret = KRB5_CONFIG_BADFORMAT;
172 /* Copy the rule and replacement strings. */
174 rule = k5memdup0(cp + 2, ep - (cp + 2), &ret);
178 repl = k5memdup0(ep + 1, tp - (ep + 1), &ret);
182 /* Advance past expression and check for trailing "g". */
184 doglobal = (*cp == 'g');
188 ret = do_replacement(rule, repl, doglobal, current, &newstr);
205 * Compute selection string for RULE rules. Advance *contextp to the string
206 * position after the selstring part if present, and set *result to the
209 static krb5_error_code
210 aname_get_selstring(krb5_context context, krb5_const_principal aname,
211 const char **contextp, char **selstring_out)
216 const krb5_data *datap;
217 struct k5buf selstring;
220 *selstring_out = NULL;
221 if (**contextp != '[') {
223 * No selstring part; use the principal name without realm. This is
224 * problematic in many multiple-realm environments, but is how we've
225 * historically done it.
227 return krb5_unparse_name_flags(context, aname,
228 KRB5_PRINCIPAL_UNPARSE_NO_REALM,
232 /* Advance past the '[' and read the number of components. */
233 current = *contextp + 1;
235 num_comps = strtol(current, &end, 10);
236 if (errno != 0 || num_comps < 0 || *end != ':')
237 return KRB5_CONFIG_BADFORMAT;
239 if (num_comps != aname->length)
240 return KRB5_LNAME_NOTRANS;
243 k5_buf_init_dynamic(&selstring);
245 /* Copy in literal characters up to the next $ or ]. */
246 nlit = strcspn(current, "$]");
247 k5_buf_add_len(&selstring, current, nlit);
252 /* Expand $ substitution to a principal component. */
254 ind = strtol(current + 1, &end, 10);
255 if (errno || ind > num_comps)
258 datap = ind > 0 ? &aname->data[ind - 1] : &aname->realm;
259 k5_buf_add_len(&selstring, datap->data, datap->length);
262 /* Check that we hit a ']' and not the end of the string. */
263 if (*current != ']') {
264 k5_buf_free(&selstring);
265 return KRB5_CONFIG_BADFORMAT;
268 if (k5_buf_status(&selstring) != 0)
271 *contextp = current + 1;
272 *selstring_out = selstring.data;
276 static krb5_error_code
277 an2ln_rule(krb5_context context, krb5_localauth_moddata data, const char *type,
278 const char *rule, krb5_const_principal aname, char **lname_out)
282 char *selstring = NULL;
286 return KRB5_CONFIG_BADFORMAT;
288 /* Compute the selection string. */
290 ret = aname_get_selstring(context, aname, ¤t, &selstring);
294 /* Check the selection string against the regexp, if present. */
295 if (*current == '(') {
296 ret = aname_do_match(selstring, ¤t);
301 /* Perform the substitution. */
302 ret = aname_replacer(selstring, ¤t, lname_out);
309 #else /* HAVE_REGEX_H */
311 static krb5_error_code
312 an2ln_rule(krb5_context context, krb5_localauth_moddata data, const char *type,
313 const char *rule, krb5_const_principal aname, char **lname_out)
315 return KRB5_LNAME_NOTRANS;
321 freestr(krb5_context context, krb5_localauth_moddata data, char *str)
327 localauth_rule_initvt(krb5_context context, int maj_ver, int min_ver,
328 krb5_plugin_vtable vtable)
330 krb5_localauth_vtable vt = (krb5_localauth_vtable)vtable;
331 static const char *types[] = { "RULE", NULL };
334 vt->an2ln_types = types;
335 vt->an2ln = an2ln_rule;
336 vt->free_string = freestr;