Annotation of src/usr.bin/sudo/vasgroups.c, Revision 1.3
1.1 millert 1: /*
2: * (c) 2006 Quest Software, Inc. All rights reserved.
3: *
4: * Redistribution and use in source and binary forms, with or without
5: * modification, are permitted provided that the following conditions are met:
6: *
7: * 1. Redistributions of source code must retain the above copyright notice,
8: * this list of conditions and the following disclaimer.
9: *
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.
13: *
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.
17: *
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.
29: */
30:
31: #include "config.h"
32:
33: #include <stdlib.h>
34: #include <sys/types.h>
35: #include <pwd.h>
36: #include <string.h>
37: #include <errno.h>
38: #include <stdio.h>
39: #include <dlfcn.h>
40:
41: #include <vas.h>
42:
43: #include "compat.h"
44: #include "logging.h"
45: #include "nonunix.h"
1.2 millert 46: #include "sudo.h"
1.1 millert 47: #include "parse.h"
48:
49:
50: /* Pseudo-boolean types */
51: #undef TRUE
52: #undef FALSE
53: #define FALSE 0
54: #define TRUE 1
55:
56:
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;
65:
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);
78:
79:
80: static int resolve_vas_funcs(void);
81:
82:
83: /**
84: * Whether nonunix group lookups are available.
85: * @return 1 if available, 0 if not.
86: */
87: int
88: sudo_nonunix_groupcheck_available(void)
89: {
90: return sudo_vas_available;
91: }
92:
93:
94: /**
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
97: * name
98: * @param user user name
99: * @param pwd (unused)
100: * @return 1 if user is a member of the group, 0 if not (or error occurred)
101: */
102: int
103: sudo_nonunix_groupcheck( const char* group, const char* user, const struct passwd* pwd )
104: {
105: static int error_cause_shown = FALSE;
106: int rval = FALSE;
107: vas_err_t vaserr;
108: vas_user_t* vas_user = NULL;
109: vas_group_t* vas_group = NULL;
110:
111: if (!sudo_vas_available) {
112: if (error_cause_shown == FALSE) {
113: /* Produce the saved error reason */
1.3 ! millert 114: warningx("Non-unix group checking unavailable: %s",
1.1 millert 115: err_msg ? err_msg
116: : "(unknown cause)");
117: error_cause_shown = TRUE;
118: }
119: return 0;
120: }
121:
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:
125: * - Group Name
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
129: *
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?
132: * */
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;
137: }
138: goto FINISHED;
139: }
140:
141: if( (vaserr = v_group_init( sudo_vas_ctx, sudo_vas_id, group, update_flags, &vas_group )) != VAS_ERR_SUCCESS ) {
142: goto FINISHED;
143: }
144:
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 ) {
147: rval = TRUE;
148: }
149: else if (vaserr == VAS_ERR_NOT_FOUND) {
150: /* fake the vaserr code so no error is triggered */
151: vaserr = VAS_ERR_SUCCESS;
152: }
153:
154:
155: FINISHED: /* cleanups */
1.2 millert 156: if (vaserr != VAS_ERR_SUCCESS && vaserr != VAS_ERR_NOT_FOUND ) {
1.3 ! millert 157: warningx("Error while checking group membership "
1.1 millert 158: "for user \"%s\", group \"%s\", error: %s%s.", user, group,
159: v_err_get_string(sudo_vas_ctx, 1),
160: /* A helpful hint if there seems to be a non-FQDN as the domain */
161: (strchr(group, '@') && !strchr(group, '.'))
162: ? "\nMake sure the fully qualified domain name is specified"
163: : "");
164: }
165: if( vas_group ) v_group_free( sudo_vas_ctx, vas_group );
166: if( vas_user ) v_user_free( sudo_vas_ctx, vas_user );
167:
168: return(rval);
169: }
170:
171:
172: static void
173: set_err_msg(const char *msg, ...) {
174: va_list ap;
175:
176: if (!msg) /* assert */
177: return;
178:
179: if (err_msg)
180: free(err_msg);
181:
182: va_start(ap, msg);
183:
184: if (vasprintf(&err_msg, msg, ap) == -1)
185: err_msg = NULL;
186:
187: va_end(ap);
188: }
189:
190:
191: /**
192: * Initialise nonunix_groupcheck state.
193: */
194: void
195: sudo_nonunix_groupcheck_init(void)
196: {
197: vas_err_t vaserr;
198: void *libvas;
199:
200: if (err_msg) {
201: free(err_msg);
202: err_msg = NULL;
203: }
204:
205: libvas = dlopen(LIBVAS_SO, RTLD_LAZY);
206: if (!libvas) {
207: set_err_msg("dlopen() failed: %s", dlerror());
208: return;
209: }
210:
211: libvas_handle = libvas;
212:
213: if (resolve_vas_funcs() != 0)
214: return;
215:
216: if (VAS_ERR_SUCCESS == (vaserr = v_ctx_alloc(&sudo_vas_ctx))) {
217:
218: if (VAS_ERR_SUCCESS == (vaserr = v_id_alloc(sudo_vas_ctx, "host/", &sudo_vas_id))) {
219:
220: if (update_flags & VAS_NAME_FLAG_NO_LDAP) {
221: sudo_vas_available = 1;
222: return; /* OK */
223: } else { /* Get a keytab */
224: if ((vaserr = v_id_establish_cred_keytab( sudo_vas_ctx,
225: sudo_vas_id,
226: VAS_ID_FLAG_USE_MEMORY_CCACHE
227: | VAS_ID_FLAG_KEEP_COPY_OF_CRED
228: | VAS_ID_FLAG_NO_INITIAL_TGT,
229: NULL )) == VAS_ERR_SUCCESS) {
230: sudo_vas_available = 1;
231: return; /* OK */
232: }
233:
234: if (!err_msg)
235: set_err_msg("unable to establish creds: %s",
236: v_err_get_string(sudo_vas_ctx, 1));
237: }
238:
239: v_id_free(sudo_vas_ctx, sudo_vas_id);
240: sudo_vas_id = NULL;
241: }
242:
243: /* This is the last opportunity to get an error message from libvas */
244: if (!err_msg)
245: set_err_msg("Error initializing non-unix group checking: %s",
246: v_err_get_string(sudo_vas_ctx, 1));
247:
248: v_ctx_free(sudo_vas_ctx);
249: sudo_vas_ctx = NULL;
250: }
251:
252: if (!err_msg)
253: set_err_msg("Failed to get a libvas handle for non-unix group checking (unknown cause)");
254:
255: sudo_vas_available = 0;
256: }
257:
258:
259: /**
260: * Clean up nonunix_groupcheck state.
261: */
262: void
263: sudo_nonunix_groupcheck_cleanup()
264: {
265: if (err_msg) {
266: free(err_msg);
267: err_msg = NULL;
268: }
269:
270: if (sudo_vas_available) {
271: v_id_free(sudo_vas_ctx, sudo_vas_id);
272: sudo_vas_id = NULL;
273:
274: v_ctx_free(sudo_vas_ctx);
275: sudo_vas_ctx = NULL;
276:
277: sudo_vas_available = FALSE;
278: }
279:
280: if (libvas_handle) {
281: if (dlclose(libvas_handle) != 0)
1.3 ! millert 282: warningx("dlclose() failed: %s", dlerror());
1.1 millert 283: libvas_handle = NULL;
284: }
285: }
286:
287: #define RESOLVE_OR_ERR(fptr, sym) \
288: do { \
289: void *_fptr = dlsym(libvas_handle, (sym)); \
290: if (!_fptr) { \
291: set_err_msg("dlsym() failed: %s", dlerror()); \
292: return -1; \
293: } \
294: fptr = _fptr; \
295: } while (0)
296:
297:
298: /**
299: * Resolve all the libvas functions.
300: * Returns -1 and sets err_msg if something went wrong, or 0 on success.
301: */
302: int
303: resolve_vas_funcs(void)
304: {
305: if (!libvas_handle) /* assert */
306: return -1;
307:
308: RESOLVE_OR_ERR(v_ctx_alloc, "vas_ctx_alloc");
309: RESOLVE_OR_ERR(v_ctx_free, "vas_ctx_free");
310: RESOLVE_OR_ERR(v_id_alloc, "vas_id_alloc");
311: RESOLVE_OR_ERR(v_id_free, "vas_id_free");
312: RESOLVE_OR_ERR(v_id_establish_cred_keytab, "vas_id_establish_cred_keytab");
313: RESOLVE_OR_ERR(v_user_init, "vas_user_init");
314: RESOLVE_OR_ERR(v_user_free, "vas_user_free");
315: RESOLVE_OR_ERR(v_group_init, "vas_group_init");
316: RESOLVE_OR_ERR(v_group_free, "vas_group_free");
317: RESOLVE_OR_ERR(v_user_is_member, "vas_user_is_member");
318: RESOLVE_OR_ERR(v_err_get_string, "vas_err_get_string");
319:
320: return 0;
321: }