|
1 # |
|
2 # This patch contains a couple of PAM enhancements: |
|
3 # 1) Each SSHv2 userauth method has its own PAM service name so that PAM can |
|
4 # be used to control what userauth methods are allowed. |
|
5 # 2) The PAMServiceName and PAMServicePrefix options. |
|
6 # |
|
7 # We have contributed back this feature to the OpenSSH upstream community. |
|
8 # For more information, see https://bugzilla.mindrot.org/show_bug.cgi?id=2246 |
|
9 # In the future, if these enhancements are accepted by the upsteam in a |
|
10 # later release, we will remove this patch when we upgrade to that release. |
|
11 # |
|
12 --- orig/auth-pam.c Fri Jun 20 15:39:05 2014 |
|
13 +++ new/auth-pam.c Fri Jun 20 16:47:09 2014 |
|
14 @@ -617,6 +617,72 @@ |
|
15 sshpam_handle = NULL; |
|
16 } |
|
17 |
|
18 +#ifdef PAM_ENHANCEMENT |
|
19 +char * |
|
20 +derive_pam_service_name(Authctxt *authctxt) |
|
21 +{ |
|
22 + char *svcname = xmalloc(BUFSIZ); |
|
23 + |
|
24 + /* |
|
25 + * If PamServiceName is set we use that for everything, including |
|
26 + * SSHv1 |
|
27 + */ |
|
28 + if (options.pam_service_name != NULL) { |
|
29 + (void) strlcpy(svcname, options.pam_service_name, BUFSIZ); |
|
30 + return (svcname); |
|
31 + } |
|
32 + |
|
33 + if (compat20) { |
|
34 + char *method_name = authctxt->authmethod_name; |
|
35 + |
|
36 + if (!method_name) |
|
37 + fatal("Userauth method unknown while starting PAM"); |
|
38 + |
|
39 + /* |
|
40 + * For SSHv2 we use "sshd-<userauth name> |
|
41 + * The "sshd" prefix can be changed via the PAMServicePrefix |
|
42 + * sshd_config option. |
|
43 + */ |
|
44 + if (strcmp(method_name, "none") == 0) { |
|
45 + snprintf(svcname, BUFSIZ, "%s-none", |
|
46 + options.pam_service_prefix); |
|
47 + } |
|
48 + if (strcmp(method_name, "password") == 0) { |
|
49 + snprintf(svcname, BUFSIZ, "%s-password", |
|
50 + options.pam_service_prefix); |
|
51 + } |
|
52 + if (strcmp(method_name, "keyboard-interactive") == 0) { |
|
53 + /* "keyboard-interactive" is too long, shorten it */ |
|
54 + snprintf(svcname, BUFSIZ, "%s-kbdint", |
|
55 + options.pam_service_prefix); |
|
56 + } |
|
57 + if (strcmp(method_name, "publickey") == 0) { |
|
58 + /* "publickey" is too long, shorten it */ |
|
59 + snprintf(svcname, BUFSIZ, "%s-pubkey", |
|
60 + options.pam_service_prefix); |
|
61 + } |
|
62 + if (strcmp(method_name, "hostbased") == 0) { |
|
63 + snprintf(svcname, BUFSIZ, "%s-hostbased", |
|
64 + options.pam_service_prefix); |
|
65 + } |
|
66 + if (strncmp(method_name, "gssapi-", 7) == 0) { |
|
67 + /* |
|
68 + * Although OpenSSH only supports "gssapi-with-mic" |
|
69 + * for now. We will still map any userauth method |
|
70 + * prefixed with "gssapi-" to the gssapi PAM service. |
|
71 + */ |
|
72 + snprintf(svcname, BUFSIZ, "%s-gssapi", |
|
73 + options.pam_service_prefix); |
|
74 + } |
|
75 + return svcname; |
|
76 + } else { |
|
77 + /* SSHv1 doesn't get to be so cool */ |
|
78 + snprintf(svcname, BUFSIZ, "sshd-v1"); |
|
79 + } |
|
80 + return svcname; |
|
81 +} |
|
82 +#endif /* PAM_ENHANCEMENT */ |
|
83 + |
|
84 static int |
|
85 sshpam_init(Authctxt *authctxt) |
|
86 { |
|
87 @@ -624,18 +690,61 @@ |
|
88 const char *pam_rhost, *pam_user, *user = authctxt->user; |
|
89 const char **ptr_pam_user = &pam_user; |
|
90 |
|
91 +#ifdef PAM_ENHANCEMENT |
|
92 + const char *pam_service; |
|
93 + const char **ptr_pam_service = &pam_service; |
|
94 + char *svc = NULL; |
|
95 + |
|
96 + svc = derive_pam_service_name(authctxt); |
|
97 + debug3("PAM service is %s", svc); |
|
98 +#endif |
|
99 + |
|
100 if (sshpam_handle != NULL) { |
|
101 +#ifdef PAM_ENHANCEMENT |
|
102 + /* get the pam service name */ |
|
103 + sshpam_err = pam_get_item(sshpam_handle, |
|
104 + PAM_SERVICE, (sshpam_const void **)ptr_pam_service); |
|
105 + if (sshpam_err != PAM_SUCCESS) |
|
106 + fatal("Failed to get the PAM service name"); |
|
107 + debug3("Previous pam_service is %s", pam_service ? |
|
108 + pam_service : "NULL"); |
|
109 + |
|
110 + /* get the pam user name */ |
|
111 + sshpam_err = pam_get_item(sshpam_handle, |
|
112 + PAM_USER, (sshpam_const void **)ptr_pam_user); |
|
113 + |
|
114 + /* |
|
115 + * only need to re-start if either user or service is |
|
116 + * different. |
|
117 + */ |
|
118 + if (sshpam_err == PAM_SUCCESS && strcmp(user, pam_user) == 0 |
|
119 + && strncmp(svc, pam_service, strlen(svc)) == 0) { |
|
120 + free(svc); |
|
121 + return (0); |
|
122 + } |
|
123 + |
|
124 +#else /* Original */ |
|
125 /* We already have a PAM context; check if the user matches */ |
|
126 sshpam_err = pam_get_item(sshpam_handle, |
|
127 PAM_USER, (sshpam_const void **)ptr_pam_user); |
|
128 if (sshpam_err == PAM_SUCCESS && strcmp(user, pam_user) == 0) |
|
129 return (0); |
|
130 +#endif /* PAM_ENHANCEMENT */ |
|
131 pam_end(sshpam_handle, sshpam_err); |
|
132 sshpam_handle = NULL; |
|
133 } |
|
134 debug("PAM: initializing for \"%s\"", user); |
|
135 + |
|
136 +#ifdef PAM_ENHANCEMENT |
|
137 + debug3("Starting PAM service %s for user %s method %s", svc, user, |
|
138 + authctxt->authmethod_name); |
|
139 sshpam_err = |
|
140 + pam_start(svc, user, &store_conv, &sshpam_handle); |
|
141 + free(svc); |
|
142 +#else /* Original */ |
|
143 + sshpam_err = |
|
144 pam_start(SSHD_PAM_SERVICE, user, &store_conv, &sshpam_handle); |
|
145 +#endif |
|
146 sshpam_authctxt = authctxt; |
|
147 |
|
148 if (sshpam_err != PAM_SUCCESS) { |
|
149 --- orig/auth.h Fri Jun 20 15:39:05 2014 |
|
150 +++ new/auth.h Fri Jun 20 15:39:05 2014 |
|
151 @@ -76,6 +76,9 @@ |
|
152 #endif |
|
153 Buffer *loginmsg; |
|
154 void *methoddata; |
|
155 +#ifdef PAM_ENHANCEMENT |
|
156 + char *authmethod_name; |
|
157 +#endif |
|
158 }; |
|
159 /* |
|
160 * Every authentication method has to handle authentication requests for |
|
161 --- orig/auth2.c Fri Jun 20 15:39:05 2014 |
|
162 +++ new/auth2.c Fri Jun 20 15:39:05 2014 |
|
163 @@ -249,10 +249,13 @@ |
|
164 PRIVSEP(audit_event(SSH_INVALID_USER)); |
|
165 #endif |
|
166 } |
|
167 + |
|
168 +#ifndef PAM_ENHANCEMENT |
|
169 #ifdef USE_PAM |
|
170 if (options.use_pam) |
|
171 PRIVSEP(start_pam(authctxt)); |
|
172 #endif |
|
173 +#endif |
|
174 setproctitle("%s%s", authctxt->valid ? user : "unknown", |
|
175 use_privsep ? " [net]" : ""); |
|
176 authctxt->service = xstrdup(service); |
|
177 @@ -286,6 +289,14 @@ |
|
178 /* try to authenticate user */ |
|
179 m = authmethod_lookup(authctxt, method); |
|
180 if (m != NULL && authctxt->failures < options.max_authtries) { |
|
181 + |
|
182 +#ifdef PAM_ENHANCEMENT |
|
183 + authctxt->authmethod_name = xstrdup(method); |
|
184 + if (use_privsep) |
|
185 + mm_inform_authmethod(method); |
|
186 + if (options.use_pam) |
|
187 + PRIVSEP(start_pam(authctxt)); |
|
188 +#endif |
|
189 debug2("input_userauth_request: try method %s", method); |
|
190 authenticated = m->userauth(authctxt); |
|
191 } |
|
192 @@ -303,6 +314,10 @@ |
|
193 char *methods; |
|
194 int partial = 0; |
|
195 |
|
196 +#ifdef PAM_ENHANCEMENT |
|
197 + debug3("%s: entering", __func__); |
|
198 +#endif |
|
199 + |
|
200 if (!authctxt->valid && authenticated) |
|
201 fatal("INTERNAL ERROR: authenticated invalid user %s", |
|
202 authctxt->user); |
|
203 @@ -623,5 +638,3 @@ |
|
204 fatal("%s: method not in AuthenticationMethods", __func__); |
|
205 return 0; |
|
206 } |
|
207 - |
|
208 - |
|
209 --- orig/monitor_wrap.c Fri Jun 20 15:39:05 2014 |
|
210 +++ new/monitor_wrap.c Fri Jun 20 15:39:05 2014 |
|
211 @@ -338,6 +338,24 @@ |
|
212 buffer_free(&m); |
|
213 } |
|
214 |
|
215 +#ifdef PAM_ENHANCEMENT |
|
216 +/* Inform the privileged process about the authentication method */ |
|
217 +void |
|
218 +mm_inform_authmethod(char *authmethod) |
|
219 +{ |
|
220 + Buffer m; |
|
221 + |
|
222 + debug3("%s entering", __func__); |
|
223 + |
|
224 + buffer_init(&m); |
|
225 + buffer_put_cstring(&m, authmethod); |
|
226 + |
|
227 + mm_request_send(pmonitor->m_recvfd, MONITOR_REQ_AUTHMETHOD, &m); |
|
228 + |
|
229 + buffer_free(&m); |
|
230 +} |
|
231 +#endif |
|
232 + |
|
233 /* Do the password authentication */ |
|
234 int |
|
235 mm_auth_password(Authctxt *authctxt, char *password) |
|
236 --- orig/monitor.c Fri Jun 20 15:39:05 2014 |
|
237 +++ new/monitor.c Fri Jun 20 15:39:05 2014 |
|
238 @@ -146,6 +146,9 @@ |
|
239 int mm_answer_pwnamallow(int, Buffer *); |
|
240 int mm_answer_auth2_read_banner(int, Buffer *); |
|
241 int mm_answer_authserv(int, Buffer *); |
|
242 +#ifdef PAM_ENHANCEMENT |
|
243 +int mm_answer_authmethod(int, Buffer *); |
|
244 +#endif |
|
245 int mm_answer_authpassword(int, Buffer *); |
|
246 int mm_answer_bsdauthquery(int, Buffer *); |
|
247 int mm_answer_bsdauthrespond(int, Buffer *); |
|
248 @@ -225,10 +228,17 @@ |
|
249 {MONITOR_REQ_SIGN, MON_ONCE, mm_answer_sign}, |
|
250 {MONITOR_REQ_PWNAM, MON_ONCE, mm_answer_pwnamallow}, |
|
251 {MONITOR_REQ_AUTHSERV, MON_ONCE, mm_answer_authserv}, |
|
252 +#ifdef PAM_ENHANCEMENT |
|
253 + {MONITOR_REQ_AUTHMETHOD, MON_ISAUTH, mm_answer_authmethod}, |
|
254 +#endif |
|
255 {MONITOR_REQ_AUTH2_READ_BANNER, MON_ONCE, mm_answer_auth2_read_banner}, |
|
256 {MONITOR_REQ_AUTHPASSWORD, MON_AUTH, mm_answer_authpassword}, |
|
257 #ifdef USE_PAM |
|
258 +#ifdef PAM_ENHANCEMENT |
|
259 + {MONITOR_REQ_PAM_START, MON_ISAUTH, mm_answer_pam_start}, |
|
260 +#else |
|
261 {MONITOR_REQ_PAM_START, MON_ONCE, mm_answer_pam_start}, |
|
262 +#endif |
|
263 {MONITOR_REQ_PAM_ACCOUNT, 0, mm_answer_pam_account}, |
|
264 {MONITOR_REQ_PAM_INIT_CTX, MON_ISAUTH, mm_answer_pam_init_ctx}, |
|
265 {MONITOR_REQ_PAM_QUERY, MON_ISAUTH, mm_answer_pam_query}, |
|
266 @@ -828,6 +838,10 @@ |
|
267 /* Allow service/style information on the auth context */ |
|
268 monitor_permit(mon_dispatch, MONITOR_REQ_AUTHSERV, 1); |
|
269 monitor_permit(mon_dispatch, MONITOR_REQ_AUTH2_READ_BANNER, 1); |
|
270 +#ifdef PAM_ENHANCEMENT |
|
271 + /* Allow authmethod information on the auth context */ |
|
272 + monitor_permit(mon_dispatch, MONITOR_REQ_AUTHMETHOD, 1); |
|
273 +#endif |
|
274 } |
|
275 #ifdef USE_PAM |
|
276 if (options.use_pam) |
|
277 @@ -868,7 +882,25 @@ |
|
278 return (0); |
|
279 } |
|
280 |
|
281 +#ifdef PAM_ENHANCEMENT |
|
282 int |
|
283 +mm_answer_authmethod(int sock, Buffer *m) |
|
284 +{ |
|
285 + monitor_permit_authentications(1); |
|
286 + |
|
287 + authctxt->authmethod_name = buffer_get_string(m, NULL); |
|
288 + debug3("%s: authmethod_name=%s", __func__, authctxt->authmethod_name); |
|
289 + |
|
290 + if (strlen(authctxt->authmethod_name) == 0) { |
|
291 + free(authctxt->authmethod_name); |
|
292 + authctxt->authmethod_name = NULL; |
|
293 + } |
|
294 + |
|
295 + return (0); |
|
296 +} |
|
297 +#endif |
|
298 + |
|
299 +int |
|
300 mm_answer_authpassword(int sock, Buffer *m) |
|
301 { |
|
302 static int call_count; |
|
303 --- orig/monitor.h Fri Jun 20 15:39:05 2014 |
|
304 +++ new/monitor.h Fri Jun 20 15:39:05 2014 |
|
305 @@ -70,6 +70,9 @@ |
|
306 MONITOR_REQ_PAM_FREE_CTX = 110, MONITOR_ANS_PAM_FREE_CTX = 111, |
|
307 MONITOR_REQ_AUDIT_EVENT = 112, MONITOR_REQ_AUDIT_COMMAND = 113, |
|
308 |
|
309 +#ifdef PAM_ENHANCEMENT |
|
310 + MONITOR_REQ_AUTHMETHOD = 114, |
|
311 +#endif |
|
312 }; |
|
313 |
|
314 struct mm_master; |
|
315 --- orig/servconf.c Fri Jun 20 15:39:05 2014 |
|
316 +++ new/servconf.c Fri Jun 20 15:39:05 2014 |
|
317 @@ -154,6 +154,10 @@ |
|
318 options->ip_qos_interactive = -1; |
|
319 options->ip_qos_bulk = -1; |
|
320 options->version_addendum = NULL; |
|
321 +#ifdef PAM_ENHANCEMENT |
|
322 + options->pam_service_name = NULL; |
|
323 + options->pam_service_prefix = NULL; |
|
324 +#endif |
|
325 } |
|
326 |
|
327 void |
|
328 @@ -303,6 +307,12 @@ |
|
329 options->ip_qos_bulk = IPTOS_THROUGHPUT; |
|
330 if (options->version_addendum == NULL) |
|
331 options->version_addendum = xstrdup(""); |
|
332 + |
|
333 +#ifdef PAM_ENHANCEMENT |
|
334 + if (options->pam_service_prefix == NULL) |
|
335 + options->pam_service_prefix = _SSH_PAM_SERVICE_PREFIX; |
|
336 +#endif |
|
337 + |
|
338 /* Turn privilege separation on by default */ |
|
339 if (use_privsep == -1) |
|
340 use_privsep = PRIVSEP_NOSANDBOX; |
|
341 @@ -351,6 +361,9 @@ |
|
342 sKexAlgorithms, sIPQoS, sVersionAddendum, |
|
343 sAuthorizedKeysCommand, sAuthorizedKeysCommandUser, |
|
344 sAuthenticationMethods, sHostKeyAgent, |
|
345 +#ifdef PAM_ENHANCEMENT |
|
346 + sPAMServicePrefix, sPAMServiceName, |
|
347 +#endif |
|
348 sDeprecated, sUnsupported |
|
349 } ServerOpCodes; |
|
350 |
|
351 @@ -482,6 +495,10 @@ |
|
352 { "authorizedkeyscommanduser", sAuthorizedKeysCommandUser, SSHCFG_ALL }, |
|
353 { "versionaddendum", sVersionAddendum, SSHCFG_GLOBAL }, |
|
354 { "authenticationmethods", sAuthenticationMethods, SSHCFG_ALL }, |
|
355 +#ifdef PAM_ENHANCEMENT |
|
356 + { "pamserviceprefix", sPAMServicePrefix, SSHCFG_GLOBAL }, |
|
357 + { "pamservicename", sPAMServiceName, SSHCFG_GLOBAL }, |
|
358 +#endif |
|
359 { NULL, sBadOption, 0 } |
|
360 }; |
|
361 |
|
362 @@ -1632,6 +1649,30 @@ |
|
363 } |
|
364 return 0; |
|
365 |
|
366 + case sPAMServicePrefix: |
|
367 + arg = strdelim(&cp); |
|
368 + if (!arg || *arg == '\0') |
|
369 + fatal("%s line %d: Missing argument.", |
|
370 + filename, linenum); |
|
371 + if (options->pam_service_name != NULL) |
|
372 + fatal("%s line %d: PAMServiceName and PAMServicePrefix" |
|
373 + " are mutually exclusive.", filename, linenum); |
|
374 + if (options->pam_service_prefix == NULL) |
|
375 + options->pam_service_prefix = xstrdup(arg); |
|
376 + break; |
|
377 + |
|
378 + case sPAMServiceName: |
|
379 + arg = strdelim(&cp); |
|
380 + if (!arg || *arg == '\0') |
|
381 + fatal("%s line %d: Missing argument.", |
|
382 + filename, linenum); |
|
383 + if (options->pam_service_prefix != NULL) |
|
384 + fatal("%s line %d: PAMServiceName and PAMServicePrefix" |
|
385 + " are mutually exclusive.", filename, linenum); |
|
386 + if (options->pam_service_name == NULL) |
|
387 + options->pam_service_name = xstrdup(arg); |
|
388 + break; |
|
389 + |
|
390 case sDeprecated: |
|
391 logit("%s line %d: Deprecated option %s", |
|
392 filename, linenum, arg); |
|
393 --- orig/servconf.h Fri Jun 20 15:39:05 2014 |
|
394 +++ new/servconf.h Fri Jun 20 15:39:05 2014 |
|
395 @@ -54,6 +54,10 @@ |
|
396 /* Magic name for internal sftp-server */ |
|
397 #define INTERNAL_SFTP_NAME "internal-sftp" |
|
398 |
|
399 +#ifdef PAM_ENHANCEMENT |
|
400 +#define _SSH_PAM_SERVICE_PREFIX "sshd" |
|
401 +#endif |
|
402 + |
|
403 typedef struct { |
|
404 u_int num_ports; |
|
405 u_int ports_from_cmdline; |
|
406 @@ -185,6 +189,12 @@ |
|
407 |
|
408 u_int num_auth_methods; |
|
409 char *auth_methods[MAX_AUTH_METHODS]; |
|
410 + |
|
411 +#ifdef PAM_ENHANCEMENT |
|
412 + char *pam_service_prefix; |
|
413 + char *pam_service_name; |
|
414 +#endif |
|
415 + |
|
416 } ServerOptions; |
|
417 |
|
418 /* Information about the incoming connection as used by Match */ |
|
419 --- orig/sshd_config.5 Fri Jun 20 15:39:05 2014 |
|
420 +++ new/sshd_config.5 Fri Jun 20 15:39:05 2014 |
|
421 @@ -868,6 +868,21 @@ |
|
422 are refused if the number of unauthenticated connections reaches |
|
423 .Dq full |
|
424 (60). |
|
425 +.It Cm PAMServiceName |
|
426 +Specifies the PAM service name for the PAM session. The PAMServiceName and |
|
427 +PAMServicePrefix options are mutually exclusive and if both set, sshd does not |
|
428 +start. If this option is set the service name is the same for all user |
|
429 +authentication methods. The option has no default value. See PAMServicePrefix |
|
430 +for more information. |
|
431 +.It Cm PAMServicePrefix |
|
432 +Specifies the PAM service name prefix for service names used for individual |
|
433 +user authentication methods. The default is sshd. The PAMServiceName and |
|
434 +PAMServicePrefix options are mutually exclusive and if both set, sshd does not |
|
435 +start. |
|
436 +.Pp |
|
437 +For example, if this option is set to admincli, the service name for the |
|
438 +keyboard-interactive authentication method is admincli-kbdint instead of the |
|
439 +default sshd-kbdint. |
|
440 .It Cm PasswordAuthentication |
|
441 Specifies whether password authentication is allowed. |
|
442 The default is |
|
443 --- orig/sshd.8 Fri Jun 20 15:39:05 2014 |
|
444 +++ new/sshd.8 Fri Jun 20 15:39:05 2014 |
|
445 @@ -951,6 +951,33 @@ |
|
446 started last). |
|
447 The content of this file is not sensitive; it can be world-readable. |
|
448 .El |
|
449 + |
|
450 +.Sh SECURITY |
|
451 +sshd uses pam(3PAM) for password and keyboard-interactive methods as well as |
|
452 +for account management, session management, and the password management for all |
|
453 +authentication methods. |
|
454 +.Pp |
|
455 +Each SSHv2 userauth type has its own PAM service name: |
|
456 + |
|
457 +.Bd -literal -offset 3n |
|
458 + |
|
459 +----------------------------------------------- |
|
460 +| SSHv2 Userauth | PAM Service Name | |
|
461 +----------------------------------------------- |
|
462 +| none | sshd-none | |
|
463 +----------------------------------------------- |
|
464 +| password | sshd-password | |
|
465 +----------------------------------------------- |
|
466 +| keyboard-interactive | sshd-kbdint | |
|
467 +----------------------------------------------- |
|
468 +| pubkey | sshd-pubkey | |
|
469 +----------------------------------------------- |
|
470 +| hostbased | sshd-hostbased | |
|
471 +----------------------------------------------- |
|
472 +| gssapi-with-mic | sshd-gssapi | |
|
473 +----------------------------------------------- |
|
474 +.Ed |
|
475 + |
|
476 .Sh SEE ALSO |
|
477 .Xr scp 1 , |
|
478 .Xr sftp 1 , |