1 /*-
2 * Copyright (c) 2002-2003 Networks Associates Technology, Inc.
3 * Copyright (c) 2004-2017 Dag-Erling Smørgrav
4 * All rights reserved.
5 *
6 * This software was developed for the FreeBSD Project by ThinkSec AS and
7 * Network Associates Laboratories, the Security Research Division of
8 * Network Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
9 * ("CBOSS"), as part of the DARPA CHATS research program.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. The name of the author may not be used to endorse or promote
20 * products derived from this software without specific prior written
21 * permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36 #ifdef HAVE_CONFIG_H
37 # include "config.h"
38 #endif
39
40 #include <sys/param.h>
41
42 #include <stdlib.h>
43 #include <string.h>
44
45 #include <security/pam_appl.h>
46 #include <security/openpam.h>
47
48 #include "openpam_impl.h"
49 #include "openpam_strlset.h"
50
51 static const char authtok_prompt[] = "Password:";
52 static const char authtok_prompt_remote[] = "Password for %u@%h:";
53 static const char oldauthtok_prompt[] = "Old Password:";
54 static const char newauthtok_prompt[] = "New Password:";
55
56 /*
57 * OpenPAM extension
58 *
59 * Retrieve authentication token
60 */
61
62 int
pam_get_authtok(pam_handle_t * pamh,int item,const char ** authtok,const char * prompt)63 pam_get_authtok(pam_handle_t *pamh,
64 int item,
65 const char **authtok,
66 const char *prompt)
67 {
68 char prompt_buf[1024];
69 size_t prompt_size;
70 const void *oldauthtok, *prevauthtok, *promptp;
71 const char *prompt_option, *default_prompt;
72 const void *lhost, *rhost;
73 char *resp, *resp2;
74 int pitem, r, style, twice;
75
76 ENTER();
77 *authtok = NULL;
78 twice = 0;
79 switch (item) {
80 case PAM_AUTHTOK:
81 pitem = PAM_AUTHTOK_PROMPT;
82 prompt_option = "authtok_prompt";
83 default_prompt = authtok_prompt;
84 r = pam_get_item(pamh, PAM_RHOST, &rhost);
85 if (r == PAM_SUCCESS && rhost != NULL) {
86 r = pam_get_item(pamh, PAM_HOST, &lhost);
87 if (r == PAM_SUCCESS && lhost != NULL) {
88 if (strcmp(rhost, lhost) != 0)
89 default_prompt = authtok_prompt_remote;
90 }
91 }
92 r = pam_get_item(pamh, PAM_OLDAUTHTOK, &oldauthtok);
93 if (r == PAM_SUCCESS && oldauthtok != NULL) {
94 default_prompt = newauthtok_prompt;
95 twice = 1;
96 }
97 break;
98 case PAM_OLDAUTHTOK:
99 pitem = PAM_OLDAUTHTOK_PROMPT;
100 prompt_option = "oldauthtok_prompt";
101 default_prompt = oldauthtok_prompt;
102 twice = 0;
103 break;
104 default:
105 RETURNC(PAM_BAD_CONSTANT);
106 }
107 if (openpam_get_option(pamh, "try_first_pass") ||
108 openpam_get_option(pamh, "use_first_pass")) {
109 r = pam_get_item(pamh, item, &prevauthtok);
110 if (r == PAM_SUCCESS && prevauthtok != NULL) {
111 *authtok = prevauthtok;
112 RETURNC(PAM_SUCCESS);
113 } else if (openpam_get_option(pamh, "use_first_pass")) {
114 RETURNC(r == PAM_SUCCESS ? PAM_AUTH_ERR : r);
115 }
116 }
117 /* pam policy overrides the module's choice */
118 if ((promptp = openpam_get_option(pamh, prompt_option)) != NULL)
119 prompt = promptp;
120 /* no prompt provided, see if there is one tucked away somewhere */
121 if (prompt == NULL) {
122 r = pam_get_item(pamh, pitem, &promptp);
123 if (r == PAM_SUCCESS && promptp != NULL)
124 prompt = promptp;
125 }
126 /* fall back to hardcoded default */
127 if (prompt == NULL)
128 prompt = default_prompt;
129 /* expand */
130 prompt_size = sizeof prompt_buf;
131 r = openpam_subst(pamh, prompt_buf, &prompt_size, prompt);
132 if (r == PAM_SUCCESS && prompt_size <= sizeof prompt_buf)
133 prompt = prompt_buf;
134 style = openpam_get_option(pamh, "echo_pass") ?
135 PAM_PROMPT_ECHO_ON : PAM_PROMPT_ECHO_OFF;
136 r = pam_prompt(pamh, style, &resp, "%s", prompt);
137 if (r != PAM_SUCCESS)
138 RETURNC(r);
139 if (twice) {
140 r = pam_prompt(pamh, style, &resp2, "Retype %s", prompt);
141 if (r != PAM_SUCCESS) {
142 strlset(resp, 0, PAM_MAX_RESP_SIZE);
143 FREE(resp);
144 RETURNC(r);
145 }
146 if (strcmp(resp, resp2) != 0) {
147 strlset(resp, 0, PAM_MAX_RESP_SIZE);
148 FREE(resp);
149 }
150 strlset(resp2, 0, PAM_MAX_RESP_SIZE);
151 FREE(resp2);
152 }
153 if (resp == NULL)
154 RETURNC(PAM_TRY_AGAIN);
155 r = pam_set_item(pamh, item, resp);
156 strlset(resp, 0, PAM_MAX_RESP_SIZE);
157 FREE(resp);
158 if (r != PAM_SUCCESS)
159 RETURNC(r);
160 r = pam_get_item(pamh, item, (const void **)authtok);
161 RETURNC(r);
162 }
163
164 /*
165 * Error codes:
166 *
167 * =pam_get_item
168 * =pam_prompt
169 * =pam_set_item
170 * !PAM_SYMBOL_ERR
171 * PAM_BAD_CONSTANT
172 * PAM_TRY_AGAIN
173 */
174
175 /**
176 * The =pam_get_authtok function either prompts the user for an
177 * authentication token or retrieves a cached authentication token,
178 * depending on circumstances.
179 * Either way, a pointer to the authentication token is stored in the
180 * location pointed to by the =authtok argument, and the corresponding PAM
181 * item is updated.
182 *
183 * The =item argument must have one of the following values:
184 *
185 * =PAM_AUTHTOK:
186 * Returns the current authentication token, or the new token
187 * when changing authentication tokens.
188 * =PAM_OLDAUTHTOK:
189 * Returns the previous authentication token when changing
190 * authentication tokens.
191 *
192 * The =prompt argument specifies a prompt to use if no token is cached.
193 * If it is =NULL, the =PAM_AUTHTOK_PROMPT or =PAM_OLDAUTHTOK_PROMPT item,
194 * as appropriate, will be used.
195 * If that item is also =NULL, a hardcoded default prompt will be used.
196 * Additionally, when =pam_get_authtok is called from a service module,
197 * the prompt may be affected by module options as described below.
198 * The prompt is then expanded using =openpam_subst before it is passed to
199 * the conversation function.
200 *
201 * If =item is set to =PAM_AUTHTOK and there is a non-null =PAM_OLDAUTHTOK
202 * item, =pam_get_authtok will ask the user to confirm the new token by
203 * retyping it.
204 * If there is a mismatch, =pam_get_authtok will return =PAM_TRY_AGAIN.
205 *
206 * MODULE OPTIONS
207 *
208 * When called by a service module, =pam_get_authtok will recognize the
209 * following module options:
210 *
211 * ;authtok_prompt:
212 * Prompt to use when =item is set to =PAM_AUTHTOK.
213 * This option overrides both the =prompt argument and the
214 * =PAM_AUTHTOK_PROMPT item.
215 * ;echo_pass:
216 * If the application's conversation function allows it, this
217 * lets the user see what they are typing.
218 * This should only be used for non-reusable authentication
219 * tokens.
220 * ;oldauthtok_prompt:
221 * Prompt to use when =item is set to =PAM_OLDAUTHTOK.
222 * This option overrides both the =prompt argument and the
223 * =PAM_OLDAUTHTOK_PROMPT item.
224 * ;try_first_pass:
225 * If the requested item is non-null, return it without
226 * prompting the user.
227 * Typically, the service module will verify the token, and
228 * if it does not match, clear the item before calling
229 * =pam_get_authtok a second time.
230 * ;use_first_pass:
231 * Do not prompt the user at all; just return the cached
232 * value, or =PAM_AUTH_ERR if there is none.
233 *
234 * >pam_conv
235 * >pam_get_item
236 * >pam_get_user
237 * >openpam_get_option
238 * >openpam_subst
239 */
240