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 #include <sys/types.h> 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <ctype.h> 34 #include <sys/stat.h> 35 #include <grp.h> 36 #include <unistd.h> 37 #include <userdefs.h> 38 #include <errno.h> 39 #include <limits.h> 40 #include "users.h" 41 #include "messages.h" 42 43 #define MYBUFSIZE (LINE_MAX) 44 /* Corresponds to MYBUFSIZE in grpck.c, BUFCONST in nss_dbdefs.c */ 45 46 int 47 edit_group(char *login, char *new_login, gid_t gids[], int overwrite) 48 { 49 char **memptr; 50 char t_name[] = "/etc/gtmp.XXXXXX"; 51 int fd; 52 FILE *e_fptr, *t_fptr; 53 struct group *g_ptr; /* group structure from fgetgrent */ 54 int i; 55 int modified = 0; 56 struct stat sbuf; 57 58 int bufsize, g_length, sav_errno; 59 long g_curr = 0L; 60 char *g_string, *new_g_string, *gstr_off; 61 62 if ((e_fptr = fopen(GROUP, "r")) == NULL) 63 return (EX_UPDATE); 64 65 if (fstat(fileno(e_fptr), &sbuf) != 0) { 66 (void) fclose(e_fptr); 67 return (EX_UPDATE); 68 } 69 70 if ((fd = mkstemp(t_name)) == -1) { 71 (void) fclose(e_fptr); 72 return (EX_UPDATE); 73 } 74 75 if ((t_fptr = fdopen(fd, "w")) == NULL) { 76 (void) close(fd); 77 (void) unlink(t_name); 78 (void) fclose(e_fptr); 79 return (EX_UPDATE); 80 } 81 82 /* 83 * Get ownership and permissions correct 84 */ 85 86 if (fchmod(fd, sbuf.st_mode) != 0 || 87 fchown(fd, sbuf.st_uid, sbuf.st_gid) != 0) { 88 (void) fclose(t_fptr); 89 (void) fclose(e_fptr); 90 (void) unlink(t_name); 91 return (EX_UPDATE); 92 } 93 94 g_curr = ftell(e_fptr); 95 96 /* Make TMP file look like we want GROUP file to look */ 97 98 bufsize = MYBUFSIZE; 99 if ((g_string = malloc(bufsize)) == NULL) { 100 (void) fclose(t_fptr); 101 (void) fclose(e_fptr); 102 (void) unlink(t_name); 103 return (EX_UPDATE); 104 } 105 /* 106 * bufsize contains the size of the currently allocated buffer 107 * buffer size, which is initially MYBUFSIZE but when a line 108 * greater than MYBUFSIZE is encountered then bufsize gets increased 109 * by MYBUFSIZE. 110 * g_string always points to the beginning of the buffer (even after 111 * realloc()). 112 * gstr_off = g_string + MYBUFSIZE * (n), where n >= 0. 113 */ 114 while (!feof(e_fptr) && !ferror(e_fptr)) { 115 g_length = 0; 116 gstr_off = g_string; 117 while (fgets(gstr_off, (bufsize - g_length), e_fptr) != NULL) { 118 g_length += strlen(gstr_off); 119 if (g_string[g_length - 1] == '\n' || feof(e_fptr)) 120 break; 121 new_g_string = realloc(g_string, (bufsize + MYBUFSIZE)); 122 if (new_g_string == NULL) { 123 free(g_string); 124 (void) fclose(t_fptr); 125 (void) fclose(e_fptr); 126 (void) unlink(t_name); 127 return (EX_UPDATE); 128 } 129 bufsize += MYBUFSIZE; 130 g_string = new_g_string; 131 gstr_off = g_string + g_length; 132 } 133 if (g_length == 0) { 134 continue; 135 } 136 137 /* While there is another group string */ 138 139 (void) fseek(e_fptr, g_curr, SEEK_SET); 140 errno = 0; 141 g_ptr = fgetgrent(e_fptr); 142 sav_errno = errno; 143 g_curr = ftell(e_fptr); 144 145 if (g_ptr == NULL) { 146 /* tried to parse a group string over MYBUFSIZ char */ 147 if (sav_errno == ERANGE) 148 errmsg(M_GROUP_ENTRY_OVF); 149 else 150 errmsg(M_READ_ERROR); 151 152 modified = 0; /* bad group file: cannot rebuild */ 153 break; 154 } 155 156 /* first delete the login from the group, if it's there */ 157 if (overwrite || !gids) { 158 if (g_ptr->gr_mem != NULL) { 159 for (memptr = g_ptr->gr_mem; *memptr; 160 memptr++) { 161 if (strcmp(*memptr, login) == 0) { 162 /* Delete this one */ 163 char **from = memptr + 1; 164 165 g_length -= (strlen(*memptr)+1); 166 167 do { 168 *(from - 1) = *from; 169 } while (*from++); 170 171 modified++; 172 break; 173 } 174 } 175 } 176 } 177 178 /* now check to see if group is one to add to */ 179 if (gids) { 180 for (i = 0; gids[i] != -1; i++) { 181 if (g_ptr->gr_gid == gids[i]) { 182 /* Find end */ 183 for (memptr = g_ptr->gr_mem; *memptr; 184 memptr++) 185 ; 186 g_length += strlen(new_login ? 187 new_login : login)+1; 188 189 *memptr++ = new_login ? 190 new_login : login; 191 *memptr = NULL; 192 193 modified++; 194 } 195 } 196 } 197 putgrent(g_ptr, t_fptr); 198 } 199 free(g_string); 200 201 (void) fclose(e_fptr); 202 203 if (fclose(t_fptr) != 0) { 204 (void) unlink(t_name); 205 return (EX_UPDATE); 206 } 207 208 /* Now, update GROUP file, if it was modified */ 209 if (modified) { 210 if (rename(t_name, GROUP) != 0) { 211 (void) unlink(t_name); 212 return (EX_UPDATE); 213 } 214 return (EX_SUCCESS); 215 } else { 216 (void) unlink(t_name); 217 return (EX_SUCCESS); 218 } 219 } 220