1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 27 /* All Rights Reserved */ 28 29 30 #pragma ident "%Z%%M% %I% %E% SMI" 31 32 #include <sys/types.h> 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <ctype.h> 37 #include <sys/stat.h> 38 #include <grp.h> 39 #include <unistd.h> 40 #include <userdefs.h> 41 #include <errno.h> 42 #include <limits.h> 43 #include "users.h" 44 #include "messages.h" 45 46 #define MYBUFSIZE (LINE_MAX) 47 /* Corresponds to MYBUFSIZE in grpck.c, BUFCONST in nss_dbdefs.c */ 48 49 int 50 edit_group(char *login, char *new_login, gid_t gids[], int overwrite) 51 { 52 char **memptr; 53 char t_name[] = "/etc/gtmp.XXXXXX"; 54 int fd; 55 FILE *e_fptr, *t_fptr; 56 struct group *g_ptr; /* group structure from fgetgrent */ 57 int i; 58 int modified = 0; 59 struct stat sbuf; 60 61 int bufsize, g_length, sav_errno; 62 long g_curr = 0L; 63 char *g_string, *new_g_string, *gstr_off; 64 65 if ((e_fptr = fopen(GROUP, "r")) == NULL) 66 return (EX_UPDATE); 67 68 if (fstat(fileno(e_fptr), &sbuf) != 0) { 69 (void) fclose(e_fptr); 70 return (EX_UPDATE); 71 } 72 73 if ((fd = mkstemp(t_name)) == -1) { 74 (void) fclose(e_fptr); 75 return (EX_UPDATE); 76 } 77 78 if ((t_fptr = fdopen(fd, "w")) == NULL) { 79 (void) close(fd); 80 (void) unlink(t_name); 81 (void) fclose(e_fptr); 82 return (EX_UPDATE); 83 } 84 85 /* 86 * Get ownership and permissions correct 87 */ 88 89 if (fchmod(fd, sbuf.st_mode) != 0 || 90 fchown(fd, sbuf.st_uid, sbuf.st_gid) != 0) { 91 (void) fclose(t_fptr); 92 (void) fclose(e_fptr); 93 (void) unlink(t_name); 94 return (EX_UPDATE); 95 } 96 97 g_curr = ftell(e_fptr); 98 99 /* Make TMP file look like we want GROUP file to look */ 100 101 bufsize = MYBUFSIZE; 102 if ((g_string = malloc(bufsize)) == NULL) { 103 (void) fclose(t_fptr); 104 (void) fclose(e_fptr); 105 (void) unlink(t_name); 106 return (EX_UPDATE); 107 } 108 /* 109 * bufsize contains the size of the currently allocated buffer 110 * buffer size, which is initially MYBUFSIZE but when a line 111 * greater than MYBUFSIZE is encountered then bufsize gets increased 112 * by MYBUFSIZE. 113 * g_string always points to the beginning of the buffer (even after 114 * realloc()). 115 * gstr_off = g_string + MYBUFSIZE * (n), where n >= 0. 116 */ 117 while (!feof(e_fptr) && !ferror(e_fptr)) { 118 g_length = 0; 119 gstr_off = g_string; 120 while (fgets(gstr_off, (bufsize - g_length), e_fptr) != NULL) { 121 g_length += strlen(gstr_off); 122 if (g_string[g_length - 1] == '\n' || feof(e_fptr)) 123 break; 124 new_g_string = realloc(g_string, (bufsize + MYBUFSIZE)); 125 if (new_g_string == NULL) { 126 free(g_string); 127 (void) fclose(t_fptr); 128 (void) fclose(e_fptr); 129 (void) unlink(t_name); 130 return (EX_UPDATE); 131 } 132 bufsize += MYBUFSIZE; 133 g_string = new_g_string; 134 gstr_off = g_string + g_length; 135 } 136 if (g_length == 0) { 137 continue; 138 } 139 140 /* While there is another group string */ 141 142 (void) fseek(e_fptr, g_curr, SEEK_SET); 143 errno = 0; 144 g_ptr = fgetgrent(e_fptr); 145 sav_errno = errno; 146 g_curr = ftell(e_fptr); 147 148 if (g_ptr == NULL) { 149 /* tried to parse a group string over MYBUFSIZ char */ 150 if (sav_errno == ERANGE) 151 errmsg(M_GROUP_ENTRY_OVF); 152 else 153 errmsg(M_READ_ERROR); 154 155 modified = 0; /* bad group file: cannot rebuild */ 156 break; 157 } 158 159 /* first delete the login from the group, if it's there */ 160 if (overwrite || !gids) { 161 if (g_ptr->gr_mem != NULL) { 162 for (memptr = g_ptr->gr_mem; *memptr; 163 memptr++) { 164 if (strcmp(*memptr, login) == 0) { 165 /* Delete this one */ 166 char **from = memptr + 1; 167 168 g_length -= (strlen(*memptr)+1); 169 170 do { 171 *(from - 1) = *from; 172 } while (*from++); 173 174 modified++; 175 break; 176 } 177 } 178 } 179 } 180 181 /* now check to see if group is one to add to */ 182 if (gids) { 183 for (i = 0; gids[i] != -1; i++) { 184 if (g_ptr->gr_gid == gids[i]) { 185 /* Find end */ 186 for (memptr = g_ptr->gr_mem; *memptr; 187 memptr++) 188 ; 189 g_length += strlen(new_login ? 190 new_login : login)+1; 191 192 *memptr++ = new_login ? 193 new_login : login; 194 *memptr = NULL; 195 196 modified++; 197 } 198 } 199 } 200 putgrent(g_ptr, t_fptr); 201 } 202 free(g_string); 203 204 (void) fclose(e_fptr); 205 206 if (fclose(t_fptr) != 0) { 207 (void) unlink(t_name); 208 return (EX_UPDATE); 209 } 210 211 /* Now, update GROUP file, if it was modified */ 212 if (modified) { 213 if (rename(t_name, GROUP) != 0) { 214 (void) unlink(t_name); 215 return (EX_UPDATE); 216 } 217 return (EX_SUCCESS); 218 } else { 219 (void) unlink(t_name); 220 return (EX_SUCCESS); 221 } 222 } 223