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