[BACK]Return to vasgroups.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / sudo

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: }