10Sstevel@tonic-gate /* 2*4621Ssemery * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 30Sstevel@tonic-gate * Use is subject to license terms. 40Sstevel@tonic-gate */ 50Sstevel@tonic-gate 60Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 70Sstevel@tonic-gate 80Sstevel@tonic-gate /* 90Sstevel@tonic-gate * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING 100Sstevel@tonic-gate * 110Sstevel@tonic-gate * Openvision retains the copyright to derivative works of 120Sstevel@tonic-gate * this source code. Do *NOT* create a derivative of this 130Sstevel@tonic-gate * source code before consulting with your legal department. 140Sstevel@tonic-gate * Do *NOT* integrate *ANY* of this source code into another 150Sstevel@tonic-gate * product before consulting with your legal department. 160Sstevel@tonic-gate * 170Sstevel@tonic-gate * For further information, read the top-level Openvision 180Sstevel@tonic-gate * copyright which is contained in the top-level MIT Kerberos 190Sstevel@tonic-gate * copyright. 200Sstevel@tonic-gate * 210Sstevel@tonic-gate * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING 220Sstevel@tonic-gate * 230Sstevel@tonic-gate */ 240Sstevel@tonic-gate 250Sstevel@tonic-gate 260Sstevel@tonic-gate /* 270Sstevel@tonic-gate * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved. 280Sstevel@tonic-gate * 292881Smp153739 * $Id: server_init.c,v 1.8 2002/10/15 15:40:49 epeisach Exp $ 300Sstevel@tonic-gate * $Source: /cvs/krbdev/krb5/src/lib/kadm5/srv/server_init.c,v $ 310Sstevel@tonic-gate */ 320Sstevel@tonic-gate 330Sstevel@tonic-gate #if !defined(lint) && !defined(__CODECENTER__) 342881Smp153739 static char *rcsid = "$Header: /cvs/krbdev/krb5/src/lib/kadm5/srv/server_init.c,v 1.8 2002/10/15 15:40:49 epeisach Exp $"; 350Sstevel@tonic-gate #endif 360Sstevel@tonic-gate 370Sstevel@tonic-gate #include <stdio.h> 380Sstevel@tonic-gate #include <stdlib.h> 390Sstevel@tonic-gate #include <com_err.h> 400Sstevel@tonic-gate #include <kadm5/admin.h> 410Sstevel@tonic-gate #include <krb5.h> 420Sstevel@tonic-gate #include "server_internal.h" 430Sstevel@tonic-gate #include <kdb/kdb_log.h> 440Sstevel@tonic-gate 450Sstevel@tonic-gate /* 460Sstevel@tonic-gate * Function check_handle 470Sstevel@tonic-gate * 480Sstevel@tonic-gate * Purpose: Check a server handle and return a com_err code if it is 490Sstevel@tonic-gate * invalid or 0 if it is valid. 500Sstevel@tonic-gate * 510Sstevel@tonic-gate * Arguments: 520Sstevel@tonic-gate * 530Sstevel@tonic-gate * handle The server handle. 540Sstevel@tonic-gate */ 550Sstevel@tonic-gate 560Sstevel@tonic-gate static int check_handle(void *handle) 570Sstevel@tonic-gate { 580Sstevel@tonic-gate CHECK_HANDLE(handle); 590Sstevel@tonic-gate return 0; 600Sstevel@tonic-gate } 610Sstevel@tonic-gate 620Sstevel@tonic-gate kadm5_ret_t kadm5_init_with_password(char *client_name, char *pass, 630Sstevel@tonic-gate char *service_name, 640Sstevel@tonic-gate kadm5_config_params *params, 650Sstevel@tonic-gate krb5_ui_4 struct_version, 660Sstevel@tonic-gate krb5_ui_4 api_version, 670Sstevel@tonic-gate void **server_handle) 680Sstevel@tonic-gate { 690Sstevel@tonic-gate return kadm5_init(client_name, pass, service_name, params, 700Sstevel@tonic-gate struct_version, api_version, 710Sstevel@tonic-gate server_handle); 720Sstevel@tonic-gate } 730Sstevel@tonic-gate 740Sstevel@tonic-gate kadm5_ret_t kadm5_init_with_creds(char *client_name, 750Sstevel@tonic-gate krb5_ccache ccache, 760Sstevel@tonic-gate char *service_name, 770Sstevel@tonic-gate kadm5_config_params *params, 780Sstevel@tonic-gate krb5_ui_4 struct_version, 790Sstevel@tonic-gate krb5_ui_4 api_version, 800Sstevel@tonic-gate void **server_handle) 810Sstevel@tonic-gate { 820Sstevel@tonic-gate /* 830Sstevel@tonic-gate * A program calling init_with_creds *never* expects to prompt the 840Sstevel@tonic-gate * user. Therefore, always pass a dummy password in case this is 850Sstevel@tonic-gate * KADM5_API_VERSION_1. If this is KADM5_API_VERSION_2 and 860Sstevel@tonic-gate * MKEY_FROM_KBD is non-zero, return an error. 870Sstevel@tonic-gate */ 880Sstevel@tonic-gate if (api_version == KADM5_API_VERSION_2 && params && 890Sstevel@tonic-gate (params->mask & KADM5_CONFIG_MKEY_FROM_KBD) && 900Sstevel@tonic-gate params->mkey_from_kbd) 910Sstevel@tonic-gate return KADM5_BAD_SERVER_PARAMS; 920Sstevel@tonic-gate return kadm5_init(client_name, NULL, service_name, params, 930Sstevel@tonic-gate struct_version, api_version, 940Sstevel@tonic-gate server_handle); 950Sstevel@tonic-gate } 960Sstevel@tonic-gate 970Sstevel@tonic-gate 980Sstevel@tonic-gate kadm5_ret_t kadm5_init_with_skey(char *client_name, char *keytab, 990Sstevel@tonic-gate char *service_name, 1000Sstevel@tonic-gate kadm5_config_params *params, 1010Sstevel@tonic-gate krb5_ui_4 struct_version, 1020Sstevel@tonic-gate krb5_ui_4 api_version, 1030Sstevel@tonic-gate void **server_handle) 1040Sstevel@tonic-gate { 1050Sstevel@tonic-gate /* 1060Sstevel@tonic-gate * A program calling init_with_skey *never* expects to prompt the 1070Sstevel@tonic-gate * user. Therefore, always pass a dummy password in case this is 1080Sstevel@tonic-gate * KADM5_API_VERSION_1. If this is KADM5_API_VERSION_2 and 1090Sstevel@tonic-gate * MKEY_FROM_KBD is non-zero, return an error. 1100Sstevel@tonic-gate */ 1110Sstevel@tonic-gate if (api_version == KADM5_API_VERSION_2 && params && 1120Sstevel@tonic-gate (params->mask & KADM5_CONFIG_MKEY_FROM_KBD) && 1130Sstevel@tonic-gate params->mkey_from_kbd) 1140Sstevel@tonic-gate return KADM5_BAD_SERVER_PARAMS; 1150Sstevel@tonic-gate return kadm5_init(client_name, NULL, service_name, params, 1160Sstevel@tonic-gate struct_version, api_version, 1170Sstevel@tonic-gate server_handle); 1180Sstevel@tonic-gate } 1190Sstevel@tonic-gate 1200Sstevel@tonic-gate kadm5_ret_t kadm5_init(char *client_name, char *pass, 1210Sstevel@tonic-gate char *service_name, 1220Sstevel@tonic-gate kadm5_config_params *params_in, 1230Sstevel@tonic-gate krb5_ui_4 struct_version, 1240Sstevel@tonic-gate krb5_ui_4 api_version, 1250Sstevel@tonic-gate void **server_handle) 1260Sstevel@tonic-gate { 1270Sstevel@tonic-gate int ret; 1280Sstevel@tonic-gate kadm5_server_handle_t handle; 1290Sstevel@tonic-gate kadm5_config_params params_local; /* for v1 compat */ 1300Sstevel@tonic-gate 1310Sstevel@tonic-gate if (! server_handle) 1320Sstevel@tonic-gate return EINVAL; 1330Sstevel@tonic-gate 1340Sstevel@tonic-gate if (! client_name) 1350Sstevel@tonic-gate return EINVAL; 1360Sstevel@tonic-gate 1370Sstevel@tonic-gate if (! (handle = (kadm5_server_handle_t) malloc(sizeof *handle))) 1380Sstevel@tonic-gate return ENOMEM; 1390Sstevel@tonic-gate memset(handle, 0, sizeof(*handle)); 1400Sstevel@tonic-gate 1412881Smp153739 ret = (int) krb5_init_context(&(handle->context)); 1422881Smp153739 if (ret) { 1430Sstevel@tonic-gate free(handle); 1440Sstevel@tonic-gate return(ret); 1450Sstevel@tonic-gate } 1460Sstevel@tonic-gate 1470Sstevel@tonic-gate handle->magic_number = KADM5_SERVER_HANDLE_MAGIC; 1480Sstevel@tonic-gate handle->struct_version = struct_version; 1490Sstevel@tonic-gate handle->api_version = api_version; 1500Sstevel@tonic-gate 1510Sstevel@tonic-gate /* 1520Sstevel@tonic-gate * Verify the version numbers before proceeding; we can't use 1530Sstevel@tonic-gate * CHECK_HANDLE because not all fields are set yet. 1540Sstevel@tonic-gate */ 1550Sstevel@tonic-gate GENERIC_CHECK_HANDLE(handle, KADM5_OLD_SERVER_API_VERSION, 1560Sstevel@tonic-gate KADM5_NEW_SERVER_API_VERSION); 1570Sstevel@tonic-gate 1580Sstevel@tonic-gate /* 1590Sstevel@tonic-gate * Acquire relevant profile entries. In version 2, merge values 1600Sstevel@tonic-gate * in params_in with values from profile, based on 1610Sstevel@tonic-gate * params_in->mask. 1620Sstevel@tonic-gate * 1630Sstevel@tonic-gate * In version 1, we've given a realm (which may be NULL) instead 1640Sstevel@tonic-gate * of params_in. So use that realm, make params_in contain an 1650Sstevel@tonic-gate * empty mask, and behave like version 2. 1660Sstevel@tonic-gate */ 1670Sstevel@tonic-gate memset((char *) ¶ms_local, 0, sizeof(params_local)); 1680Sstevel@tonic-gate if (api_version == KADM5_API_VERSION_1) { 1690Sstevel@tonic-gate params_local.realm = (char *) params_in; 1700Sstevel@tonic-gate if (params_in) 1710Sstevel@tonic-gate params_local.mask = KADM5_CONFIG_REALM; 1720Sstevel@tonic-gate params_in = ¶ms_local; 1730Sstevel@tonic-gate } 1740Sstevel@tonic-gate 1750Sstevel@tonic-gate #define ILLEGAL_PARAMS (KADM5_CONFIG_ADMIN_SERVER) 1760Sstevel@tonic-gate if (params_in && (params_in->mask & ILLEGAL_PARAMS)) { 1770Sstevel@tonic-gate krb5_free_context(handle->context); 1780Sstevel@tonic-gate free(handle); 1790Sstevel@tonic-gate return KADM5_BAD_SERVER_PARAMS; 1800Sstevel@tonic-gate } 1810Sstevel@tonic-gate 1822881Smp153739 ret = kadm5_get_config_params(handle->context, (char *) NULL, 1832881Smp153739 (char *) NULL, params_in, 1842881Smp153739 &handle->params); 1852881Smp153739 if (ret) { 1860Sstevel@tonic-gate krb5_free_context(handle->context); 1870Sstevel@tonic-gate free(handle); 1880Sstevel@tonic-gate return(ret); 1890Sstevel@tonic-gate } 1900Sstevel@tonic-gate 1910Sstevel@tonic-gate #define REQUIRED_PARAMS (KADM5_CONFIG_REALM | KADM5_CONFIG_DBNAME | \ 1920Sstevel@tonic-gate KADM5_CONFIG_ADBNAME | \ 1930Sstevel@tonic-gate KADM5_CONFIG_ADB_LOCKFILE | \ 1940Sstevel@tonic-gate KADM5_CONFIG_ENCTYPE | \ 1950Sstevel@tonic-gate KADM5_CONFIG_FLAGS | \ 1960Sstevel@tonic-gate KADM5_CONFIG_MAX_LIFE | KADM5_CONFIG_MAX_RLIFE | \ 1970Sstevel@tonic-gate KADM5_CONFIG_EXPIRATION | KADM5_CONFIG_ENCTYPES) 1982881Smp153739 1990Sstevel@tonic-gate if ((handle->params.mask & REQUIRED_PARAMS) != REQUIRED_PARAMS) { 2000Sstevel@tonic-gate krb5_free_context(handle->context); 2010Sstevel@tonic-gate free(handle); 2020Sstevel@tonic-gate return KADM5_MISSING_CONF_PARAMS; 2030Sstevel@tonic-gate } 2040Sstevel@tonic-gate 2052881Smp153739 /* 2062881Smp153739 * Set the db_name based on configuration before calling 2072881Smp153739 * krb5_db_init, so it will get used. 2082881Smp153739 */ 2092881Smp153739 2102881Smp153739 ret = krb5_db_set_name(handle->context, handle->params.dbname); 2112881Smp153739 if (ret) { 2120Sstevel@tonic-gate free(handle); 2130Sstevel@tonic-gate return(ret); 2140Sstevel@tonic-gate } 2150Sstevel@tonic-gate 2162881Smp153739 ret = krb5_db_init(handle->context); 2172881Smp153739 if (ret) { 2180Sstevel@tonic-gate krb5_free_context(handle->context); 2190Sstevel@tonic-gate free(handle); 2200Sstevel@tonic-gate return(ret); 2210Sstevel@tonic-gate } 2220Sstevel@tonic-gate 2230Sstevel@tonic-gate if ((ret = krb5_parse_name(handle->context, client_name, 2240Sstevel@tonic-gate &handle->current_caller))) { 2250Sstevel@tonic-gate krb5_db_fini(handle->context); 2260Sstevel@tonic-gate krb5_free_context(handle->context); 2270Sstevel@tonic-gate free(handle); 2280Sstevel@tonic-gate return ret; 2290Sstevel@tonic-gate } 2300Sstevel@tonic-gate 2312881Smp153739 if (! (handle->lhandle = malloc(sizeof(*handle)))) { 2322881Smp153739 krb5_db_fini(handle->context); 2332881Smp153739 krb5_free_context(handle->context); 2340Sstevel@tonic-gate free(handle); 2352881Smp153739 return ENOMEM; 2360Sstevel@tonic-gate } 2372881Smp153739 *handle->lhandle = *handle; 2382881Smp153739 handle->lhandle->api_version = KADM5_API_VERSION_2; 2392881Smp153739 handle->lhandle->struct_version = KADM5_STRUCT_VERSION; 2402881Smp153739 handle->lhandle->lhandle = handle->lhandle; 2410Sstevel@tonic-gate 2422881Smp153739 /* can't check the handle until current_caller is set */ 2432881Smp153739 ret = check_handle((void *) handle); 2442881Smp153739 if (ret) { 2452881Smp153739 free(handle); 2462881Smp153739 return ret; 2472881Smp153739 } 2482881Smp153739 2492881Smp153739 /* 2502881Smp153739 * The KADM5_API_VERSION_1 spec said "If pass (or keytab) is NULL 2512881Smp153739 * or an empty string, reads the master password from [the stash 2522881Smp153739 * file]. Otherwise, the non-NULL password is ignored and the 2532881Smp153739 * user is prompted for it via the tty." However, the code was 2542881Smp153739 * implemented the other way: when a non-NULL password was 2552881Smp153739 * provided, the stash file was used. This is somewhat more 2562881Smp153739 * sensible, as then a local or remote client that provides a 2572881Smp153739 * password does not prompt the user. This code maintains the 2582881Smp153739 * previous actual behavior, and not the old spec behavior, 2592881Smp153739 * because that is how the unit tests are written. 2602881Smp153739 * 2612881Smp153739 * In KADM5_API_VERSION_2, this decision is controlled by 2622881Smp153739 * params. 2632881Smp153739 * 2642881Smp153739 * kdb_init_master's third argument is "from_keyboard". 2652881Smp153739 */ 266*4621Ssemery /* 267*4621Ssemery * Solaris Kerberos: Setting to an unknown enc type will make the function 268*4621Ssemery * read the encryption type in the stash file instead of assumming that it 269*4621Ssemery * is the default type. 270*4621Ssemery */ 271*4621Ssemery if (handle->params.enctype == DEFAULT_KDC_ENCTYPE) 272*4621Ssemery handle->params.enctype = ENCTYPE_UNKNOWN; 2732881Smp153739 ret = kdb_init_master(handle, handle->params.realm, 2742881Smp153739 (handle->api_version == KADM5_API_VERSION_1 ? 2752881Smp153739 ((pass == NULL) || !(strlen(pass))) : 2762881Smp153739 ((handle->params.mask & KADM5_CONFIG_MKEY_FROM_KBD) 2772881Smp153739 && handle->params.mkey_from_kbd) 2782881Smp153739 )); 2792881Smp153739 if (ret) { 2802881Smp153739 krb5_db_fini(handle->context); 2812881Smp153739 krb5_free_context(handle->context); 2822881Smp153739 free(handle); 2832881Smp153739 return ret; 2842881Smp153739 } 285*4621Ssemery /* 286*4621Ssemery * Solaris Kerberos: We used the enc type that was discovered in the stash 287*4621Ssemery * file to associate with the other magic principals in the database. 288*4621Ssemery */ 289*4621Ssemery handle->params.enctype = handle->master_keyblock.enctype; 2902881Smp153739 2912881Smp153739 ret = kdb_init_hist(handle, handle->params.realm); 2922881Smp153739 if (ret) { 2930Sstevel@tonic-gate krb5_db_fini(handle->context); 2940Sstevel@tonic-gate krb5_free_context(handle->context); 2950Sstevel@tonic-gate free(handle); 2960Sstevel@tonic-gate return ret; 2970Sstevel@tonic-gate } 2980Sstevel@tonic-gate 2992881Smp153739 ret = init_dict(&handle->params); 3002881Smp153739 if (ret) { 3012881Smp153739 krb5_db_fini(handle->context); 3020Sstevel@tonic-gate krb5_free_principal(handle->context, handle->current_caller); 3030Sstevel@tonic-gate krb5_free_context(handle->context); 3040Sstevel@tonic-gate free(handle); 3050Sstevel@tonic-gate return ret; 3060Sstevel@tonic-gate } 3070Sstevel@tonic-gate 3082881Smp153739 ret = adb_policy_init(handle); 3092881Smp153739 if (ret) { 3100Sstevel@tonic-gate krb5_db_fini(handle->context); 3110Sstevel@tonic-gate krb5_free_principal(handle->context, handle->current_caller); 3120Sstevel@tonic-gate krb5_free_context(handle->context); 3130Sstevel@tonic-gate free(handle); 3140Sstevel@tonic-gate return ret; 3150Sstevel@tonic-gate } 3160Sstevel@tonic-gate handle->lhandle->policy_db = handle->policy_db; 3170Sstevel@tonic-gate 3180Sstevel@tonic-gate *server_handle = (void *) handle; 3190Sstevel@tonic-gate 3200Sstevel@tonic-gate return KADM5_OK; 3210Sstevel@tonic-gate } 3220Sstevel@tonic-gate 3230Sstevel@tonic-gate kadm5_ret_t kadm5_destroy(void *server_handle) 3240Sstevel@tonic-gate { 3250Sstevel@tonic-gate kadm5_server_handle_t handle = server_handle; 3260Sstevel@tonic-gate 3270Sstevel@tonic-gate CHECK_HANDLE(server_handle); 3280Sstevel@tonic-gate 3290Sstevel@tonic-gate destroy_dict(); 3300Sstevel@tonic-gate 3310Sstevel@tonic-gate adb_policy_close(handle); 3320Sstevel@tonic-gate krb5_db_fini(handle->context); 3330Sstevel@tonic-gate krb5_free_principal(handle->context, handle->current_caller); 3340Sstevel@tonic-gate kadm5_free_config_params(handle->context, &handle->params); 3350Sstevel@tonic-gate krb5_free_context(handle->context); 3360Sstevel@tonic-gate handle->magic_number = 0; 3370Sstevel@tonic-gate free(handle->lhandle); 3380Sstevel@tonic-gate free(handle); 3390Sstevel@tonic-gate 3400Sstevel@tonic-gate return KADM5_OK; 3410Sstevel@tonic-gate } 3420Sstevel@tonic-gate 3432881Smp153739 kadm5_ret_t kadm5_lock(void *server_handle) 3442881Smp153739 { 3452881Smp153739 kadm5_server_handle_t handle = server_handle; 3462881Smp153739 kadm5_ret_t ret; 3472881Smp153739 3482881Smp153739 CHECK_HANDLE(server_handle); 3492881Smp153739 ret = osa_adb_open_and_lock(handle->policy_db, OSA_ADB_EXCLUSIVE); 3502881Smp153739 if (ret) 3512881Smp153739 return ret; 3522881Smp153739 ret = krb5_db_lock(handle->context, KRB5_LOCKMODE_EXCLUSIVE); 3532881Smp153739 if (ret) 3542881Smp153739 return ret; 3552881Smp153739 3562881Smp153739 return KADM5_OK; 3572881Smp153739 } 3582881Smp153739 3592881Smp153739 kadm5_ret_t kadm5_unlock(void *server_handle) 3602881Smp153739 { 3612881Smp153739 kadm5_server_handle_t handle = server_handle; 3622881Smp153739 kadm5_ret_t ret; 3632881Smp153739 3642881Smp153739 CHECK_HANDLE(server_handle); 3652881Smp153739 ret = osa_adb_close_and_unlock(handle->policy_db); 3662881Smp153739 if (ret) 3672881Smp153739 return ret; 3682881Smp153739 ret = krb5_db_unlock(handle->context); 3692881Smp153739 if (ret) 3702881Smp153739 return ret; 3712881Smp153739 3722881Smp153739 return KADM5_OK; 3732881Smp153739 } 3742881Smp153739 3750Sstevel@tonic-gate kadm5_ret_t kadm5_flush(void *server_handle) 3760Sstevel@tonic-gate { 3770Sstevel@tonic-gate kadm5_server_handle_t handle = server_handle; 3780Sstevel@tonic-gate kadm5_ret_t ret; 3790Sstevel@tonic-gate 3800Sstevel@tonic-gate CHECK_HANDLE(server_handle); 3810Sstevel@tonic-gate 3820Sstevel@tonic-gate if ((ret = krb5_db_fini(handle->context)) || 3830Sstevel@tonic-gate /* 3840Sstevel@tonic-gate * Set the db_name based on configuration before calling 3850Sstevel@tonic-gate * krb5_db_init, so it will get used. 3860Sstevel@tonic-gate */ 3870Sstevel@tonic-gate (ret = krb5_db_set_name(handle->context, 3880Sstevel@tonic-gate handle->params.dbname)) || 3890Sstevel@tonic-gate (ret = krb5_db_init(handle->context)) || 3900Sstevel@tonic-gate (ret = adb_policy_close(handle)) || 3910Sstevel@tonic-gate (ret = adb_policy_init(handle))) { 3920Sstevel@tonic-gate (void) kadm5_destroy(server_handle); 3930Sstevel@tonic-gate return ret; 3940Sstevel@tonic-gate } 3950Sstevel@tonic-gate return KADM5_OK; 3960Sstevel@tonic-gate } 3970Sstevel@tonic-gate 3980Sstevel@tonic-gate int _kadm5_check_handle(void *handle) 3990Sstevel@tonic-gate { 4000Sstevel@tonic-gate CHECK_HANDLE(handle); 4010Sstevel@tonic-gate return 0; 4020Sstevel@tonic-gate } 4030Sstevel@tonic-gate 4040Sstevel@tonic-gate krb5_error_code 4050Sstevel@tonic-gate kadm5_init_iprop(void *handle) 4060Sstevel@tonic-gate { 4070Sstevel@tonic-gate kadm5_server_handle_t iprop_h; 4080Sstevel@tonic-gate krb5_error_code retval; 4090Sstevel@tonic-gate 4100Sstevel@tonic-gate iprop_h = handle; 4110Sstevel@tonic-gate if (iprop_h->params.iprop_enabled) { 4120Sstevel@tonic-gate ulog_set_role(iprop_h->context, IPROP_MASTER); 4130Sstevel@tonic-gate if ((retval = ulog_map(iprop_h->context, &iprop_h->params, 4140Sstevel@tonic-gate FKCOMMAND)) != 0) 4150Sstevel@tonic-gate return (retval); 4160Sstevel@tonic-gate } 4170Sstevel@tonic-gate return (0); 4180Sstevel@tonic-gate } 419