1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358
|
/****************************************************************
* *
* Copyright (c) 2009-2023 Fidelity National Information *
* Services, Inc. and/or its subsidiaries. All rights reserved. *
* *
* This source code contains the intellectual property *
* of its copyright holder(s), and is made available *
* under a license. If you do not know the terms of *
* the license, please stop and do not read further. *
* *
****************************************************************/
#include "mdef.h"
#include "gtm_string.h"
#include "gtm_stdio.h"
#include "gtm_stdlib.h"
#include "gtm_limits.h"
#include "gtm_unistd.h"
#include "gtm_fcntl.h"
#include "gtm_stat.h"
#include "gtm_pwd.h"
#include <grp.h>
#include "gtm_permissions.h"
#include "send_msg.h"
#include "eintr_wrappers.h"
GBLDEF gid_t *gid_list = NULL;
GBLDEF int gid_list_len = 0;
GBLREF char gtm_dist[GTM_PATH_MAX];
GBLREF boolean_t gtm_dist_ok_to_use;
#if defined(__MVS__)
# define LIBGTMSHR "%s/libgtmshr.dll"
#else
# define LIBGTMSHR "%s/libgtmshr.so"
#endif
/* Get the process's group list and stash the information to avoid repeated calls */
void gtm_init_gid_list(void)
{
int tmp_gid_list_len;
assert(NULL == gid_list);
tmp_gid_list_len = getgroups(0, NULL);
assert(0 <= tmp_gid_list_len);
if (0 < tmp_gid_list_len)
{
gid_list = malloc(tmp_gid_list_len * SIZEOF(gid_t));
gid_list_len = getgroups(tmp_gid_list_len, gid_list);
assert(gid_list_len == tmp_gid_list_len);
if (gid_list_len != tmp_gid_list_len)
{
gid_list_len = 0;
free(gid_list);
gid_list = NULL;
}
}
}
/* Search through the supplementary gid list for a match */
boolean_t gtm_gid_in_gid_list(gid_t gid)
{
int i;
assert(NULL != gid_list);
if (NULL == gid_list)
return FALSE;
for (i = 0; i < gid_list_len; i++)
if (gid == gid_list[i])
return TRUE;
return FALSE;
}
/* Return the group id of the distribution based on libgtmshr.xx[x]. If there is some
* problem accessing that file then return INVALID_GID which signals no change to group. Otherwise,
* the pointer to the stat buffer will contain the result of the call to STAT_FILE.
*/
gid_t gtm_get_group_id(struct stat *stat_buff)
{
int ret_stat;
char temp[GTM_PATH_MAX];
static boolean_t first_time = TRUE;
static struct stat st_buff;
if (!first_time)
{
*stat_buff = st_buff;
return st_buff.st_gid;
}
if (gtm_dist_ok_to_use)
{
/* build a path to libgtmshr.so or .sl on hpux or .dll on zos */
SNPRINTF(temp, SIZEOF(temp), LIBGTMSHR, gtm_dist);
STAT_FILE(temp, stat_buff, ret_stat);
if (0 == ret_stat)
{
first_time = FALSE;
st_buff = *stat_buff;
return(stat_buff->st_gid);
}
}
/* return INVALID_GID if STAT_FILE returned a -1 or gtm_dist has not been validated */
return (INVALID_GID);
}
/* Return TRUE if the "uid" parameter is a member of the "gid" group parameter.
* Return FALSE if it is not.
*/
boolean_t gtm_member_group_id(uid_t uid, gid_t gid, struct perm_diag_data *pdd)
{
struct group *grp;
struct passwd *uinfo;
# ifdef DEBUG
if (WBTEST_HELPOUT_FAILGROUPCHECK == gtm_white_box_test_case_number)
{
if (NULL == gid_list)
gtm_init_gid_list();
return FALSE;
}
# endif
/* Effective user is uid ?*/
if (GETEUID() == uid)
{ /* Effective group is gid? */
if (GETEGID() == gid)
return(TRUE);
/* EUID == uid but check if gid is in any groups associated with the process */
return GID_IN_GID_LIST(gid);
}
/* Get "uid" details */
uinfo = getpwuid(uid);
if (NULL == uinfo)
return(FALSE); /* If user id not found then assume uid not a member */
/* If the gid of the file is the same as the gid for the process uid we are done */
if (gid == uinfo->pw_gid)
return(TRUE);
/* Else get "gid" details */
grp = getgrgid(gid);
if (NULL == grp)
return(FALSE); /* If group id not found then assume uid not a member */
/* Else we have to compare the name stored in pwd struct with the names of the group members in the group struct */
while (NULL != *(grp->gr_mem))
{
if (!strcmp(uinfo->pw_name, *(grp->gr_mem++)))
return(TRUE);
}
return(FALSE);
}
/* Based on security rules in this routine, set
* a) *group_id to the group to be used for shared resources (journals, temp files etc.).
* If no change, will be set to INVALID_GID.
* b) *perm to the permissions to be used.
* c) *user_id to the user id to be used.
* If the user is root, *user_id may be set to a target uid if needed. Otherwise, it will be set to INVALID_UID.
*/
boolean_t gtm_permissions(struct stat *stat_buff, int *user_id, int *group_id, int *perm,
enum perm_target_types target_type, struct perm_diag_data *pdd)
{
uid_t this_uid, file_uid;
gid_t this_gid, file_gid;
gid_t gtm_dist_gid = INVALID_GID;
struct stat dist_stat_buff;
int this_uid_is_file_owner;
int this_uid_is_root;
int this_uid_in_file_group;
int owner_in_file_group;
int gtm_group_restricted;
int file_owner_perms, file_group_perms, file_other_perms;
int new_owner_perms, new_group_perms, new_other_perms;
char *strnow, *strtop;
int i, len;
mode_t st_mode, dir_mode = 0;
/* get this_uid/gid */
this_uid = GETEUID();
this_gid = GETEGID();
file_uid = stat_buff->st_uid; /* get owning file uid */
file_gid = stat_buff->st_gid; /* get owning file gid */
/* set variables for permission logic */
this_uid_is_file_owner = (this_uid == file_uid);
this_uid_is_root = (0 == this_uid);
this_uid_in_file_group = gtm_member_group_id(this_uid, file_gid, pdd);
owner_in_file_group = gtm_member_group_id(file_uid, file_gid, pdd);
*user_id = INVALID_UID; /* set default uid */
*group_id = INVALID_GID; /* set default gid */
assert((PERM_FILE & target_type) || (PERM_IPC & target_type)); /* code below relies on this */
st_mode = stat_buff->st_mode;
/* General rule for permissions below. If target is a FILE (not an IPC, i.e. "(target_type & PERM_FILE) == TRUE",
* and if "this_uid_is_file_owner" is FALSE but "this_uid_in_file_group" is TRUE, copy over group permissions of
* source file into owner permissions of the target file. This ensures that the target file is accessible by the
* new owner (who is governed by the owner permissions) as well as other members of the group owning the source
* file (and in turn the target file).
*/
file_owner_perms = (0600 & st_mode);
file_group_perms = (0060 & st_mode);
if (file_owner_perms && !(0066 & st_mode))
{ /* File is only user accessible */
/* Use default group */
assert(this_uid_is_file_owner || this_uid_is_root);
if (this_uid_is_root)
{
*user_id = file_uid;
*group_id = file_gid;
}
/* Else: use default uid/gid */
new_owner_perms = ((PERM_FILE & target_type) ? file_owner_perms : 0600);
*perm = new_owner_perms; /* read and/or write for ONLY user and none for group/other */
} else if (file_group_perms && !(0606 & st_mode))
{ /* File is only group accessible */
if (this_uid_is_root)
{
*user_id = file_uid;
new_group_perms = ((PERM_FILE & target_type)) ? file_group_perms : 0060;
*perm = new_group_perms;
} else
{
/* No need to initialize *user_id. Use default uid instead. */
if (PERM_FILE & target_type)
{
new_owner_perms = (file_group_perms << 3);
new_group_perms = file_group_perms;
} else
{
new_owner_perms = 0600;
new_group_perms = 0060;
}
*perm = new_owner_perms | new_group_perms;
}
*group_id = file_gid; /* use file group */
} else
{ /* File is other accessible OR accessible to user and group but not other */
file_other_perms = (0006 & st_mode);
if (PERM_IPC & target_type)
{
new_owner_perms = file_owner_perms ? 0600 : 0000;
new_group_perms = file_group_perms ? 0060 : 0000;
new_other_perms = file_other_perms ? 0006 : 0000;
} else
{
new_owner_perms = file_owner_perms;
new_group_perms = file_group_perms;
new_other_perms = file_other_perms;
}
/* Find restricted group, if any */
gtm_dist_gid = gtm_get_group_id(&dist_stat_buff);
dir_mode = (INVALID_GID != gtm_dist_gid) ? dist_stat_buff.st_mode : 0; /* 4SCA: Assigned value is garbage */
gtm_group_restricted = ((INVALID_GID != gtm_dist_gid) && !(dir_mode & 01)); /* not other executable */
if ((this_uid_is_file_owner && this_uid_in_file_group) || this_uid_is_root)
{
if (this_uid_is_root) /* otherwise, use default uid */
*user_id = file_uid;
*group_id = file_gid; /* use file group */
*perm = new_owner_perms | new_group_perms | new_other_perms;
} else if (this_uid_is_file_owner)
{ /* This uid has access via file owner membership but is not a member of the file group */
assert(!this_uid_in_file_group);
/* Since there could be userids that are members of the file group as well as the new group,
* one needs to keep the existing group permissions while setting up the new group permissions.
*/
new_group_perms = (new_group_perms | (new_other_perms << 3));
if (gtm_group_restricted)
{
assert((WBTEST_HELPOUT_FAILGROUPCHECK == gtm_white_box_test_case_number)
|| gtm_member_group_id(this_uid, gtm_dist_gid, pdd));
*group_id = gtm_dist_gid; /* use restricted group */
*perm = new_owner_perms | new_group_perms;
} else
{
/* No need to set *group_id. Use default group.
* But because file owner is not part of the file group, transfer group permissions of
* file to "other" when creating the new file so members of the file group (that are not
* part of the default group of the file owner) can still access the new file with same
* permissions as the source file. And transfer other permissions of file to "group" when
* creating the new file for similar reasons. Note that it is possible for userids to exist
* that are part of the file group and the default group of the file owner. For those userids,
* we need to ensure the group permissions of the new file stay same as source file. Therefore
* we need the new group and other permissions to be a bitwise OR of the old group and other.
*/
assert(file_owner_perms);
new_other_perms = new_group_perms >> 3;
*perm = new_owner_perms | new_group_perms | new_other_perms;
}
} else
{ /* This uid has access via file group OR file other membership */
/* Use default uid in all cases below */
if ((this_uid_in_file_group && owner_in_file_group) || this_uid_is_root)
{
*group_id = file_gid; /* use file group */
assert(file_group_perms);
new_owner_perms = new_group_perms << 3;
*perm = new_owner_perms | new_group_perms | new_other_perms;
} else if (gtm_group_restricted)
{
assert((WBTEST_HELPOUT_FAILGROUPCHECK == gtm_white_box_test_case_number)
|| gtm_member_group_id(this_uid, gtm_dist_gid, pdd));
*group_id = gtm_dist_gid; /* use restricted group */
new_group_perms = (new_group_perms | (new_other_perms << 3));
/* "owner" gets group permissions if in group, otherwise "other" permissions */
new_owner_perms = this_uid_in_file_group ? (new_group_perms << 3) : (new_other_perms << 6);
*perm = new_owner_perms | new_group_perms;
} else
{
if (this_uid_in_file_group)
{
*group_id = file_gid; /* use file group */
/* Since owner_in_file_group is FALSE, we need to make sure owner permissions
* are transferred to "other" in the new file.
*/
new_other_perms |= (new_owner_perms >> 6);
new_owner_perms = new_group_perms << 3;
} else
{
/* Use default group; Use 'other' permissions for owner */
new_owner_perms = (new_other_perms << 6);
new_group_perms = (new_group_perms | (new_other_perms << 3));
new_other_perms = new_group_perms >> 3;
}
*perm = new_owner_perms | new_group_perms | new_other_perms;
}
}
}
if (target_type & PERM_EXEC)
*perm |= ((*perm & 0444) >> 2); /* Grab the read bits, shift them to the exec bit position, and add them back in. */
assert(*perm);
if (!(this_uid_is_root) && (!(*perm & 0600)
|| (!(this_uid_is_file_owner) && !(this_uid_in_file_group) && !(0006 & st_mode))))
{ /* Either permissions don't include owner or could not ascertain group membership */
pdd->this_uid = this_uid;
pdd->this_gid = this_gid;
pdd->file_uid = file_uid; /* get owning file uid */
pdd->file_gid = file_gid; /* get owning file gid */
pdd->lib_gid = gtm_dist_gid;
SNPRINTF(pdd->file_perm, SIZEOF(pdd->file_perm), "%04o", st_mode & PERMALL);
SNPRINTF(pdd->lib_perm, SIZEOF(pdd->lib_perm), "%04o", dir_mode & PERMALL);
if (gid_list_len > 0)
{
strtop = pdd->print_gid_list + MAX_PRINT_GID_LEN;
strnow = pdd->print_gid_list;
for (i = 0; (strnow < strtop) && (i < gid_list_len); i++)
if (this_gid != gid_list[i]) /* Avoid printing the primary GID */
strnow += SNPRINTF(strnow, (strtop - strnow), ",%d", (int) gid_list[i]);
if (strnow >= strtop)
{ /* It's too long, terminate with an ellipsis*/
strnow = pdd->print_gid_list + MAX_PRINT_GID_LEN - GID_ELLIPSIS_LEN;
SNPRINTF(strnow, GID_ELLIPSIS_LEN, GID_ELLIPSIS);
}
pdd->print_gid_list_len = strlen(pdd->print_gid_list);
assert(pdd->print_gid_list_len < MAX_PRINT_GID_LEN);
} else
pdd->print_gid_list_len = 0;
return FALSE;
}
return TRUE;
}
|