1da6c28aaSamw /* 2da6c28aaSamw * CDDL HEADER START 3da6c28aaSamw * 4da6c28aaSamw * The contents of this file are subject to the terms of the 5da6c28aaSamw * Common Development and Distribution License (the "License"). 6da6c28aaSamw * You may not use this file except in compliance with the License. 7da6c28aaSamw * 8da6c28aaSamw * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9da6c28aaSamw * or http://www.opensolaris.org/os/licensing. 10da6c28aaSamw * See the License for the specific language governing permissions 11da6c28aaSamw * and limitations under the License. 12da6c28aaSamw * 13da6c28aaSamw * When distributing Covered Code, include this CDDL HEADER in each 14da6c28aaSamw * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15da6c28aaSamw * If applicable, add the following below this CDDL HEADER, with the 16da6c28aaSamw * fields enclosed by brackets "[]" replaced with your own identifying 17da6c28aaSamw * information: Portions Copyright [yyyy] [name of copyright owner] 18da6c28aaSamw * 19da6c28aaSamw * CDDL HEADER END 20da6c28aaSamw */ 21148c5f43SAlan Wright 22da6c28aaSamw /* 23148c5f43SAlan Wright * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. 24*b3700b07SGordon Ross * Copyright 2015 Nexenta Systems, Inc. All rights reserved. 25da6c28aaSamw */ 26da6c28aaSamw 27da6c28aaSamw /* 28da6c28aaSamw * This module provides the high level interface to the SAM RPC 29da6c28aaSamw * functions. 30da6c28aaSamw */ 31da6c28aaSamw 321ed6b69aSGordon Ross #include <sys/types.h> 331ed6b69aSGordon Ross #include <sys/isa_defs.h> 341ed6b69aSGordon Ross #include <sys/byteorder.h> 351ed6b69aSGordon Ross 36da6c28aaSamw #include <alloca.h> 37da6c28aaSamw 38da6c28aaSamw #include <smbsrv/libsmb.h> 39da6c28aaSamw #include <smbsrv/libmlsvc.h> 40da6c28aaSamw #include <smbsrv/ntaccess.h> 418d7e4166Sjose borrego #include <lsalib.h> 428d7e4166Sjose borrego #include <samlib.h> 43da6c28aaSamw 441ed6b69aSGordon Ross #ifdef _LITTLE_ENDIAN 451ed6b69aSGordon Ross /* little-endian values on little-endian */ 461ed6b69aSGordon Ross #define htolel(x) ((uint32_t)(x)) 471ed6b69aSGordon Ross #define letohl(x) ((uint32_t)(x)) 481ed6b69aSGordon Ross #else /* (BYTE_ORDER == LITTLE_ENDIAN) */ 491ed6b69aSGordon Ross /* little-endian values on big-endian (swap) */ 501ed6b69aSGordon Ross #define letohl(x) BSWAP_32(x) 511ed6b69aSGordon Ross #define htolel(x) BSWAP_32(x) 521ed6b69aSGordon Ross #endif /* (BYTE_ORDER == LITTLE_ENDIAN) */ 531ed6b69aSGordon Ross 54da6c28aaSamw /* 55da6c28aaSamw * Valid values for the OEM OWF password encryption. 56da6c28aaSamw */ 57da6c28aaSamw #define SAM_KEYLEN 16 58da6c28aaSamw 591ed6b69aSGordon Ross static void samr_fill_userpw(struct samr_user_password *, const char *); 601ed6b69aSGordon Ross static void samr_make_encrypted_password( 611ed6b69aSGordon Ross struct samr_encr_passwd *epw, 621ed6b69aSGordon Ross char *new_pw_clear, 631ed6b69aSGordon Ross uint8_t *crypt_key); 64da6c28aaSamw 65da6c28aaSamw 66da6c28aaSamw /* 671ed6b69aSGordon Ross * Todo: Implement "unjoin" domain, which would use the 681ed6b69aSGordon Ross * sam_remove_trust_account code below. 69da6c28aaSamw */ 70da6c28aaSamw 71da6c28aaSamw /* 72da6c28aaSamw * sam_remove_trust_account 73da6c28aaSamw * 74da6c28aaSamw * Attempt to remove the workstation trust account for this system. 75da6c28aaSamw * Administrator access is required to perform this operation. 76da6c28aaSamw * 77da6c28aaSamw * Returns NT status codes. 78da6c28aaSamw */ 79da6c28aaSamw DWORD 80da6c28aaSamw sam_remove_trust_account(char *server, char *domain) 81da6c28aaSamw { 82b89a8333Snatalie li - Sun Microsystems - Irvine United States char account_name[SMB_SAMACCT_MAXLEN]; 83da6c28aaSamw 84b89a8333Snatalie li - Sun Microsystems - Irvine United States if (smb_getsamaccount(account_name, SMB_SAMACCT_MAXLEN) != 0) 85b89a8333Snatalie li - Sun Microsystems - Irvine United States return (NT_STATUS_INTERNAL_ERROR); 86da6c28aaSamw 87da6c28aaSamw return (sam_delete_account(server, domain, account_name)); 88da6c28aaSamw } 89da6c28aaSamw 90da6c28aaSamw 91da6c28aaSamw /* 92da6c28aaSamw * sam_delete_account 93da6c28aaSamw * 94da6c28aaSamw * Attempt to remove an account from the SAM database on the specified 95da6c28aaSamw * server. 96da6c28aaSamw * 97da6c28aaSamw * Returns NT status codes. 98da6c28aaSamw */ 99da6c28aaSamw DWORD 100da6c28aaSamw sam_delete_account(char *server, char *domain_name, char *account_name) 101da6c28aaSamw { 102da6c28aaSamw mlsvc_handle_t samr_handle; 103da6c28aaSamw mlsvc_handle_t domain_handle; 104da6c28aaSamw mlsvc_handle_t user_handle; 1057f667e74Sjose borrego smb_account_t ainfo; 1061ed6b69aSGordon Ross smb_sid_t *sid; 107da6c28aaSamw DWORD access_mask; 108da6c28aaSamw DWORD status; 109da6c28aaSamw int rc; 110a0aa776eSAlan Wright char user[SMB_USERNAME_MAXLEN]; 111a0aa776eSAlan Wright 112a0aa776eSAlan Wright smb_ipc_get_user(user, SMB_USERNAME_MAXLEN); 113da6c28aaSamw 11455bf511dSas200622 rc = samr_open(server, domain_name, user, SAM_LOOKUP_INFORMATION, 11555bf511dSas200622 &samr_handle); 1167f667e74Sjose borrego if (rc != 0) 1171ed6b69aSGordon Ross return (NT_STATUS_CANT_ACCESS_DOMAIN_INFO); 1187f667e74Sjose borrego 1191ed6b69aSGordon Ross sid = samr_lookup_domain(&samr_handle, domain_name); 1201ed6b69aSGordon Ross if (sid == NULL) { 1211ed6b69aSGordon Ross status = NT_STATUS_CANT_ACCESS_DOMAIN_INFO; 1221ed6b69aSGordon Ross goto out_samr_hdl; 123da6c28aaSamw } 124da6c28aaSamw 1251ed6b69aSGordon Ross status = samr_open_domain(&samr_handle, SAM_LOOKUP_INFORMATION, 1261ed6b69aSGordon Ross (struct samr_sid *)sid, &domain_handle); 1271ed6b69aSGordon Ross if (status != NT_STATUS_SUCCESS) 1281ed6b69aSGordon Ross goto out_sid_ptr; 1291ed6b69aSGordon Ross 1307f667e74Sjose borrego status = samr_lookup_domain_names(&domain_handle, account_name, &ainfo); 1311ed6b69aSGordon Ross if (status != NT_STATUS_SUCCESS) 1321ed6b69aSGordon Ross goto out_dom_hdl; 1331ed6b69aSGordon Ross 134da6c28aaSamw access_mask = STANDARD_RIGHTS_EXECUTE | DELETE; 13555bf511dSas200622 status = samr_open_user(&domain_handle, access_mask, 1367f667e74Sjose borrego ainfo.a_rid, &user_handle); 1371ed6b69aSGordon Ross if (status != NT_STATUS_SUCCESS) 1381ed6b69aSGordon Ross goto out_dom_hdl; 1391ed6b69aSGordon Ross 1401ed6b69aSGordon Ross status = samr_delete_user(&user_handle); 1411ed6b69aSGordon Ross 142da6c28aaSamw (void) samr_close_handle(&user_handle); 1431ed6b69aSGordon Ross out_dom_hdl: 144da6c28aaSamw (void) samr_close_handle(&domain_handle); 1451ed6b69aSGordon Ross out_sid_ptr: 1467f667e74Sjose borrego free(sid); 1471ed6b69aSGordon Ross out_samr_hdl: 1487f667e74Sjose borrego (void) samr_close_handle(&samr_handle); 1491ed6b69aSGordon Ross 1507f667e74Sjose borrego return (status); 1517f667e74Sjose borrego } 15255bf511dSas200622 15355bf511dSas200622 15455bf511dSas200622 /* 155da6c28aaSamw * sam_lookup_name 156da6c28aaSamw * 157da6c28aaSamw * Lookup an account name in the SAM database on the specified domain 158da6c28aaSamw * controller. Provides the account RID on success. 159da6c28aaSamw * 160da6c28aaSamw * Returns NT status codes. 161da6c28aaSamw */ 162da6c28aaSamw DWORD 163da6c28aaSamw sam_lookup_name(char *server, char *domain_name, char *account_name, 164da6c28aaSamw DWORD *rid_ret) 165da6c28aaSamw { 166da6c28aaSamw mlsvc_handle_t samr_handle; 167da6c28aaSamw mlsvc_handle_t domain_handle; 1687f667e74Sjose borrego smb_account_t ainfo; 169da6c28aaSamw struct samr_sid *domain_sid; 170da6c28aaSamw int rc; 171da6c28aaSamw DWORD status; 172a0aa776eSAlan Wright char user[SMB_USERNAME_MAXLEN]; 173a0aa776eSAlan Wright 174a0aa776eSAlan Wright smb_ipc_get_user(user, SMB_USERNAME_MAXLEN); 175da6c28aaSamw 176da6c28aaSamw *rid_ret = 0; 177da6c28aaSamw 17855bf511dSas200622 rc = samr_open(server, domain_name, user, SAM_LOOKUP_INFORMATION, 17955bf511dSas200622 &samr_handle); 180da6c28aaSamw 1817f667e74Sjose borrego if (rc != 0) 182da6c28aaSamw return (NT_STATUS_OPEN_FAILED); 183da6c28aaSamw 1847f667e74Sjose borrego domain_sid = (struct samr_sid *)samr_lookup_domain(&samr_handle, 1857f667e74Sjose borrego domain_name); 1867f667e74Sjose borrego if (domain_sid == NULL) { 187da6c28aaSamw (void) samr_close_handle(&samr_handle); 188da6c28aaSamw return (NT_STATUS_NO_SUCH_DOMAIN); 189da6c28aaSamw } 190da6c28aaSamw 191da6c28aaSamw status = samr_open_domain(&samr_handle, SAM_LOOKUP_INFORMATION, 192da6c28aaSamw domain_sid, &domain_handle); 1937f667e74Sjose borrego if (status == NT_STATUS_SUCCESS) { 194da6c28aaSamw status = samr_lookup_domain_names(&domain_handle, 1957f667e74Sjose borrego account_name, &ainfo); 1967f667e74Sjose borrego if (status == NT_STATUS_SUCCESS) 1977f667e74Sjose borrego *rid_ret = ainfo.a_rid; 198da6c28aaSamw 199da6c28aaSamw (void) samr_close_handle(&domain_handle); 200da6c28aaSamw } 201da6c28aaSamw 202da6c28aaSamw (void) samr_close_handle(&samr_handle); 203da6c28aaSamw return (status); 204da6c28aaSamw } 205da6c28aaSamw 206da6c28aaSamw /* 207da6c28aaSamw * sam_get_local_domains 208da6c28aaSamw * 209da6c28aaSamw * Query a remote server to get the list of local domains that it 210da6c28aaSamw * supports. 211da6c28aaSamw * 212da6c28aaSamw * Returns NT status codes. 213da6c28aaSamw */ 214da6c28aaSamw DWORD 215da6c28aaSamw sam_get_local_domains(char *server, char *domain_name) 216da6c28aaSamw { 217da6c28aaSamw mlsvc_handle_t samr_handle; 218da6c28aaSamw DWORD status; 219da6c28aaSamw int rc; 220a0aa776eSAlan Wright char user[SMB_USERNAME_MAXLEN]; 221a0aa776eSAlan Wright 222a0aa776eSAlan Wright smb_ipc_get_user(user, SMB_USERNAME_MAXLEN); 223da6c28aaSamw 22455bf511dSas200622 rc = samr_open(server, domain_name, user, SAM_ENUM_LOCAL_DOMAIN, 22555bf511dSas200622 &samr_handle); 226da6c28aaSamw if (rc != 0) 227da6c28aaSamw return (NT_STATUS_OPEN_FAILED); 228da6c28aaSamw 229da6c28aaSamw status = samr_enum_local_domains(&samr_handle); 230da6c28aaSamw (void) samr_close_handle(&samr_handle); 231da6c28aaSamw return (status); 232da6c28aaSamw } 233da6c28aaSamw 234da6c28aaSamw /* 2351ed6b69aSGordon Ross * Set the account control flags on some account for which we 2361ed6b69aSGordon Ross * have already opened a SAM handle with appropriate rights, 2371ed6b69aSGordon Ross * passed in here as sam_handle, along with the new flags. 238da6c28aaSamw */ 2391ed6b69aSGordon Ross DWORD 2401ed6b69aSGordon Ross netr_set_user_control( 2411ed6b69aSGordon Ross mlsvc_handle_t *user_handle, 2421ed6b69aSGordon Ross DWORD UserAccountControl) 243da6c28aaSamw { 2441ed6b69aSGordon Ross struct samr_SetUserInfo16 info; 245da6c28aaSamw 2461ed6b69aSGordon Ross info.UserAccountControl = UserAccountControl; 2471ed6b69aSGordon Ross return (samr_set_user_info(user_handle, 16, &info)); 248da6c28aaSamw } 2498d7e4166Sjose borrego 2501ed6b69aSGordon Ross /* 2511ed6b69aSGordon Ross * Set the password on some account, for which we have already 2521ed6b69aSGordon Ross * opened a SAM handle with appropriate rights, passed in here 2531ed6b69aSGordon Ross * as sam_handle, along with the new password as cleartext. 2541ed6b69aSGordon Ross * 2551ed6b69aSGordon Ross * This builds a struct SAMPR_USER_INTERNAL5_INFORMATION [MS-SAMR] 2561ed6b69aSGordon Ross * containing the new password, encrypted with our session key. 2571ed6b69aSGordon Ross */ 2581ed6b69aSGordon Ross DWORD 2591ed6b69aSGordon Ross netr_set_user_password( 2601ed6b69aSGordon Ross mlsvc_handle_t *user_handle, 2611ed6b69aSGordon Ross char *new_pw_clear) 2628d7e4166Sjose borrego { 2631ed6b69aSGordon Ross unsigned char ssn_key[SMBAUTH_HASH_SZ]; 2641ed6b69aSGordon Ross struct samr_SetUserInfo24 info; 2658d7e4166Sjose borrego 2661ed6b69aSGordon Ross if (ndr_rpc_get_ssnkey(user_handle, ssn_key, SMBAUTH_HASH_SZ)) 2671ed6b69aSGordon Ross return (NT_STATUS_INTERNAL_ERROR); 2681ed6b69aSGordon Ross 2691ed6b69aSGordon Ross (void) memset(&info, 0, sizeof (info)); 2701ed6b69aSGordon Ross samr_make_encrypted_password(&info.encr_pw, new_pw_clear, ssn_key); 2711ed6b69aSGordon Ross 2721ed6b69aSGordon Ross /* Rather not leave the session key around. */ 2731ed6b69aSGordon Ross (void) memset(ssn_key, 0, sizeof (ssn_key)); 2741ed6b69aSGordon Ross 2751ed6b69aSGordon Ross return (samr_set_user_info(user_handle, 24, &info)); 2768d7e4166Sjose borrego } 27729bd2886SAlan Wright 2781ed6b69aSGordon Ross /* 2791ed6b69aSGordon Ross * Change a password like NetUserChangePassword(), 2801ed6b69aSGordon Ross * but where we already know which AD server to use, 2811ed6b69aSGordon Ross * so we don't request the domain name or search for 2821ed6b69aSGordon Ross * an AD server for that domain here. 2831ed6b69aSGordon Ross */ 2841ed6b69aSGordon Ross DWORD 2851ed6b69aSGordon Ross netr_change_password( 2861ed6b69aSGordon Ross char *server, 2871ed6b69aSGordon Ross char *account, 2881ed6b69aSGordon Ross char *old_pw_clear, 2891ed6b69aSGordon Ross char *new_pw_clear) 2901ed6b69aSGordon Ross { 2911ed6b69aSGordon Ross struct samr_encr_passwd epw; 2921ed6b69aSGordon Ross struct samr_encr_hash old_hash; 2931ed6b69aSGordon Ross uint8_t old_nt_hash[SAMR_PWHASH_LEN]; 2941ed6b69aSGordon Ross uint8_t new_nt_hash[SAMR_PWHASH_LEN]; 2951ed6b69aSGordon Ross mlsvc_handle_t handle; 296*b3700b07SGordon Ross DWORD status; 2971ed6b69aSGordon Ross 2981ed6b69aSGordon Ross /* 2991ed6b69aSGordon Ross * Create an RPC handle to this server, bound to SAMR. 3001ed6b69aSGordon Ross */ 301*b3700b07SGordon Ross status = ndr_rpc_bind(&handle, server, "", "", "SAMR"); 302*b3700b07SGordon Ross if (status != NT_STATUS_SUCCESS) 303*b3700b07SGordon Ross return (status); 3041ed6b69aSGordon Ross 3051ed6b69aSGordon Ross /* 3061ed6b69aSGordon Ross * Encrypt the new p/w (plus random filler) with the 3071ed6b69aSGordon Ross * old password, and send the old p/w encrypted with 3081ed6b69aSGordon Ross * the new p/w hash to prove we know the old p/w. 3091ed6b69aSGordon Ross * Details: [MS-SAMR 3.1.5.10.3] 3101ed6b69aSGordon Ross */ 3111ed6b69aSGordon Ross (void) smb_auth_ntlm_hash(old_pw_clear, old_nt_hash); 3121ed6b69aSGordon Ross (void) smb_auth_ntlm_hash(new_pw_clear, new_nt_hash); 3131ed6b69aSGordon Ross samr_make_encrypted_password(&epw, new_pw_clear, old_nt_hash); 3141ed6b69aSGordon Ross 3151ed6b69aSGordon Ross (void) smb_auth_DES(old_hash.data, SAMR_PWHASH_LEN, 3161ed6b69aSGordon Ross new_nt_hash, 14, /* key */ 3171ed6b69aSGordon Ross old_nt_hash, SAMR_PWHASH_LEN); 3181ed6b69aSGordon Ross 3191ed6b69aSGordon Ross /* 3201ed6b69aSGordon Ross * Finally, ready to try the OtW call. 3211ed6b69aSGordon Ross */ 322*b3700b07SGordon Ross status = samr_change_password( 3231ed6b69aSGordon Ross &handle, server, account, 3241ed6b69aSGordon Ross &epw, &old_hash); 3251ed6b69aSGordon Ross 3261ed6b69aSGordon Ross /* Avoid leaving cleartext (or equivalent) around. */ 3271ed6b69aSGordon Ross (void) memset(old_nt_hash, 0, sizeof (old_nt_hash)); 3281ed6b69aSGordon Ross (void) memset(new_nt_hash, 0, sizeof (new_nt_hash)); 3291ed6b69aSGordon Ross 3301ed6b69aSGordon Ross ndr_rpc_unbind(&handle); 331*b3700b07SGordon Ross return (status); 3328d7e4166Sjose borrego } 3338d7e4166Sjose borrego 3341ed6b69aSGordon Ross /* 3351ed6b69aSGordon Ross * Build an encrypted password, as used by samr_set_user_info 3361ed6b69aSGordon Ross * and samr_change_password. Note: This builds the unencrypted 3371ed6b69aSGordon Ross * form in one union arm, and encrypts it in the other union arm. 3381ed6b69aSGordon Ross */ 3391ed6b69aSGordon Ross void 3401ed6b69aSGordon Ross samr_make_encrypted_password( 3411ed6b69aSGordon Ross struct samr_encr_passwd *epw, 3421ed6b69aSGordon Ross char *new_pw_clear, 3431ed6b69aSGordon Ross uint8_t *crypt_key) 3441ed6b69aSGordon Ross { 3451ed6b69aSGordon Ross union { 3461ed6b69aSGordon Ross struct samr_user_password u; 3471ed6b69aSGordon Ross struct samr_encr_passwd e; 3481ed6b69aSGordon Ross } pwu; 3491ed6b69aSGordon Ross 3501ed6b69aSGordon Ross samr_fill_userpw(&pwu.u, new_pw_clear); 3511ed6b69aSGordon Ross 3521ed6b69aSGordon Ross (void) smb_auth_RC4(pwu.e.data, sizeof (pwu.e.data), 3531ed6b69aSGordon Ross crypt_key, SAMR_PWHASH_LEN, 3541ed6b69aSGordon Ross pwu.e.data, sizeof (pwu.e.data)); 3551ed6b69aSGordon Ross 3561ed6b69aSGordon Ross (void) memcpy(epw->data, pwu.e.data, sizeof (pwu.e.data)); 3571ed6b69aSGordon Ross (void) memset(pwu.e.data, 0, sizeof (pwu.e.data)); 3581ed6b69aSGordon Ross } 3591ed6b69aSGordon Ross 3601ed6b69aSGordon Ross /* 3611ed6b69aSGordon Ross * This fills in a samr_user_password (a.k.a. SAMPR_USER_PASSWORD 3621ed6b69aSGordon Ross * in the MS Net API) which has the new password "right justified" 3631ed6b69aSGordon Ross * in the buffer, and any space on the left filled with random junk 3641ed6b69aSGordon Ross * to improve the quality of the encryption that is subsequently 3651ed6b69aSGordon Ross * applied to this buffer before it goes over the wire. 3661ed6b69aSGordon Ross */ 3671ed6b69aSGordon Ross static void 3681ed6b69aSGordon Ross samr_fill_userpw(struct samr_user_password *upw, const char *new_pw) 3691ed6b69aSGordon Ross { 3701ed6b69aSGordon Ross smb_wchar_t *pbuf; 3711ed6b69aSGordon Ross uint32_t pwlen_bytes; 3721ed6b69aSGordon Ross size_t pwlen_wchars; 3731ed6b69aSGordon Ross 3741ed6b69aSGordon Ross /* 3751ed6b69aSGordon Ross * First fill the whole buffer with the random junk. 3761ed6b69aSGordon Ross * (Slightly less random when debugging:) 3771ed6b69aSGordon Ross */ 3781ed6b69aSGordon Ross #ifdef DEBUG 3791ed6b69aSGordon Ross (void) memset(upw->Buffer, '*', sizeof (upw->Buffer)); 3801ed6b69aSGordon Ross #else 3811ed6b69aSGordon Ross randomize((char *)upw->Buffer, sizeof (upw->Buffer)); 3821ed6b69aSGordon Ross #endif 3831ed6b69aSGordon Ross 3841ed6b69aSGordon Ross /* 3851ed6b69aSGordon Ross * Now overwrite the last pwlen characters of 3861ed6b69aSGordon Ross * that buffer with the password, and set the 3871ed6b69aSGordon Ross * length field so the receiving end knows where 3881ed6b69aSGordon Ross * the junk ends and the real password starts. 3891ed6b69aSGordon Ross */ 3901ed6b69aSGordon Ross pwlen_wchars = smb_wcequiv_strlen(new_pw) / 2; 3911ed6b69aSGordon Ross if (pwlen_wchars > SAMR_USER_PWLEN) 3921ed6b69aSGordon Ross pwlen_wchars = SAMR_USER_PWLEN; 3931ed6b69aSGordon Ross pwlen_bytes = pwlen_wchars * 2; 3941ed6b69aSGordon Ross 3951ed6b69aSGordon Ross pbuf = &upw->Buffer[SAMR_USER_PWLEN - pwlen_wchars]; 3961ed6b69aSGordon Ross (void) smb_mbstowcs(pbuf, new_pw, pwlen_wchars); 3971ed6b69aSGordon Ross 3981ed6b69aSGordon Ross /* Yes, this is in Bytes, not wchars. */ 3991ed6b69aSGordon Ross upw->Length = htolel(pwlen_bytes); 4008d7e4166Sjose borrego } 401