Annotation of src/usr.bin/sudo/vasgroups.c, Revision 1.2
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 */
114: log_error(NO_MAIL|NO_EXIT, "Non-unix group checking unavailable: %s",
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 ) {
! 157: int error_flags = NO_MAIL | MSG_ONLY | NO_EXIT;
1.1 millert 158:
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"
165: : "");
166: }
167: if( vas_group ) v_group_free( sudo_vas_ctx, vas_group );
168: if( vas_user ) v_user_free( sudo_vas_ctx, vas_user );
169:
170: return(rval);
171: }
172:
173:
174: static void
175: set_err_msg(const char *msg, ...) {
176: va_list ap;
177:
178: if (!msg) /* assert */
179: return;
180:
181: if (err_msg)
182: free(err_msg);
183:
184: va_start(ap, msg);
185:
186: if (vasprintf(&err_msg, msg, ap) == -1)
187: err_msg = NULL;
188:
189: va_end(ap);
190: }
191:
192:
193: /**
194: * Initialise nonunix_groupcheck state.
195: */
196: void
197: sudo_nonunix_groupcheck_init(void)
198: {
199: vas_err_t vaserr;
200: void *libvas;
201:
202: if (err_msg) {
203: free(err_msg);
204: err_msg = NULL;
205: }
206:
207: libvas = dlopen(LIBVAS_SO, RTLD_LAZY);
208: if (!libvas) {
209: set_err_msg("dlopen() failed: %s", dlerror());
210: return;
211: }
212:
213: libvas_handle = libvas;
214:
215: if (resolve_vas_funcs() != 0)
216: return;
217:
218: if (VAS_ERR_SUCCESS == (vaserr = v_ctx_alloc(&sudo_vas_ctx))) {
219:
220: if (VAS_ERR_SUCCESS == (vaserr = v_id_alloc(sudo_vas_ctx, "host/", &sudo_vas_id))) {
221:
222: if (update_flags & VAS_NAME_FLAG_NO_LDAP) {
223: sudo_vas_available = 1;
224: return; /* OK */
225: } else { /* Get a keytab */
226: if ((vaserr = v_id_establish_cred_keytab( sudo_vas_ctx,
227: sudo_vas_id,
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;
233: return; /* OK */
234: }
235:
236: if (!err_msg)
237: set_err_msg("unable to establish creds: %s",
238: v_err_get_string(sudo_vas_ctx, 1));
239: }
240:
241: v_id_free(sudo_vas_ctx, sudo_vas_id);
242: sudo_vas_id = NULL;
243: }
244:
245: /* This is the last opportunity to get an error message from libvas */
246: if (!err_msg)
247: set_err_msg("Error initializing non-unix group checking: %s",
248: v_err_get_string(sudo_vas_ctx, 1));
249:
250: v_ctx_free(sudo_vas_ctx);
251: sudo_vas_ctx = NULL;
252: }
253:
254: if (!err_msg)
255: set_err_msg("Failed to get a libvas handle for non-unix group checking (unknown cause)");
256:
257: sudo_vas_available = 0;
258: }
259:
260:
261: /**
262: * Clean up nonunix_groupcheck state.
263: */
264: void
265: sudo_nonunix_groupcheck_cleanup()
266: {
267: if (err_msg) {
268: free(err_msg);
269: err_msg = NULL;
270: }
271:
272: if (sudo_vas_available) {
273: v_id_free(sudo_vas_ctx, sudo_vas_id);
274: sudo_vas_id = NULL;
275:
276: v_ctx_free(sudo_vas_ctx);
277: sudo_vas_ctx = NULL;
278:
279: sudo_vas_available = FALSE;
280: }
281:
282: if (libvas_handle) {
283: if (dlclose(libvas_handle) != 0)
284: log_error(NO_MAIL|NO_EXIT, "dlclose() failed: %s", dlerror());
285: libvas_handle = NULL;
286: }
287: }
288:
289: #define RESOLVE_OR_ERR(fptr, sym) \
290: do { \
291: void *_fptr = dlsym(libvas_handle, (sym)); \
292: if (!_fptr) { \
293: set_err_msg("dlsym() failed: %s", dlerror()); \
294: return -1; \
295: } \
296: fptr = _fptr; \
297: } while (0)
298:
299:
300: /**
301: * Resolve all the libvas functions.
302: * Returns -1 and sets err_msg if something went wrong, or 0 on success.
303: */
304: int
305: resolve_vas_funcs(void)
306: {
307: if (!libvas_handle) /* assert */
308: return -1;
309:
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");
321:
322: return 0;
323: }