xref: /freebsd/contrib/pam-krb5/tests/fakepam/config.c (revision bf6873c5786e333d679a7838d28812febf479a8a)
1*bf6873c5SCy Schubert /*
2*bf6873c5SCy Schubert  * Run a PAM interaction script for testing.
3*bf6873c5SCy Schubert  *
4*bf6873c5SCy Schubert  * Provides an interface that loads a PAM interaction script from a file and
5*bf6873c5SCy Schubert  * runs through that script, calling the internal PAM module functions and
6*bf6873c5SCy Schubert  * checking their results.  This allows automation of PAM testing through
7*bf6873c5SCy Schubert  * external data files instead of coding everything in C.
8*bf6873c5SCy Schubert  *
9*bf6873c5SCy Schubert  * The canonical version of this file is maintained in the rra-c-util package,
10*bf6873c5SCy Schubert  * which can be found at <https://www.eyrie.org/~eagle/software/rra-c-util/>.
11*bf6873c5SCy Schubert  *
12*bf6873c5SCy Schubert  * Written by Russ Allbery <eagle@eyrie.org>
13*bf6873c5SCy Schubert  * Copyright 2017-2018, 2020 Russ Allbery <eagle@eyrie.org>
14*bf6873c5SCy Schubert  * Copyright 2011-2012, 2014
15*bf6873c5SCy Schubert  *     The Board of Trustees of the Leland Stanford Junior University
16*bf6873c5SCy Schubert  *
17*bf6873c5SCy Schubert  * Permission is hereby granted, free of charge, to any person obtaining a
18*bf6873c5SCy Schubert  * copy of this software and associated documentation files (the "Software"),
19*bf6873c5SCy Schubert  * to deal in the Software without restriction, including without limitation
20*bf6873c5SCy Schubert  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
21*bf6873c5SCy Schubert  * and/or sell copies of the Software, and to permit persons to whom the
22*bf6873c5SCy Schubert  * Software is furnished to do so, subject to the following conditions:
23*bf6873c5SCy Schubert  *
24*bf6873c5SCy Schubert  * The above copyright notice and this permission notice shall be included in
25*bf6873c5SCy Schubert  * all copies or substantial portions of the Software.
26*bf6873c5SCy Schubert  *
27*bf6873c5SCy Schubert  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
28*bf6873c5SCy Schubert  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
29*bf6873c5SCy Schubert  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
30*bf6873c5SCy Schubert  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
31*bf6873c5SCy Schubert  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
32*bf6873c5SCy Schubert  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
33*bf6873c5SCy Schubert  * DEALINGS IN THE SOFTWARE.
34*bf6873c5SCy Schubert  *
35*bf6873c5SCy Schubert  * SPDX-License-Identifier: MIT
36*bf6873c5SCy Schubert  */
37*bf6873c5SCy Schubert 
38*bf6873c5SCy Schubert #include <config.h>
39*bf6873c5SCy Schubert #include <portable/pam.h>
40*bf6873c5SCy Schubert #include <portable/system.h>
41*bf6873c5SCy Schubert 
42*bf6873c5SCy Schubert #include <assert.h>
43*bf6873c5SCy Schubert #include <ctype.h>
44*bf6873c5SCy Schubert #include <dirent.h>
45*bf6873c5SCy Schubert #include <errno.h>
46*bf6873c5SCy Schubert #include <syslog.h>
47*bf6873c5SCy Schubert 
48*bf6873c5SCy Schubert #include <tests/fakepam/internal.h>
49*bf6873c5SCy Schubert #include <tests/fakepam/script.h>
50*bf6873c5SCy Schubert #include <tests/tap/basic.h>
51*bf6873c5SCy Schubert #include <tests/tap/string.h>
52*bf6873c5SCy Schubert 
53*bf6873c5SCy Schubert /* Used for enumerating arrays. */
54*bf6873c5SCy Schubert #define ARRAY_SIZE(array) (sizeof(array) / sizeof((array)[0]))
55*bf6873c5SCy Schubert 
56*bf6873c5SCy Schubert /* Mapping of strings to PAM function pointers and group numbers. */
57*bf6873c5SCy Schubert static const struct {
58*bf6873c5SCy Schubert     const char *name;
59*bf6873c5SCy Schubert     pam_call call;
60*bf6873c5SCy Schubert     enum group_type group;
61*bf6873c5SCy Schubert } CALLS[] = {
62*bf6873c5SCy Schubert     /* clang-format off */
63*bf6873c5SCy Schubert     {"acct_mgmt",     pam_sm_acct_mgmt,     GROUP_ACCOUNT },
64*bf6873c5SCy Schubert     {"authenticate",  pam_sm_authenticate,  GROUP_AUTH    },
65*bf6873c5SCy Schubert     {"setcred",       pam_sm_setcred,       GROUP_AUTH    },
66*bf6873c5SCy Schubert     {"chauthtok",     pam_sm_chauthtok,     GROUP_PASSWORD},
67*bf6873c5SCy Schubert     {"open_session",  pam_sm_open_session,  GROUP_SESSION },
68*bf6873c5SCy Schubert     {"close_session", pam_sm_close_session, GROUP_SESSION },
69*bf6873c5SCy Schubert     /* clang-format on */
70*bf6873c5SCy Schubert };
71*bf6873c5SCy Schubert 
72*bf6873c5SCy Schubert /* Mapping of PAM flag names without the leading PAM_ to values. */
73*bf6873c5SCy Schubert static const struct {
74*bf6873c5SCy Schubert     const char *name;
75*bf6873c5SCy Schubert     int value;
76*bf6873c5SCy Schubert } FLAGS[] = {
77*bf6873c5SCy Schubert     /* clang-format off */
78*bf6873c5SCy Schubert     {"CHANGE_EXPIRED_AUTHTOK", PAM_CHANGE_EXPIRED_AUTHTOK},
79*bf6873c5SCy Schubert     {"DELETE_CRED",            PAM_DELETE_CRED           },
80*bf6873c5SCy Schubert     {"DISALLOW_NULL_AUTHTOK",  PAM_DISALLOW_NULL_AUTHTOK },
81*bf6873c5SCy Schubert     {"ESTABLISH_CRED",         PAM_ESTABLISH_CRED        },
82*bf6873c5SCy Schubert     {"PRELIM_CHECK",           PAM_PRELIM_CHECK          },
83*bf6873c5SCy Schubert     {"REFRESH_CRED",           PAM_REFRESH_CRED          },
84*bf6873c5SCy Schubert     {"REINITIALIZE_CRED",      PAM_REINITIALIZE_CRED     },
85*bf6873c5SCy Schubert     {"SILENT",                 PAM_SILENT                },
86*bf6873c5SCy Schubert     {"UPDATE_AUTHTOK",         PAM_UPDATE_AUTHTOK        },
87*bf6873c5SCy Schubert     /* clang-format on */
88*bf6873c5SCy Schubert };
89*bf6873c5SCy Schubert 
90*bf6873c5SCy Schubert /* Mapping of strings to PAM groups. */
91*bf6873c5SCy Schubert static const struct {
92*bf6873c5SCy Schubert     const char *name;
93*bf6873c5SCy Schubert     enum group_type group;
94*bf6873c5SCy Schubert } GROUPS[] = {
95*bf6873c5SCy Schubert     /* clang-format off */
96*bf6873c5SCy Schubert     {"account",  GROUP_ACCOUNT },
97*bf6873c5SCy Schubert     {"auth",     GROUP_AUTH    },
98*bf6873c5SCy Schubert     {"password", GROUP_PASSWORD},
99*bf6873c5SCy Schubert     {"session",  GROUP_SESSION },
100*bf6873c5SCy Schubert     /* clang-format on */
101*bf6873c5SCy Schubert };
102*bf6873c5SCy Schubert 
103*bf6873c5SCy Schubert /* Mapping of strings to PAM return values. */
104*bf6873c5SCy Schubert static const struct {
105*bf6873c5SCy Schubert     const char *name;
106*bf6873c5SCy Schubert     int status;
107*bf6873c5SCy Schubert } RETURNS[] = {
108*bf6873c5SCy Schubert     /* clang-format off */
109*bf6873c5SCy Schubert     {"PAM_AUTH_ERR",         PAM_AUTH_ERR        },
110*bf6873c5SCy Schubert     {"PAM_AUTHINFO_UNAVAIL", PAM_AUTHINFO_UNAVAIL},
111*bf6873c5SCy Schubert     {"PAM_AUTHTOK_ERR",      PAM_AUTHTOK_ERR     },
112*bf6873c5SCy Schubert     {"PAM_DATA_SILENT",      PAM_DATA_SILENT     },
113*bf6873c5SCy Schubert     {"PAM_IGNORE",           PAM_IGNORE          },
114*bf6873c5SCy Schubert     {"PAM_NEW_AUTHTOK_REQD", PAM_NEW_AUTHTOK_REQD},
115*bf6873c5SCy Schubert     {"PAM_SESSION_ERR",      PAM_SESSION_ERR     },
116*bf6873c5SCy Schubert     {"PAM_SUCCESS",          PAM_SUCCESS         },
117*bf6873c5SCy Schubert     {"PAM_USER_UNKNOWN",     PAM_USER_UNKNOWN    },
118*bf6873c5SCy Schubert     /* clang-format on */
119*bf6873c5SCy Schubert };
120*bf6873c5SCy Schubert 
121*bf6873c5SCy Schubert /* Mapping of PAM prompt styles to their values. */
122*bf6873c5SCy Schubert static const struct {
123*bf6873c5SCy Schubert     const char *name;
124*bf6873c5SCy Schubert     int style;
125*bf6873c5SCy Schubert } STYLES[] = {
126*bf6873c5SCy Schubert     /* clang-format off */
127*bf6873c5SCy Schubert     {"echo_off",  PAM_PROMPT_ECHO_OFF},
128*bf6873c5SCy Schubert     {"echo_on",   PAM_PROMPT_ECHO_ON },
129*bf6873c5SCy Schubert     {"error_msg", PAM_ERROR_MSG      },
130*bf6873c5SCy Schubert     {"info",      PAM_TEXT_INFO      },
131*bf6873c5SCy Schubert     /* clang-format on */
132*bf6873c5SCy Schubert };
133*bf6873c5SCy Schubert 
134*bf6873c5SCy Schubert /* Mappings of strings to syslog priorities. */
135*bf6873c5SCy Schubert static const struct {
136*bf6873c5SCy Schubert     const char *name;
137*bf6873c5SCy Schubert     int priority;
138*bf6873c5SCy Schubert } PRIORITIES[] = {
139*bf6873c5SCy Schubert     /* clang-format off */
140*bf6873c5SCy Schubert     {"DEBUG",  LOG_DEBUG },
141*bf6873c5SCy Schubert     {"INFO",   LOG_INFO  },
142*bf6873c5SCy Schubert     {"NOTICE", LOG_NOTICE},
143*bf6873c5SCy Schubert     {"ERR",    LOG_ERR   },
144*bf6873c5SCy Schubert     {"CRIT",   LOG_CRIT  },
145*bf6873c5SCy Schubert     /* clang-format on */
146*bf6873c5SCy Schubert };
147*bf6873c5SCy Schubert 
148*bf6873c5SCy Schubert 
149*bf6873c5SCy Schubert /*
150*bf6873c5SCy Schubert  * Given a pointer to a string, skip any leading whitespace and return a
151*bf6873c5SCy Schubert  * pointer to the first non-whitespace character.
152*bf6873c5SCy Schubert  */
153*bf6873c5SCy Schubert static char *
skip_whitespace(char * p)154*bf6873c5SCy Schubert skip_whitespace(char *p)
155*bf6873c5SCy Schubert {
156*bf6873c5SCy Schubert     while (isspace((unsigned char) (*p)))
157*bf6873c5SCy Schubert         p++;
158*bf6873c5SCy Schubert     return p;
159*bf6873c5SCy Schubert }
160*bf6873c5SCy Schubert 
161*bf6873c5SCy Schubert 
162*bf6873c5SCy Schubert /*
163*bf6873c5SCy Schubert  * Read a line from a file into a BUFSIZ buffer, failing if the line was too
164*bf6873c5SCy Schubert  * long to fit into the buffer, and returns a copy of that line in newly
165*bf6873c5SCy Schubert  * allocated memory.  Ignores blank lines and comments.  Caller is responsible
166*bf6873c5SCy Schubert  * for freeing.  Returns NULL on end of file and fails on read errors.
167*bf6873c5SCy Schubert  */
168*bf6873c5SCy Schubert static char *
readline(FILE * file)169*bf6873c5SCy Schubert readline(FILE *file)
170*bf6873c5SCy Schubert {
171*bf6873c5SCy Schubert     char buffer[BUFSIZ];
172*bf6873c5SCy Schubert     char *line, *first;
173*bf6873c5SCy Schubert 
174*bf6873c5SCy Schubert     do {
175*bf6873c5SCy Schubert         line = fgets(buffer, sizeof(buffer), file);
176*bf6873c5SCy Schubert         if (line == NULL) {
177*bf6873c5SCy Schubert             if (feof(file))
178*bf6873c5SCy Schubert                 return NULL;
179*bf6873c5SCy Schubert             sysbail("cannot read line from script");
180*bf6873c5SCy Schubert         }
181*bf6873c5SCy Schubert         if (buffer[strlen(buffer) - 1] != '\n')
182*bf6873c5SCy Schubert             bail("script line too long");
183*bf6873c5SCy Schubert         buffer[strlen(buffer) - 1] = '\0';
184*bf6873c5SCy Schubert         first = skip_whitespace(buffer);
185*bf6873c5SCy Schubert     } while (first[0] == '#' || first[0] == '\0');
186*bf6873c5SCy Schubert     line = bstrdup(buffer);
187*bf6873c5SCy Schubert     return line;
188*bf6873c5SCy Schubert }
189*bf6873c5SCy Schubert 
190*bf6873c5SCy Schubert 
191*bf6873c5SCy Schubert /*
192*bf6873c5SCy Schubert  * Given the name of a PAM call, map it to a call enum.  This is used later in
193*bf6873c5SCy Schubert  * switch statements to determine which function to call.  Fails on any
194*bf6873c5SCy Schubert  * unrecognized string.  If the optional second argument is not NULL, also
195*bf6873c5SCy Schubert  * store the group number in that argument.
196*bf6873c5SCy Schubert  */
197*bf6873c5SCy Schubert static pam_call
string_to_call(const char * name,enum group_type * group)198*bf6873c5SCy Schubert string_to_call(const char *name, enum group_type *group)
199*bf6873c5SCy Schubert {
200*bf6873c5SCy Schubert     size_t i;
201*bf6873c5SCy Schubert 
202*bf6873c5SCy Schubert     for (i = 0; i < ARRAY_SIZE(CALLS); i++)
203*bf6873c5SCy Schubert         if (strcmp(name, CALLS[i].name) == 0) {
204*bf6873c5SCy Schubert             if (group != NULL)
205*bf6873c5SCy Schubert                 *group = CALLS[i].group;
206*bf6873c5SCy Schubert             return CALLS[i].call;
207*bf6873c5SCy Schubert         }
208*bf6873c5SCy Schubert     bail("unrecognized PAM call %s", name);
209*bf6873c5SCy Schubert }
210*bf6873c5SCy Schubert 
211*bf6873c5SCy Schubert 
212*bf6873c5SCy Schubert /*
213*bf6873c5SCy Schubert  * Given a PAM flag value without the leading PAM_, map it to the numeric
214*bf6873c5SCy Schubert  * value of that flag.  Fails on any unrecognized string.
215*bf6873c5SCy Schubert  */
216*bf6873c5SCy Schubert static int
string_to_flag(const char * name)217*bf6873c5SCy Schubert string_to_flag(const char *name)
218*bf6873c5SCy Schubert {
219*bf6873c5SCy Schubert     size_t i;
220*bf6873c5SCy Schubert 
221*bf6873c5SCy Schubert     for (i = 0; i < ARRAY_SIZE(FLAGS); i++)
222*bf6873c5SCy Schubert         if (strcmp(name, FLAGS[i].name) == 0)
223*bf6873c5SCy Schubert             return FLAGS[i].value;
224*bf6873c5SCy Schubert     bail("unrecognized PAM flag %s", name);
225*bf6873c5SCy Schubert }
226*bf6873c5SCy Schubert 
227*bf6873c5SCy Schubert 
228*bf6873c5SCy Schubert /*
229*bf6873c5SCy Schubert  * Given a PAM group name, map it to the array index for the options array for
230*bf6873c5SCy Schubert  * that group.  Fails on any unrecognized string.
231*bf6873c5SCy Schubert  */
232*bf6873c5SCy Schubert static enum group_type
string_to_group(const char * name)233*bf6873c5SCy Schubert string_to_group(const char *name)
234*bf6873c5SCy Schubert {
235*bf6873c5SCy Schubert     size_t i;
236*bf6873c5SCy Schubert 
237*bf6873c5SCy Schubert     for (i = 0; i < ARRAY_SIZE(GROUPS); i++)
238*bf6873c5SCy Schubert         if (strcmp(name, GROUPS[i].name) == 0)
239*bf6873c5SCy Schubert             return GROUPS[i].group;
240*bf6873c5SCy Schubert     bail("unrecognized PAM group %s", name);
241*bf6873c5SCy Schubert }
242*bf6873c5SCy Schubert 
243*bf6873c5SCy Schubert 
244*bf6873c5SCy Schubert /*
245*bf6873c5SCy Schubert  * Given a syslog priority name, map it to the numeric value of that priority.
246*bf6873c5SCy Schubert  * Fails on any unrecognized string.
247*bf6873c5SCy Schubert  */
248*bf6873c5SCy Schubert static int
string_to_priority(const char * name)249*bf6873c5SCy Schubert string_to_priority(const char *name)
250*bf6873c5SCy Schubert {
251*bf6873c5SCy Schubert     size_t i;
252*bf6873c5SCy Schubert 
253*bf6873c5SCy Schubert     for (i = 0; i < ARRAY_SIZE(PRIORITIES); i++)
254*bf6873c5SCy Schubert         if (strcmp(name, PRIORITIES[i].name) == 0)
255*bf6873c5SCy Schubert             return PRIORITIES[i].priority;
256*bf6873c5SCy Schubert     bail("unrecognized syslog priority %s", name);
257*bf6873c5SCy Schubert }
258*bf6873c5SCy Schubert 
259*bf6873c5SCy Schubert 
260*bf6873c5SCy Schubert /*
261*bf6873c5SCy Schubert  * Given a PAM return status, map it to the actual expected value.  Fails on
262*bf6873c5SCy Schubert  * any unrecognized string.
263*bf6873c5SCy Schubert  */
264*bf6873c5SCy Schubert static int
string_to_status(const char * name)265*bf6873c5SCy Schubert string_to_status(const char *name)
266*bf6873c5SCy Schubert {
267*bf6873c5SCy Schubert     size_t i;
268*bf6873c5SCy Schubert 
269*bf6873c5SCy Schubert     if (name == NULL)
270*bf6873c5SCy Schubert         bail("no PAM status on line");
271*bf6873c5SCy Schubert     for (i = 0; i < ARRAY_SIZE(RETURNS); i++)
272*bf6873c5SCy Schubert         if (strcmp(name, RETURNS[i].name) == 0)
273*bf6873c5SCy Schubert             return RETURNS[i].status;
274*bf6873c5SCy Schubert     bail("unrecognized PAM status %s", name);
275*bf6873c5SCy Schubert }
276*bf6873c5SCy Schubert 
277*bf6873c5SCy Schubert 
278*bf6873c5SCy Schubert /*
279*bf6873c5SCy Schubert  * Given a PAM prompt style value without the leading PAM_PROMPT_, map it to
280*bf6873c5SCy Schubert  * the numeric value of that flag.  Fails on any unrecognized string.
281*bf6873c5SCy Schubert  */
282*bf6873c5SCy Schubert static int
string_to_style(const char * name)283*bf6873c5SCy Schubert string_to_style(const char *name)
284*bf6873c5SCy Schubert {
285*bf6873c5SCy Schubert     size_t i;
286*bf6873c5SCy Schubert 
287*bf6873c5SCy Schubert     for (i = 0; i < ARRAY_SIZE(STYLES); i++)
288*bf6873c5SCy Schubert         if (strcmp(name, STYLES[i].name) == 0)
289*bf6873c5SCy Schubert             return STYLES[i].style;
290*bf6873c5SCy Schubert     bail("unrecognized PAM prompt style %s", name);
291*bf6873c5SCy Schubert }
292*bf6873c5SCy Schubert 
293*bf6873c5SCy Schubert 
294*bf6873c5SCy Schubert /*
295*bf6873c5SCy Schubert  * We found a section delimiter while parsing another section.  Rewind our
296*bf6873c5SCy Schubert  * input file back before the section delimiter so that we'll read it again.
297*bf6873c5SCy Schubert  * Takes the length of the line we read, which is used to determine how far to
298*bf6873c5SCy Schubert  * rewind.
299*bf6873c5SCy Schubert  */
300*bf6873c5SCy Schubert static void
rewind_section(FILE * script,size_t length)301*bf6873c5SCy Schubert rewind_section(FILE *script, size_t length)
302*bf6873c5SCy Schubert {
303*bf6873c5SCy Schubert     if (fseek(script, -length - 1, SEEK_CUR) != 0)
304*bf6873c5SCy Schubert         sysbail("cannot rewind file");
305*bf6873c5SCy Schubert }
306*bf6873c5SCy Schubert 
307*bf6873c5SCy Schubert 
308*bf6873c5SCy Schubert /*
309*bf6873c5SCy Schubert  * Given a string that may contain %-escapes, expand it into the resulting
310*bf6873c5SCy Schubert  * value.  The following escapes are supported:
311*bf6873c5SCy Schubert  *
312*bf6873c5SCy Schubert  *     %i   current UID (not target user UID)
313*bf6873c5SCy Schubert  *     %n   new password
314*bf6873c5SCy Schubert  *     %p   password
315*bf6873c5SCy Schubert  *     %u   username
316*bf6873c5SCy Schubert  *     %0   user-supplied string
317*bf6873c5SCy Schubert  *     ...
318*bf6873c5SCy Schubert  *     %9   user-supplied string
319*bf6873c5SCy Schubert  *
320*bf6873c5SCy Schubert  * The %* escape is preserved as-is, as it has to be interpreted at the time
321*bf6873c5SCy Schubert  * of checking output.  Returns the expanded string in newly-allocated memory.
322*bf6873c5SCy Schubert  */
323*bf6873c5SCy Schubert static char *
expand_string(const char * template,const struct script_config * config)324*bf6873c5SCy Schubert expand_string(const char *template, const struct script_config *config)
325*bf6873c5SCy Schubert {
326*bf6873c5SCy Schubert     size_t length = 0;
327*bf6873c5SCy Schubert     const char *p, *extra;
328*bf6873c5SCy Schubert     char *output, *out;
329*bf6873c5SCy Schubert     char *uid = NULL;
330*bf6873c5SCy Schubert 
331*bf6873c5SCy Schubert     length = 0;
332*bf6873c5SCy Schubert     for (p = template; *p != '\0'; p++) {
333*bf6873c5SCy Schubert         if (*p != '%')
334*bf6873c5SCy Schubert             length++;
335*bf6873c5SCy Schubert         else {
336*bf6873c5SCy Schubert             p++;
337*bf6873c5SCy Schubert             switch (*p) {
338*bf6873c5SCy Schubert             case 'i':
339*bf6873c5SCy Schubert                 if (uid == NULL)
340*bf6873c5SCy Schubert                     basprintf(&uid, "%lu", (unsigned long) getuid());
341*bf6873c5SCy Schubert                 length += strlen(uid);
342*bf6873c5SCy Schubert                 break;
343*bf6873c5SCy Schubert             case 'n':
344*bf6873c5SCy Schubert                 if (config->newpass == NULL)
345*bf6873c5SCy Schubert                     bail("new password not set");
346*bf6873c5SCy Schubert                 length += strlen(config->newpass);
347*bf6873c5SCy Schubert                 break;
348*bf6873c5SCy Schubert             case 'p':
349*bf6873c5SCy Schubert                 if (config->password == NULL)
350*bf6873c5SCy Schubert                     bail("password not set");
351*bf6873c5SCy Schubert                 length += strlen(config->password);
352*bf6873c5SCy Schubert                 break;
353*bf6873c5SCy Schubert             case 'u':
354*bf6873c5SCy Schubert                 length += strlen(config->user);
355*bf6873c5SCy Schubert                 break;
356*bf6873c5SCy Schubert             case '0':
357*bf6873c5SCy Schubert             case '1':
358*bf6873c5SCy Schubert             case '2':
359*bf6873c5SCy Schubert             case '3':
360*bf6873c5SCy Schubert             case '4':
361*bf6873c5SCy Schubert             case '5':
362*bf6873c5SCy Schubert             case '6':
363*bf6873c5SCy Schubert             case '7':
364*bf6873c5SCy Schubert             case '8':
365*bf6873c5SCy Schubert             case '9':
366*bf6873c5SCy Schubert                 if (config->extra[*p - '0'] == NULL)
367*bf6873c5SCy Schubert                     bail("extra script parameter %%%c not set", *p);
368*bf6873c5SCy Schubert                 length += strlen(config->extra[*p - '0']);
369*bf6873c5SCy Schubert                 break;
370*bf6873c5SCy Schubert             case '*':
371*bf6873c5SCy Schubert                 length += 2;
372*bf6873c5SCy Schubert                 break;
373*bf6873c5SCy Schubert             default:
374*bf6873c5SCy Schubert                 length++;
375*bf6873c5SCy Schubert                 break;
376*bf6873c5SCy Schubert             }
377*bf6873c5SCy Schubert         }
378*bf6873c5SCy Schubert     }
379*bf6873c5SCy Schubert     output = bmalloc(length + 1);
380*bf6873c5SCy Schubert     for (p = template, out = output; *p != '\0'; p++) {
381*bf6873c5SCy Schubert         if (*p != '%')
382*bf6873c5SCy Schubert             *out++ = *p;
383*bf6873c5SCy Schubert         else {
384*bf6873c5SCy Schubert             p++;
385*bf6873c5SCy Schubert             switch (*p) {
386*bf6873c5SCy Schubert             case 'i':
387*bf6873c5SCy Schubert                 assert(uid != NULL);
388*bf6873c5SCy Schubert                 memcpy(out, uid, strlen(uid));
389*bf6873c5SCy Schubert                 out += strlen(uid);
390*bf6873c5SCy Schubert                 break;
391*bf6873c5SCy Schubert             case 'n':
392*bf6873c5SCy Schubert                 memcpy(out, config->newpass, strlen(config->newpass));
393*bf6873c5SCy Schubert                 out += strlen(config->newpass);
394*bf6873c5SCy Schubert                 break;
395*bf6873c5SCy Schubert             case 'p':
396*bf6873c5SCy Schubert                 memcpy(out, config->password, strlen(config->password));
397*bf6873c5SCy Schubert                 out += strlen(config->password);
398*bf6873c5SCy Schubert                 break;
399*bf6873c5SCy Schubert             case 'u':
400*bf6873c5SCy Schubert                 memcpy(out, config->user, strlen(config->user));
401*bf6873c5SCy Schubert                 out += strlen(config->user);
402*bf6873c5SCy Schubert                 break;
403*bf6873c5SCy Schubert             case '0':
404*bf6873c5SCy Schubert             case '1':
405*bf6873c5SCy Schubert             case '2':
406*bf6873c5SCy Schubert             case '3':
407*bf6873c5SCy Schubert             case '4':
408*bf6873c5SCy Schubert             case '5':
409*bf6873c5SCy Schubert             case '6':
410*bf6873c5SCy Schubert             case '7':
411*bf6873c5SCy Schubert             case '8':
412*bf6873c5SCy Schubert             case '9':
413*bf6873c5SCy Schubert                 extra = config->extra[*p - '0'];
414*bf6873c5SCy Schubert                 memcpy(out, extra, strlen(extra));
415*bf6873c5SCy Schubert                 out += strlen(extra);
416*bf6873c5SCy Schubert                 break;
417*bf6873c5SCy Schubert             case '*':
418*bf6873c5SCy Schubert                 *out++ = '%';
419*bf6873c5SCy Schubert                 *out++ = '*';
420*bf6873c5SCy Schubert                 break;
421*bf6873c5SCy Schubert             default:
422*bf6873c5SCy Schubert                 *out++ = *p;
423*bf6873c5SCy Schubert                 break;
424*bf6873c5SCy Schubert             }
425*bf6873c5SCy Schubert         }
426*bf6873c5SCy Schubert     }
427*bf6873c5SCy Schubert     *out = '\0';
428*bf6873c5SCy Schubert     free(uid);
429*bf6873c5SCy Schubert     return output;
430*bf6873c5SCy Schubert }
431*bf6873c5SCy Schubert 
432*bf6873c5SCy Schubert 
433*bf6873c5SCy Schubert /*
434*bf6873c5SCy Schubert  * Given a whitespace-delimited string of PAM options, split it into an argv
435*bf6873c5SCy Schubert  * array and argc count and store it in the provided option struct.
436*bf6873c5SCy Schubert  */
437*bf6873c5SCy Schubert static void
split_options(char * string,struct options * options,const struct script_config * config)438*bf6873c5SCy Schubert split_options(char *string, struct options *options,
439*bf6873c5SCy Schubert               const struct script_config *config)
440*bf6873c5SCy Schubert {
441*bf6873c5SCy Schubert     char *opt;
442*bf6873c5SCy Schubert     size_t size, count;
443*bf6873c5SCy Schubert 
444*bf6873c5SCy Schubert     for (opt = strtok(string, " "); opt != NULL; opt = strtok(NULL, " ")) {
445*bf6873c5SCy Schubert         if (options->argv == NULL) {
446*bf6873c5SCy Schubert             options->argv = bcalloc(2, sizeof(const char *));
447*bf6873c5SCy Schubert             options->argv[0] = expand_string(opt, config);
448*bf6873c5SCy Schubert             options->argc = 1;
449*bf6873c5SCy Schubert         } else {
450*bf6873c5SCy Schubert             count = (options->argc + 2);
451*bf6873c5SCy Schubert             size = sizeof(const char *);
452*bf6873c5SCy Schubert             options->argv = breallocarray(options->argv, count, size);
453*bf6873c5SCy Schubert             options->argv[options->argc] = expand_string(opt, config);
454*bf6873c5SCy Schubert             options->argv[options->argc + 1] = NULL;
455*bf6873c5SCy Schubert             options->argc++;
456*bf6873c5SCy Schubert         }
457*bf6873c5SCy Schubert     }
458*bf6873c5SCy Schubert }
459*bf6873c5SCy Schubert 
460*bf6873c5SCy Schubert 
461*bf6873c5SCy Schubert /*
462*bf6873c5SCy Schubert  * Parse the options section of a PAM script.  This consists of one or more
463*bf6873c5SCy Schubert  * lines in the format:
464*bf6873c5SCy Schubert  *
465*bf6873c5SCy Schubert  *     <group> = <options>
466*bf6873c5SCy Schubert  *
467*bf6873c5SCy Schubert  * where options are either option names or option=value pairs, where the
468*bf6873c5SCy Schubert  * value may not contain whitespace.  Returns an options struct, which stores
469*bf6873c5SCy Schubert  * argc and argv values for each group.
470*bf6873c5SCy Schubert  *
471*bf6873c5SCy Schubert  * Takes the work struct as an argument and puts values into its array.
472*bf6873c5SCy Schubert  */
473*bf6873c5SCy Schubert static void
parse_options(FILE * script,struct work * work,const struct script_config * config)474*bf6873c5SCy Schubert parse_options(FILE *script, struct work *work,
475*bf6873c5SCy Schubert               const struct script_config *config)
476*bf6873c5SCy Schubert {
477*bf6873c5SCy Schubert     char *line, *group, *token;
478*bf6873c5SCy Schubert     size_t length = 0;
479*bf6873c5SCy Schubert     enum group_type type;
480*bf6873c5SCy Schubert 
481*bf6873c5SCy Schubert     for (line = readline(script); line != NULL; line = readline(script)) {
482*bf6873c5SCy Schubert         length = strlen(line);
483*bf6873c5SCy Schubert         group = strtok(line, " ");
484*bf6873c5SCy Schubert         if (group == NULL)
485*bf6873c5SCy Schubert             bail("malformed script line");
486*bf6873c5SCy Schubert         if (group[0] == '[')
487*bf6873c5SCy Schubert             break;
488*bf6873c5SCy Schubert         type = string_to_group(group);
489*bf6873c5SCy Schubert         token = strtok(NULL, " ");
490*bf6873c5SCy Schubert         if (token == NULL)
491*bf6873c5SCy Schubert             bail("malformed action line");
492*bf6873c5SCy Schubert         if (strcmp(token, "=") != 0)
493*bf6873c5SCy Schubert             bail("malformed action line near %s", token);
494*bf6873c5SCy Schubert         token = strtok(NULL, "");
495*bf6873c5SCy Schubert         split_options(token, &work->options[type], config);
496*bf6873c5SCy Schubert         free(line);
497*bf6873c5SCy Schubert     }
498*bf6873c5SCy Schubert     if (line != NULL) {
499*bf6873c5SCy Schubert         free(line);
500*bf6873c5SCy Schubert         rewind_section(script, length);
501*bf6873c5SCy Schubert     }
502*bf6873c5SCy Schubert }
503*bf6873c5SCy Schubert 
504*bf6873c5SCy Schubert 
505*bf6873c5SCy Schubert /*
506*bf6873c5SCy Schubert  * Parse the call portion of a PAM call in the run section of a PAM script.
507*bf6873c5SCy Schubert  * This handles parsing the PAM flags that optionally may be given as part of
508*bf6873c5SCy Schubert  * the call.  Takes the token representing the call and a pointer to the
509*bf6873c5SCy Schubert  * action struct to fill in with the call and the option flags.
510*bf6873c5SCy Schubert  */
511*bf6873c5SCy Schubert static void
parse_call(char * token,struct action * action)512*bf6873c5SCy Schubert parse_call(char *token, struct action *action)
513*bf6873c5SCy Schubert {
514*bf6873c5SCy Schubert     char *flags, *flag;
515*bf6873c5SCy Schubert 
516*bf6873c5SCy Schubert     action->flags = 0;
517*bf6873c5SCy Schubert     flags = strchr(token, '(');
518*bf6873c5SCy Schubert     if (flags != NULL) {
519*bf6873c5SCy Schubert         *flags = '\0';
520*bf6873c5SCy Schubert         flags++;
521*bf6873c5SCy Schubert         for (flag = strtok(flags, "|,)"); flag != NULL;
522*bf6873c5SCy Schubert              flag = strtok(NULL, "|,)")) {
523*bf6873c5SCy Schubert             action->flags |= string_to_flag(flag);
524*bf6873c5SCy Schubert         }
525*bf6873c5SCy Schubert     }
526*bf6873c5SCy Schubert     action->call = string_to_call(token, &action->group);
527*bf6873c5SCy Schubert }
528*bf6873c5SCy Schubert 
529*bf6873c5SCy Schubert 
530*bf6873c5SCy Schubert /*
531*bf6873c5SCy Schubert  * Parse the run section of a PAM script.  This consists of one or more lines
532*bf6873c5SCy Schubert  * in the format:
533*bf6873c5SCy Schubert  *
534*bf6873c5SCy Schubert  *     <call> = <status>
535*bf6873c5SCy Schubert  *
536*bf6873c5SCy Schubert  * where <call> is a PAM call and <status> is what it should return.  Returns
537*bf6873c5SCy Schubert  * a linked list of actions.  Fails on any error in parsing.
538*bf6873c5SCy Schubert  */
539*bf6873c5SCy Schubert static struct action *
parse_run(FILE * script)540*bf6873c5SCy Schubert parse_run(FILE *script)
541*bf6873c5SCy Schubert {
542*bf6873c5SCy Schubert     struct action *head = NULL, *current = NULL, *next;
543*bf6873c5SCy Schubert     char *line, *token, *call;
544*bf6873c5SCy Schubert     size_t length = 0;
545*bf6873c5SCy Schubert 
546*bf6873c5SCy Schubert     for (line = readline(script); line != NULL; line = readline(script)) {
547*bf6873c5SCy Schubert         length = strlen(line);
548*bf6873c5SCy Schubert         token = strtok(line, " ");
549*bf6873c5SCy Schubert         if (token[0] == '[')
550*bf6873c5SCy Schubert             break;
551*bf6873c5SCy Schubert         next = bmalloc(sizeof(struct action));
552*bf6873c5SCy Schubert         next->next = NULL;
553*bf6873c5SCy Schubert         if (head == NULL)
554*bf6873c5SCy Schubert             head = next;
555*bf6873c5SCy Schubert         else
556*bf6873c5SCy Schubert             current->next = next;
557*bf6873c5SCy Schubert         next->name = bstrdup(token);
558*bf6873c5SCy Schubert         call = token;
559*bf6873c5SCy Schubert         token = strtok(NULL, " ");
560*bf6873c5SCy Schubert         if (token == NULL)
561*bf6873c5SCy Schubert             bail("malformed action line");
562*bf6873c5SCy Schubert         if (strcmp(token, "=") != 0)
563*bf6873c5SCy Schubert             bail("malformed action line near %s", token);
564*bf6873c5SCy Schubert         token = strtok(NULL, " ");
565*bf6873c5SCy Schubert         next->status = string_to_status(token);
566*bf6873c5SCy Schubert         parse_call(call, next);
567*bf6873c5SCy Schubert         free(line);
568*bf6873c5SCy Schubert         current = next;
569*bf6873c5SCy Schubert     }
570*bf6873c5SCy Schubert     if (head == NULL)
571*bf6873c5SCy Schubert         bail("empty run section in script");
572*bf6873c5SCy Schubert     if (line != NULL) {
573*bf6873c5SCy Schubert         free(line);
574*bf6873c5SCy Schubert         rewind_section(script, length);
575*bf6873c5SCy Schubert     }
576*bf6873c5SCy Schubert     return head;
577*bf6873c5SCy Schubert }
578*bf6873c5SCy Schubert 
579*bf6873c5SCy Schubert 
580*bf6873c5SCy Schubert /*
581*bf6873c5SCy Schubert  * Parse the end section of a PAM script.  There is one supported line in the
582*bf6873c5SCy Schubert  * format:
583*bf6873c5SCy Schubert  *
584*bf6873c5SCy Schubert  *     flags = <flag>|<flag>
585*bf6873c5SCy Schubert  *
586*bf6873c5SCy Schubert  * where <flag> is a flag to pass to pam_end.  Returns the flags.
587*bf6873c5SCy Schubert  */
588*bf6873c5SCy Schubert static int
parse_end(FILE * script)589*bf6873c5SCy Schubert parse_end(FILE *script)
590*bf6873c5SCy Schubert {
591*bf6873c5SCy Schubert     char *line, *token, *flag;
592*bf6873c5SCy Schubert     size_t length = 0;
593*bf6873c5SCy Schubert     int flags = PAM_SUCCESS;
594*bf6873c5SCy Schubert 
595*bf6873c5SCy Schubert     for (line = readline(script); line != NULL; line = readline(script)) {
596*bf6873c5SCy Schubert         length = strlen(line);
597*bf6873c5SCy Schubert         token = strtok(line, " ");
598*bf6873c5SCy Schubert         if (token[0] == '[')
599*bf6873c5SCy Schubert             break;
600*bf6873c5SCy Schubert         if (strcmp(token, "flags") != 0)
601*bf6873c5SCy Schubert             bail("unknown end setting %s", token);
602*bf6873c5SCy Schubert         token = strtok(NULL, " ");
603*bf6873c5SCy Schubert         if (token == NULL)
604*bf6873c5SCy Schubert             bail("malformed end line");
605*bf6873c5SCy Schubert         if (strcmp(token, "=") != 0)
606*bf6873c5SCy Schubert             bail("malformed end line near %s", token);
607*bf6873c5SCy Schubert         token = strtok(NULL, " ");
608*bf6873c5SCy Schubert         flag = strtok(token, "|");
609*bf6873c5SCy Schubert         while (flag != NULL) {
610*bf6873c5SCy Schubert             flags |= string_to_status(flag);
611*bf6873c5SCy Schubert             flag = strtok(NULL, "|");
612*bf6873c5SCy Schubert         }
613*bf6873c5SCy Schubert         free(line);
614*bf6873c5SCy Schubert     }
615*bf6873c5SCy Schubert     if (line != NULL) {
616*bf6873c5SCy Schubert         free(line);
617*bf6873c5SCy Schubert         rewind_section(script, length);
618*bf6873c5SCy Schubert     }
619*bf6873c5SCy Schubert     return flags;
620*bf6873c5SCy Schubert }
621*bf6873c5SCy Schubert 
622*bf6873c5SCy Schubert 
623*bf6873c5SCy Schubert /*
624*bf6873c5SCy Schubert  * Parse the output section of a PAM script.  This consists of zero or more
625*bf6873c5SCy Schubert  * lines in the format:
626*bf6873c5SCy Schubert  *
627*bf6873c5SCy Schubert  *     PRIORITY some output information
628*bf6873c5SCy Schubert  *     PRIORITY /output regex/
629*bf6873c5SCy Schubert  *
630*bf6873c5SCy Schubert  * where PRIORITY is replaced by the numeric syslog priority corresponding to
631*bf6873c5SCy Schubert  * that priority and the rest of the output undergoes %-esacape expansion.
632*bf6873c5SCy Schubert  * Returns the accumulated output as a vector.
633*bf6873c5SCy Schubert  */
634*bf6873c5SCy Schubert static struct output *
parse_output(FILE * script,const struct script_config * config)635*bf6873c5SCy Schubert parse_output(FILE *script, const struct script_config *config)
636*bf6873c5SCy Schubert {
637*bf6873c5SCy Schubert     char *line, *token, *message;
638*bf6873c5SCy Schubert     struct output *output;
639*bf6873c5SCy Schubert     int priority;
640*bf6873c5SCy Schubert 
641*bf6873c5SCy Schubert     output = output_new();
642*bf6873c5SCy Schubert     if (output == NULL)
643*bf6873c5SCy Schubert         sysbail("cannot allocate vector");
644*bf6873c5SCy Schubert     for (line = readline(script); line != NULL; line = readline(script)) {
645*bf6873c5SCy Schubert         token = strtok(line, " ");
646*bf6873c5SCy Schubert         priority = string_to_priority(token);
647*bf6873c5SCy Schubert         token = strtok(NULL, "");
648*bf6873c5SCy Schubert         if (token == NULL)
649*bf6873c5SCy Schubert             bail("malformed line %s", line);
650*bf6873c5SCy Schubert         message = expand_string(token, config);
651*bf6873c5SCy Schubert         output_add(output, priority, message);
652*bf6873c5SCy Schubert         free(message);
653*bf6873c5SCy Schubert         free(line);
654*bf6873c5SCy Schubert     }
655*bf6873c5SCy Schubert     return output;
656*bf6873c5SCy Schubert }
657*bf6873c5SCy Schubert 
658*bf6873c5SCy Schubert 
659*bf6873c5SCy Schubert /*
660*bf6873c5SCy Schubert  * Parse the prompts section of a PAM script.  This consists of zero or more
661*bf6873c5SCy Schubert  * lines in one of the formats:
662*bf6873c5SCy Schubert  *
663*bf6873c5SCy Schubert  *     type = prompt
664*bf6873c5SCy Schubert  *     type = /prompt/
665*bf6873c5SCy Schubert  *     type = prompt|response
666*bf6873c5SCy Schubert  *     type = /prompt/|response
667*bf6873c5SCy Schubert  *
668*bf6873c5SCy Schubert  * If the type is error_msg or info, there is no response.  Otherwise,
669*bf6873c5SCy Schubert  * everything after the last | is taken to be the response that should be
670*bf6873c5SCy Schubert  * provided to that prompt.  The response undergoes %-escape expansion.
671*bf6873c5SCy Schubert  */
672*bf6873c5SCy Schubert static struct prompts *
parse_prompts(FILE * script,const struct script_config * config)673*bf6873c5SCy Schubert parse_prompts(FILE *script, const struct script_config *config)
674*bf6873c5SCy Schubert {
675*bf6873c5SCy Schubert     struct prompts *prompts = NULL;
676*bf6873c5SCy Schubert     struct prompt *prompt;
677*bf6873c5SCy Schubert     char *line, *token, *style, *end;
678*bf6873c5SCy Schubert     size_t size, count, i;
679*bf6873c5SCy Schubert     size_t length = 0;
680*bf6873c5SCy Schubert 
681*bf6873c5SCy Schubert     for (line = readline(script); line != NULL; line = readline(script)) {
682*bf6873c5SCy Schubert         length = strlen(line);
683*bf6873c5SCy Schubert         token = strtok(line, " ");
684*bf6873c5SCy Schubert         if (token[0] == '[')
685*bf6873c5SCy Schubert             break;
686*bf6873c5SCy Schubert         if (prompts == NULL) {
687*bf6873c5SCy Schubert             prompts = bcalloc(1, sizeof(struct prompts));
688*bf6873c5SCy Schubert             prompts->prompts = bcalloc(1, sizeof(struct prompt));
689*bf6873c5SCy Schubert             prompts->allocated = 1;
690*bf6873c5SCy Schubert         } else if (prompts->allocated == prompts->size) {
691*bf6873c5SCy Schubert             count = prompts->allocated * 2;
692*bf6873c5SCy Schubert             size = sizeof(struct prompt);
693*bf6873c5SCy Schubert             prompts->prompts = breallocarray(prompts->prompts, count, size);
694*bf6873c5SCy Schubert             prompts->allocated = count;
695*bf6873c5SCy Schubert             for (i = prompts->size; i < prompts->allocated; i++) {
696*bf6873c5SCy Schubert                 prompts->prompts[i].prompt = NULL;
697*bf6873c5SCy Schubert                 prompts->prompts[i].response = NULL;
698*bf6873c5SCy Schubert             }
699*bf6873c5SCy Schubert         }
700*bf6873c5SCy Schubert         prompt = &prompts->prompts[prompts->size];
701*bf6873c5SCy Schubert         style = token;
702*bf6873c5SCy Schubert         token = strtok(NULL, " ");
703*bf6873c5SCy Schubert         if (token == NULL)
704*bf6873c5SCy Schubert             bail("malformed prompt line");
705*bf6873c5SCy Schubert         if (strcmp(token, "=") != 0)
706*bf6873c5SCy Schubert             bail("malformed prompt line near %s", token);
707*bf6873c5SCy Schubert         prompt->style = string_to_style(style);
708*bf6873c5SCy Schubert         token = strtok(NULL, "");
709*bf6873c5SCy Schubert         if (prompt->style == PAM_ERROR_MSG || prompt->style == PAM_TEXT_INFO)
710*bf6873c5SCy Schubert             prompt->prompt = expand_string(token, config);
711*bf6873c5SCy Schubert         else {
712*bf6873c5SCy Schubert             end = strrchr(token, '|');
713*bf6873c5SCy Schubert             if (end == NULL)
714*bf6873c5SCy Schubert                 bail("malformed prompt line near %s", token);
715*bf6873c5SCy Schubert             *end = '\0';
716*bf6873c5SCy Schubert             prompt->prompt = expand_string(token, config);
717*bf6873c5SCy Schubert             token = end + 1;
718*bf6873c5SCy Schubert             prompt->response = expand_string(token, config);
719*bf6873c5SCy Schubert         }
720*bf6873c5SCy Schubert         prompts->size++;
721*bf6873c5SCy Schubert         free(line);
722*bf6873c5SCy Schubert     }
723*bf6873c5SCy Schubert     if (line != NULL) {
724*bf6873c5SCy Schubert         free(line);
725*bf6873c5SCy Schubert         rewind_section(script, length);
726*bf6873c5SCy Schubert     }
727*bf6873c5SCy Schubert     return prompts;
728*bf6873c5SCy Schubert }
729*bf6873c5SCy Schubert 
730*bf6873c5SCy Schubert 
731*bf6873c5SCy Schubert /*
732*bf6873c5SCy Schubert  * Parse a PAM interaction script.  This handles parsing of the top-level
733*bf6873c5SCy Schubert  * section markers and dispatches the parsing to other functions.  Returns the
734*bf6873c5SCy Schubert  * total work to do as a work struct.
735*bf6873c5SCy Schubert  */
736*bf6873c5SCy Schubert struct work *
parse_script(FILE * script,const struct script_config * config)737*bf6873c5SCy Schubert parse_script(FILE *script, const struct script_config *config)
738*bf6873c5SCy Schubert {
739*bf6873c5SCy Schubert     struct work *work;
740*bf6873c5SCy Schubert     char *line, *token;
741*bf6873c5SCy Schubert 
742*bf6873c5SCy Schubert     work = bmalloc(sizeof(struct work));
743*bf6873c5SCy Schubert     memset(work, 0, sizeof(struct work));
744*bf6873c5SCy Schubert     work->end_flags = PAM_SUCCESS;
745*bf6873c5SCy Schubert     for (line = readline(script); line != NULL; line = readline(script)) {
746*bf6873c5SCy Schubert         token = strtok(line, " ");
747*bf6873c5SCy Schubert         if (token[0] != '[')
748*bf6873c5SCy Schubert             bail("line outside of section: %s", line);
749*bf6873c5SCy Schubert         if (strcmp(token, "[options]") == 0)
750*bf6873c5SCy Schubert             parse_options(script, work, config);
751*bf6873c5SCy Schubert         else if (strcmp(token, "[run]") == 0)
752*bf6873c5SCy Schubert             work->actions = parse_run(script);
753*bf6873c5SCy Schubert         else if (strcmp(token, "[end]") == 0)
754*bf6873c5SCy Schubert             work->end_flags = parse_end(script);
755*bf6873c5SCy Schubert         else if (strcmp(token, "[output]") == 0)
756*bf6873c5SCy Schubert             work->output = parse_output(script, config);
757*bf6873c5SCy Schubert         else if (strcmp(token, "[prompts]") == 0)
758*bf6873c5SCy Schubert             work->prompts = parse_prompts(script, config);
759*bf6873c5SCy Schubert         else
760*bf6873c5SCy Schubert             bail("unknown section: %s", token);
761*bf6873c5SCy Schubert         free(line);
762*bf6873c5SCy Schubert     }
763*bf6873c5SCy Schubert     if (work->actions == NULL)
764*bf6873c5SCy Schubert         bail("no run section defined");
765*bf6873c5SCy Schubert     return work;
766*bf6873c5SCy Schubert }
767