4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #include <kadm5/admin.h>
28 #include <security/pam_appl.h>
29 #include <security/pam_modules.h>
30 #include <security/pam_impl.h>
38 #define KRB5_AUTOMIGRATE_DATA "SUNW-KRB5-AUTOMIGRATE-DATA"
40 static void krb5_migrate_cleanup(pam_handle_t
*pamh
, void *data
,
44 * pam_sm_authenticate - Authenticate a host-based client service
45 * principal to kadmind in order to permit the creation of a new user
46 * principal in the client's default realm.
48 int pam_sm_authenticate(pam_handle_t
*pamh
, int flags
,
49 int argc
, const char **argv
)
52 char *userdata
= NULL
;
53 char *olduserdata
= NULL
;
54 char *password
= NULL
;
58 /* pam.conf options */
64 /* krb5-specific defines */
65 kadm5_ret_t retval
= 0;
66 krb5_context context
= NULL
;
67 kadm5_config_params params
;
68 krb5_principal svcprinc
;
69 char *svcprincstr
= NULL
;
70 krb5_principal userprinc
;
71 char *userprincstr
= NULL
;
73 kadm5_principal_ent_rec kadm5_userprinc
;
74 char *kadmin_princ
= NULL
;
75 char *def_realm
= NULL
;
79 for (i
= 0; i
< argc
; i
++) {
80 if (strcmp(argv
[i
], "debug") == 0) {
82 } else if (strcmp(argv
[i
], "quiet") == 0) {
84 } else if (strcmp(argv
[i
], "expire_pw") == 0) {
86 } else if ((strstr(argv
[i
], "client_service=") != NULL
) &&
87 (strcmp((strstr(argv
[i
], "=") + 1), "") != 0)) {
88 service
= strdup(strstr(argv
[i
], "=") + 1);
90 __pam_log(LOG_AUTH
| LOG_ERR
,
91 "PAM-KRB5-AUTOMIGRATE (auth): unrecognized "
92 "option %s", argv
[i
]);
96 if (flags
& PAM_SILENT
)
99 err
= pam_get_item(pamh
, PAM_USER
, (void**)&user
);
100 if (err
!= PAM_SUCCESS
) {
105 * Check if user name is *not* NULL
107 if (user
== NULL
|| (user
[0] == '\0')) {
109 __pam_log(LOG_AUTH
| LOG_DEBUG
,
110 "PAM-KRB5-AUTOMIGRATE (auth): user empty or null");
115 * Can't tolerate memory failure later on. Get a copy
116 * before any work is done.
118 if ((userdata
= strdup(user
)) == NULL
) {
119 __pam_log(LOG_AUTH
| LOG_ERR
,
120 "PAM-KRB5-AUTOMIGRATE (auth): Out of memory");
125 * Grok the user password
127 err
= pam_get_item(pamh
, PAM_AUTHTOK
, (void **)&password
);
128 if (err
!= PAM_SUCCESS
) {
132 if (password
== NULL
|| (password
[0] == '\0')) {
134 __pam_log(LOG_AUTH
| LOG_DEBUG
,
135 "PAM-KRB5-AUTOMIGRATE (auth): "
136 "authentication token is empty or null");
142 * Now, lets do the all krb5/kadm5 setup for the principal addition
144 if (retval
= krb5_init_secure_context(&context
)) {
145 __pam_log(LOG_AUTH
| LOG_ERR
,
146 "PAM-KRB5-AUTOMIGRATE (auth): Error initializing "
147 "krb5: %s", error_message(retval
));
151 (void) memset((char *)¶ms
, 0, sizeof (params
));
152 (void) memset(&kadm5_userprinc
, 0, sizeof (kadm5_userprinc
));
154 if (def_realm
== NULL
&& krb5_get_default_realm(context
, &def_realm
)) {
155 __pam_log(LOG_AUTH
| LOG_ERR
,
156 "PAM-KRB5-AUTOMIGRATE (auth): Error while obtaining "
157 "default krb5 realm");
161 params
.mask
|= KADM5_CONFIG_REALM
;
162 params
.realm
= def_realm
;
164 if (kadm5_get_adm_host_srv_name(context
, def_realm
,
166 __pam_log(LOG_AUTH
| LOG_ERR
,
167 "PAM-KRB5-AUTOMIGRATE (auth): Error while obtaining "
168 "host based service name for realm %s\n", def_realm
);
172 if (retval
= krb5_sname_to_principal(context
, NULL
,
173 (service
!= NULL
) ? service
: "host", KRB5_NT_SRV_HST
, &svcprinc
)) {
174 __pam_log(LOG_AUTH
| LOG_ERR
,
175 "PAM-KRB5-AUTOMIGRATE (auth): Error while creating "
176 "krb5 host service principal: %s",
177 error_message(retval
));
181 if (retval
= krb5_unparse_name(context
, svcprinc
,
183 __pam_log(LOG_AUTH
| LOG_ERR
,
184 "PAM-KRB5-AUTOMIGRATE (auth): Error while "
185 "unparsing principal name: %s", error_message(retval
));
186 krb5_free_principal(context
, svcprinc
);
190 krb5_free_principal(context
, svcprinc
);
193 * Initialize the kadm5 connection using the default keytab
195 retval
= kadm5_init_with_skey(svcprincstr
, NULL
,
196 kadmin_princ
, ¶ms
, KADM5_STRUCT_VERSION
, KADM5_API_VERSION_2
,
199 __pam_log(LOG_AUTH
| LOG_ERR
,
200 "PAM-KRB5-AUTOMIGRATE (auth): Error while "
201 "doing kadm5_init_with_skey: %s", error_message(retval
));
207 * The RPCSEC_GSS connection has been established; Lets check to see
208 * if the corresponding user principal exists in the KDC database.
209 * If not, lets create a new one.
212 strlength
= strlen(user
) + strlen(def_realm
) + 2;
213 if ((userprincstr
= malloc(strlength
)) == NULL
)
215 (void) strlcpy(userprincstr
, user
, strlength
);
216 (void) strlcat(userprincstr
, "@", strlength
);
217 (void) strlcat(userprincstr
, def_realm
, strlength
);
220 if (retval
= krb5_parse_name(context
, userprincstr
,
222 __pam_log(LOG_AUTH
| LOG_ERR
,
223 "PAM-KRB5-AUTOMIGRATE (auth): Error while "
224 "parsing user principal name: %s",
225 error_message(retval
));
229 retval
= kadm5_get_principal(handle
, userprinc
, &kadm5_userprinc
,
230 KADM5_PRINCIPAL_NORMAL_MASK
);
232 krb5_free_principal(context
, userprinc
);
238 __pam_log(LOG_AUTH
| LOG_DEBUG
,
239 "PAM-KRB5-AUTOMIGRATE (auth): %s does "
240 "not have the GET privilege "
241 "for kadm5_get_principal: %s",
242 svcprincstr
, error_message(retval
));
245 case KADM5_UNK_PRINC
:
250 * We will try & add this principal anyways, continue on ...
252 (void) memset(&kadm5_userprinc
, 0, sizeof (kadm5_userprinc
));
255 * Principal already exists in the KDC database, quit now
258 __pam_log(LOG_AUTH
| LOG_DEBUG
,
259 "PAM-KRB5-AUTOMIGRATE (auth): Principal %s "
260 "already exists in Kerberos KDC database",
267 if (retval
= krb5_parse_name(context
, userprincstr
,
268 &(kadm5_userprinc
.principal
))) {
269 __pam_log(LOG_AUTH
| LOG_ERR
,
270 "PAM-KRB5-AUTOMIGRATE (auth): Error while "
271 "parsing user principal name: %s",
272 error_message(retval
));
279 * The local system time could actually be later than the
280 * system time of the KDC we are authenticating to. We expire
281 * w/the local system time minus clockskew so that we are
282 * assured that it is expired on this login, not the next.
284 now
-= context
->clockskew
;
285 kadm5_userprinc
.pw_expiration
= now
;
286 mask
|= KADM5_PW_EXPIRATION
;
289 mask
|= KADM5_PRINCIPAL
;
290 retval
= kadm5_create_principal(handle
, &kadm5_userprinc
,
296 __pam_log(LOG_AUTH
| LOG_DEBUG
,
297 "PAM-KRB5-AUTOMIGRATE (auth): %s does "
298 "not have the ADD privilege "
299 "for kadm5_create_principal: %s",
300 svcprincstr
, error_message(retval
));
304 __pam_log(LOG_AUTH
| LOG_ERR
,
305 "PAM-KRB5-AUTOMIGRATE (auth): Generic error"
306 "while doing kadm5_create_principal: %s",
307 error_message(retval
));
314 * Success, new user principal has been added !
317 char messages
[PAM_MAX_NUM_MSG
][PAM_MAX_MSG_SIZE
];
319 (void) snprintf(messages
[0], sizeof (messages
[0]),
320 dgettext(TEXT_DOMAIN
, "\nUser `%s' has been "
321 "automatically migrated to the Kerberos realm %s\n"),
323 (void) __pam_display_msg(pamh
, PAM_TEXT_INFO
, 1,
327 __pam_log(LOG_AUTH
| LOG_DEBUG
,
328 "PAM-KRB5-AUTOMIGRATE (auth): User %s "
329 "has been added to the Kerberos KDC database",
333 * Since this is a new krb5 principal, do a pam_set_data()
334 * for possible use by the acct_mgmt routine of pam_krb5(5)
336 if (pam_get_data(pamh
, KRB5_AUTOMIGRATE_DATA
,
337 (const void **)&olduserdata
) == PAM_SUCCESS
) {
339 * We created a princ in a previous run on the same handle and
340 * it must have been for a different PAM_USER / princ name,
341 * otherwise we couldn't succeed here, unless that princ
346 if (pam_set_data(pamh
, KRB5_AUTOMIGRATE_DATA
, userdata
,
347 krb5_migrate_cleanup
) != PAM_SUCCESS
) {
357 (void) kadm5_free_principal_ent(handle
, &kadm5_userprinc
);
358 (void) kadm5_destroy((void *)handle
);
360 krb5_free_context(context
);
367 krb5_migrate_cleanup(pam_handle_t
*pamh
, void *data
, int pam_status
) {
374 pam_sm_setcred(pam_handle_t
*pamh
, int flags
, int argc
, const char **argv
)