2 * (c) 2006 Quest Software, Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
7 * 1. Redistributions of source code must retain the above copyright notice,
8 * this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of Quest Software, Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from this
16 * software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
34 #include <sys/types.h>
50 /* Pseudo-boolean types */
57 static vas_ctx_t *sudo_vas_ctx;
58 static vas_id_t *sudo_vas_id;
59 /* Don't use VAS_NAME_FLAG_NO_CACHE or lookups just won't work.
60 * -tedp, 2006-08-29 */
61 static const int update_flags = 0;
62 static int sudo_vas_available = 0;
63 static char *err_msg = NULL;
64 static void *libvas_handle = NULL;
66 /* libvas functions */
67 static vas_err_t (*v_ctx_alloc) (vas_ctx_t **ctx);
68 static void (*v_ctx_free) (vas_ctx_t *ctx);
69 static vas_err_t (*v_id_alloc) (vas_ctx_t *ctx, const char *name, vas_id_t **id);
70 static void (*v_id_free) (vas_ctx_t *ctx, vas_id_t *id);
71 static vas_err_t (*v_id_establish_cred_keytab) (vas_ctx_t *ctx, vas_id_t *id, int credflags, const char *keytab);
72 static vas_err_t (*v_user_init) (vas_ctx_t *ctx, vas_id_t *id, const char *name, int flags, vas_user_t **user);
73 static void (*v_user_free) (vas_ctx_t *ctx, vas_user_t *user);
74 static vas_err_t (*v_group_init) (vas_ctx_t *ctx, vas_id_t *id, const char *name, int flags, vas_group_t **group);
75 static void (*v_group_free) (vas_ctx_t *ctx, vas_group_t *group);
76 static vas_err_t (*v_user_is_member) (vas_ctx_t *ctx, vas_id_t *id, vas_user_t *user, vas_group_t *group);
77 static const char* (*v_err_get_string) (vas_ctx_t *ctx, int with_cause);
80 static int resolve_vas_funcs(void);
84 * Whether nonunix group lookups are available.
85 * @return 1 if available, 0 if not.
88 sudo_nonunix_groupcheck_available(void)
90 return sudo_vas_available;
95 * Check if the user is in the group
96 * @param group group name which can be in DOMAIN\sam format or just the group
98 * @param user user name
100 * @return 1 if user is a member of the group, 0 if not (or error occurred)
103 sudo_nonunix_groupcheck( const char* group, const char* user, const struct passwd* pwd )
105 static int error_cause_shown = FALSE;
108 vas_user_t* vas_user = NULL;
109 vas_group_t* vas_group = NULL;
111 if (!sudo_vas_available) {
112 if (error_cause_shown == FALSE) {
113 /* Produce the saved error reason */
114 log_error(NO_MAIL|NO_EXIT, "Non-unix group checking unavailable: %s",
116 : "(unknown cause)");
117 error_cause_shown = TRUE;
122 /* resolve the user and group. The user will be a real Unix account name,
123 * while the group may be a unix name, or any group name accepted by
124 * vas_name_to_dn, which means any of:
126 * - Group Name@FULLY.QUALIFIED.DOMAIN
127 * - CN=sudoers,CN=Users,DC=rcdev,DC=vintela,DC=com
128 * - S-1-2-34-5678901234-5678901234-5678901234-567
130 * XXX - we may get non-VAS user accounts here. You can add local users to an
131 * Active Directory group through override files. Should we handle that case?
133 if( (vaserr = v_user_init( sudo_vas_ctx, sudo_vas_id, user, update_flags, &vas_user )) != VAS_ERR_SUCCESS ) {
134 if (vaserr == VAS_ERR_NOT_FOUND) {
135 /* No such user in AD. Probably a local user. */
136 vaserr = VAS_ERR_SUCCESS;
141 if( (vaserr = v_group_init( sudo_vas_ctx, sudo_vas_id, group, update_flags, &vas_group )) != VAS_ERR_SUCCESS ) {
145 /* do the membership check */
146 if( (vaserr = v_user_is_member( sudo_vas_ctx, sudo_vas_id, vas_user, vas_group )) == VAS_ERR_SUCCESS ) {
149 else if (vaserr == VAS_ERR_NOT_FOUND) {
150 /* fake the vaserr code so no error is triggered */
151 vaserr = VAS_ERR_SUCCESS;
155 FINISHED: /* cleanups */
156 if (vaserr != VAS_ERR_SUCCESS) {
157 int error_flags = NO_MAIL | MSG_ONLY | (uses_inversion ? 0 : NO_EXIT);
159 log_error(error_flags, "Error while checking group membership "
160 "for user \"%s\", group \"%s\", error: %s%s.", user, group,
161 v_err_get_string(sudo_vas_ctx, 1),
162 /* A helpful hint if there seems to be a non-FQDN as the domain */
163 (strchr(group, '@') && !strchr(group, '.'))
164 ? "\nMake sure the fully qualified domain name is specified"
167 if( vas_group ) v_group_free( sudo_vas_ctx, vas_group );
168 if( vas_user ) v_user_free( sudo_vas_ctx, vas_user );
175 set_err_msg(const char *msg, ...) {
178 if (!msg) /* assert */
186 if (vasprintf(&err_msg, msg, ap) == -1)
194 * Initialise nonunix_groupcheck state.
197 sudo_nonunix_groupcheck_init(void)
207 libvas = dlopen(LIBVAS_SO, RTLD_LAZY);
209 set_err_msg("dlopen() failed: %s", dlerror());
213 libvas_handle = libvas;
215 if (resolve_vas_funcs() != 0)
218 if (VAS_ERR_SUCCESS == (vaserr = v_ctx_alloc(&sudo_vas_ctx))) {
220 if (VAS_ERR_SUCCESS == (vaserr = v_id_alloc(sudo_vas_ctx, "host/", &sudo_vas_id))) {
222 if (update_flags & VAS_NAME_FLAG_NO_LDAP) {
223 sudo_vas_available = 1;
225 } else { /* Get a keytab */
226 if ((vaserr = v_id_establish_cred_keytab( sudo_vas_ctx,
228 VAS_ID_FLAG_USE_MEMORY_CCACHE
229 | VAS_ID_FLAG_KEEP_COPY_OF_CRED
230 | VAS_ID_FLAG_NO_INITIAL_TGT,
231 NULL )) == VAS_ERR_SUCCESS) {
232 sudo_vas_available = 1;
237 set_err_msg("unable to establish creds: %s",
238 v_err_get_string(sudo_vas_ctx, 1));
241 v_id_free(sudo_vas_ctx, sudo_vas_id);
245 /* This is the last opportunity to get an error message from libvas */
247 set_err_msg("Error initializing non-unix group checking: %s",
248 v_err_get_string(sudo_vas_ctx, 1));
250 v_ctx_free(sudo_vas_ctx);
255 set_err_msg("Failed to get a libvas handle for non-unix group checking (unknown cause)");
257 sudo_vas_available = 0;
262 * Clean up nonunix_groupcheck state.
265 sudo_nonunix_groupcheck_cleanup()
272 if (sudo_vas_available) {
273 v_id_free(sudo_vas_ctx, sudo_vas_id);
276 v_ctx_free(sudo_vas_ctx);
279 sudo_vas_available = FALSE;
283 if (dlclose(libvas_handle) != 0)
284 log_error(NO_MAIL|NO_EXIT, "dlclose() failed: %s", dlerror());
285 libvas_handle = NULL;
289 #define RESOLVE_OR_ERR(fptr, sym) \
291 void *_fptr = dlsym(libvas_handle, (sym)); \
293 set_err_msg("dlsym() failed: %s", dlerror()); \
301 * Resolve all the libvas functions.
302 * Returns -1 and sets err_msg if something went wrong, or 0 on success.
305 resolve_vas_funcs(void)
307 if (!libvas_handle) /* assert */
310 RESOLVE_OR_ERR(v_ctx_alloc, "vas_ctx_alloc");
311 RESOLVE_OR_ERR(v_ctx_free, "vas_ctx_free");
312 RESOLVE_OR_ERR(v_id_alloc, "vas_id_alloc");
313 RESOLVE_OR_ERR(v_id_free, "vas_id_free");
314 RESOLVE_OR_ERR(v_id_establish_cred_keytab, "vas_id_establish_cred_keytab");
315 RESOLVE_OR_ERR(v_user_init, "vas_user_init");
316 RESOLVE_OR_ERR(v_user_free, "vas_user_free");
317 RESOLVE_OR_ERR(v_group_init, "vas_group_init");
318 RESOLVE_OR_ERR(v_group_free, "vas_group_free");
319 RESOLVE_OR_ERR(v_user_is_member, "vas_user_is_member");
320 RESOLVE_OR_ERR(v_err_get_string, "vas_err_get_string");