/* * Copyright 2000, International Business Machines Corporation and others. * All Rights Reserved. * * This software has been released under the terms of the IBM Public * License. For details, see the LICENSE file in the top-level source * directory or online at http://www.openafs.org/dl/license10.html */ /* * Implementation of basic procedures for the AFS user account * facility. */ /* * --------------------- Required definitions --------------------- */ #include #include #include #include /*MAXKTCREALMLEN*/ #include "uss_procs.h" /*Module interface */ #include "uss_common.h" /*Common defs & operations */ #include "uss_acl.h" /*ACL-related operations */ #undef USS_PROCS_DB #undef USS_PROCS_DB_INSTANCE #undef USS_PROCS_DB_BUILDDIR #define uss_procs_MAX_SIZE 2048 char temp[1000]; extern int line; static int Copy(char *a_from, char *a_to, int a_mode); static int Echo(char *a_s, char *a_f, int a_mode); /*----------------------------------------------------------------------- * EXPORTED uss_procs_BuildDir * * Environment: * Called from the code generated by the uss grammar. * * Side Effects: * As advertised. *------------------------------------------------------------------------*/ afs_int32 uss_procs_BuildDir(char *a_path, char *a_mode, char *a_owner, char *a_access) { /*uss_procs_BuildDir */ int m, o; char buf[1000]; struct uss_subdir *new_dir; struct stat stbuf; /* * Don't do anything if there's already a problem. */ if (uss_syntax_err) return (1); if (uss_verbose) printf("Building directory '%s'; owner: '%s', ACL: '%s'\n", a_path, a_owner, a_access); /* * If we've not been given permission to overwrite things, make sure * the target doesn't exist before doing anything. */ if (!uss_OverwriteThisOne) { strcpy(temp, a_path); if (!stat(temp, &stbuf)) { if (uss_verbose) printf("\t[Directory exists, NOT overwriting it]\n"); return (0); } } sscanf(a_mode, "%o", &m); o = uss_procs_GetOwner(a_owner); #ifdef USS_PROCS_DB_BUILDDIR printf("DEBUGGING: Owner '%s' is uid %d\n", a_owner, o); #endif /* USS_PROCS_DB_BUILDDIR */ if (!uss_DryRun) { if (mkdir(a_path, m)) { /* * Can't make the directory. Complain if the directory doesn't * already exist. */ if (errno != EEXIST) { uss_procs_PrintErr(line, "Failed to create directory '%s': %s\n", a_path, strerror(errno)); return (1); } /*Directory didn't exist */ } /*Create the directory */ } /*Not a dry run */ else { if (uss_OverwriteThisOne) fprintf(stderr, "\t[Dry run: mkdir %s, mode %o]\n", a_path, m); } /*Dry run */ if (!uss_DryRun) { if (chmod(a_path, m)) { uss_procs_PrintErr(line, "Can't chmod() directory '%s' to be '%s' : %s\n", a_path, a_mode, strerror(errno)); return (1); } /* chmod the directory */ if (chown(a_path, o, -1)) { uss_procs_PrintErr(line, "Can't chown() directory '%s' to be owned by '%s' (uid %d): %s\n", a_path, a_owner, o, strerror(errno)); return (1); } /*Couldn't chown */ } /*Not a dry run */ else { fprintf(stderr, "\t[Dry run: chown() directory '%s' to be owned by user]\n", a_path); } /*Dry run */ /* * Set the ACL for this new directory so that the uss_AccountCreator * is the only party that has rights. This will be corrected as the * final action performed on the account. */ sprintf(buf, "%s %s all", a_path, uss_AccountCreator); if (!uss_DryRun) { if (uss_verbose) fprintf(stderr, "Setting ACL: '%s'\n", buf); if (uss_acl_SetAccess(buf, 1, 0)) return (1); } /*Not a dry run */ else { fprintf(stderr, "\t[Dry run: uss_acl_SetAccess(%s) on '%s']\n", buf, a_path); } /*Dry run */ /* * Use our linked list to remember this directory's true ACL setting so * we may set it correctly at the tail end of the account creation. */ new_dir = malloc(sizeof(struct uss_subdir)); new_dir->previous = uss_currentDir; new_dir->path = strdup(a_path); new_dir->finalACL = strdup(a_access); uss_currentDir = new_dir; /* * Return the happy news. */ return (0); } /*uss_procs_BuildDir */ /*----------------------------------------------------------------------- * EXPORTED uss_procs_CpFile * * Environment: * Called from the code generated by the uss grammar. * * Side Effects: * As advertised. *------------------------------------------------------------------------*/ afs_int32 uss_procs_CpFile(char *a_path, char *a_mode, char *a_owner, char *a_proto) { /*uss_procs_CpFile */ int m, o; struct stat stbuf; char *cp; /* * Don't do anything if something has already gone wrong. */ if (uss_syntax_err) return (1); if (uss_verbose) printf("Installing '%s'\n", a_path); /* * If we've not been given permission to overwrite things, make sure * the target doesn't exist before doing anything. */ if (!uss_OverwriteThisOne) { strcpy(temp, a_path); if (!stat(temp, &stbuf)) { if (uss_verbose) printf("\t[Entry exists, NOT overwriting it]\n"); return (0); } } sscanf(a_mode, "%o", &m); o = uss_procs_GetOwner(a_owner); strcpy(temp, a_proto); if (stat(temp, &stbuf)) { uss_procs_PrintErr(line, "Failed to stat '%s': %s\n", a_proto, strerror(errno)); return (1); } if (stbuf.st_mode & S_IFDIR) { if ((cp = strrchr(a_path, '/')) == NULL) { strcat(a_proto, "/"); strcat(a_proto, a_path); } else { /* * Append the last part (file name). */ strcat(a_proto, cp); } } /*Target is a directory */ if (!uss_DryRun) { if (Copy(a_proto, a_path, m)) { uss_procs_PrintErr(line, "Failed to copy '%s' to '%s'\n", a_proto, a_path); return (1); } /*Copy failed */ } /*Not a dry run */ else { fprintf(stderr, "\t[Dry run: Copying '%s' to '%s', mode %o]\n", a_proto, a_path, m); } if (!uss_DryRun) { if (chown(a_path, o, -1)) { uss_procs_PrintErr(line, "Can't chown() file '%s' to be owned by '%s' (uid %d): %s\n", a_path, a_owner, o, strerror(errno)); return (1); } /*chown failed */ } /*Not a dry run */ else { fprintf(stderr, "\t[Dry run: chown() file '%s' to be owned by user]\n", a_path); } /*Dry run */ /* * Return the happy news. */ return (0); } /*uss_procs_CpFile */ /*----------------------------------------------------------------------- * EXPORTED uss_procs_EchoToFile * * Environment: * Called from the code generated by the uss grammar. * * Side Effects: * As advertised. *------------------------------------------------------------------------*/ afs_int32 uss_procs_EchoToFile(char *a_path, char *a_mode, char *a_owner, char *a_content) { /*uss_procs_EchoToFile */ int m, o; struct stat stbuf; /* * Don't do anything if something has already gone wrong. */ if (uss_syntax_err) return (1); if (uss_verbose) printf("Echoing to '%s'\n", a_path); /* * If we've not been given permission to overwrite things, make sure * the target doesn't exist before doing anything. */ if (!uss_OverwriteThisOne) { strcpy(temp, a_path); if (!stat(temp, &stbuf)) { if (uss_verbose) printf("\t[Entry exists, NOT overwriting it]\n"); return (0); } } sscanf(a_mode, "%o", &m); o = uss_procs_GetOwner(a_owner); if (!uss_DryRun) { if (Echo(a_content, a_path, m)) { uss_procs_PrintErr(line, "Failed to echo string '%s' to file '%s'\n", a_content, a_path); return (1); } } /*Not a dry run */ else { fprintf(stderr, "\t[Dry run: Echoing '%s' into file '%s']\n", a_content, a_path); } /*Dry run */ if (!uss_DryRun) { if (chown(a_path, o, -1)) { uss_procs_PrintErr(line, "Can't chown() file '%s' to be owned by '%s' (uid %d): %s\n", a_path, a_owner, o, strerror(errno)); return (1); } } /*Not a dry run */ else { fprintf(stderr, "\t[Dry run: chown() file '%s' to be owned by user]\n", a_path); } /*Dry run */ /* * Return the happy news. */ return (0); } /*uss_procs_EchoToFile */ /*----------------------------------------------------------------------- * EXPORTED uss_procs_Exec * * Environment: * Called from the code generated by the uss grammar, as well as * from uss.c itself. * * Side Effects: * As advertised. *------------------------------------------------------------------------*/ afs_int32 uss_procs_Exec(char *a_command) { /*uss_procs_Exec */ if (uss_verbose) printf("Running '%s'\n", a_command); if (!uss_DryRun) { if (system(a_command)) { uss_procs_PrintErr(line, "Failed to run the '%s' command: %s\n", a_command, strerror(errno)); return (1); } } /*Not a dry run */ else { fprintf(stderr, "\t[Dry run: executing '%s']\n", a_command); } /*Dry run */ /* * Return the happy news. */ return (0); } /*uss_procs_Exec */ /*----------------------------------------------------------------------- * EXPORTED uss_procs_SetLink * * Environment: * Called from the code generated by the uss grammar. * * Side Effects: * As advertised. *------------------------------------------------------------------------*/ afs_int32 uss_procs_SetLink(char *a_path1, char *a_path2, char a_type) { /*uss_procs_SetLink */ struct stat stbuf; if (uss_verbose) printf("Setting link '%s' to '%s'\n", a_path1, a_path2); /* * If we've not been given permission to overwrite things, make sure * the target doesn't exist before doing anything. */ if (!uss_OverwriteThisOne) { strcpy(temp, a_path2); if (!stat(temp, &stbuf)) { if (uss_verbose) printf("\t[Entry exists, NOT overwriting it]\n"); return (0); } } if (a_type == 's') { /* * Symbolic link. */ if (!uss_DryRun) { if (symlink(a_path1, a_path2)) { uss_procs_PrintErr(line, "Failed to make symlink '%s' to '%s': %s\n", a_path1, a_path2, strerror(errno)); return (1); } } /*Dry run */ else { fprintf(stderr, "\t[Dry run: Making symlink '%s' to '%s']\n", a_path1, a_path2); } /*Not a dry run */ } /*Symbolic link */ else { /* * Hard link. */ if (!uss_DryRun) { if (link(a_path1, a_path2)) { uss_procs_PrintErr(line, "Failed to make hard link '%s' to '%s': %s\n", a_path1, a_path2, strerror(errno)); return (1); } } /*Dry run */ else { fprintf(stderr, "\t[Dry run: Making hard link '%s' to '%s']\n", a_path1, a_path2); } /*Not a dry run */ } /*Hard link */ /* * Return the happy news. */ return (0); } /*uss_procs_SetLink */ /*----------------------------------------------------------------------- * EXPORTED uss_procs_GetOwner * * Environment: * Nothing interesting. * * Side Effects: * As advertised. *------------------------------------------------------------------------*/ int uss_procs_GetOwner(char *a_ownerStr) { /*uss_procs_GetOwner */ struct passwd *pw; /*Ptr to password file entry */ int ownerID; /*Numerical owner */ ownerID = atoi(a_ownerStr); if ((ownerID == 0) && (a_ownerStr[0] != '0')) { /* * The owner is not in numerical format */ if ((pw = getpwnam(a_ownerStr)) == NULL) { uss_procs_PrintErr(line, "Owner '%s' is an unknown user\n", a_ownerStr); return (1); } return (pw->pw_uid); } else return (ownerID); } /*uss_procs_GetOwner */ /*----------------------------------------------------------------------- * static Copy * * Description: * Copies the "from" file to the "to" file and sets the mode. * * Arguments: * a_from : File to copy from. * a_to : File to copy to. * a_mode : New Unix mode to set. * * Returns: * 0 if everything went well, * if there was a problem. * * Environment: * Nothing interesting. * * Side Effects: * As advertised. *------------------------------------------------------------------------*/ static int Copy(char *a_from, char *a_to, int a_mode) { /*Copy */ int fd1, fd2; char buf[BUFSIZ]; int rcnt, wcnt = -1, rc; umask(0); fd1 = open(a_to, O_EXCL | O_CREAT | O_WRONLY, a_mode); if (fd1 < 0) { if (errno == EEXIST) { /* * The file exists. Since we can only be called when overwriting * has been enabled, we back off and open the file normally (not * supplying O_EXCL), then continue normally. */ fd1 = open(a_to, O_CREAT | O_WRONLY, a_mode); if (fd1 < 0) { uss_procs_PrintErr(line, "%s: Failed to open '%s' for overwrite: %s.\n", uss_whoami, a_to, strerror(errno)); return (1); } } else { uss_procs_PrintErr(line, "%s: Failed to open '%s': %s.\n", uss_whoami, a_to, strerror(errno)); return (1); } } if ((fd2 = open(a_from, O_RDONLY, 0)) < 0) { uss_procs_PrintErr(line, "%s: Error reading '%s': %s\n", uss_whoami, a_from, strerror(errno)); close(fd1); return (1); } do { rcnt = read(fd2, buf, BUFSIZ); if (rcnt == -1) break; wcnt = write(fd1, buf, rcnt); } while (rcnt == BUFSIZ && rcnt == wcnt); if (rcnt == -1 || wcnt != rcnt) { uss_procs_PrintErr(line, "read/write error to %s\n", a_to); close(fd1); close(fd2); return (1); } rc = close(fd1); if (rc) { uss_procs_PrintErr(line, "Failed to close '%s' %s\n", a_to, strerror(errno)); return (1); } if ((rc = close(fd2))) uss_procs_PrintErr(line, "Warning: Failed to close '%s': %s\n", a_from, strerror(errno)); return (0); } /*Copy */ /*----------------------------------------------------------------------- * static Echo * * Description: * Writes a string into a file and sets its mode. * * Arguments: * a_s : String to write. * a_f : Filename to write to. * a_mode : New Unix mode to set. * * Returns: * 0 if everything went well, * if there was a problem. * * Environment: * Nothing interesting. * * Side Effects: * As advertised. *------------------------------------------------------------------------*/ static int Echo(char *a_s, char *a_f, int a_mode) { /*Echo */ int fd; umask(0); fd = open(a_f, O_EXCL | O_CREAT | O_WRONLY, a_mode); if (fd < 0) { if (errno == EEXIST) { /* * The file exists. Since we can only be called when * overwriting has been enabled, we back off and open the * file normally (not supplying the O_EXCL), then continue * normally. Notice that we must truncate the file, since * if the original file is longer than the stuff we're about * to put in, all the old data past the current write will * still be there. */ fd = open(a_f, O_TRUNC | O_WRONLY, a_mode); if (fd < 0) { uss_procs_PrintErr(line, "%s: Failed to open '%s' for overwrite: %s.\n", uss_whoami, a_f, strerror(errno)); return (1); } } else { uss_procs_PrintErr(line, "%s: Failed to open '%s': %s. \n", uss_whoami, a_f, strerror(errno)); return (1); } } if (write(fd, a_s, strlen(a_s)) != strlen(a_s) || write(fd, "\n", 1) != 1) { uss_procs_PrintErr(line, "Short write to '%s'\n", a_f); close(fd); return (1); } if (close(fd)) { uss_procs_PrintErr(line, "Failed to close '%s': %s\n", a_f, strerror(errno)); return (1); } return (0); } /*Echo */ /*----------------------------------------------------------------------- * static uss_procs_PickADir * * Description: * Tries to replace $AUTO by a subdir. Subdirs are given by means * of "G" in the configuration file. Each of the directories is * examined, and the one with the inimum number of entries is * picked. * * Arguments: * a_path : ??? * a_cp : ??? * * Returns: * 0 if everything went well, * -1 if there was a problem. * * Environment: * Called with THREE parameters from lex.c! * * Side Effects: * As advertised. *------------------------------------------------------------------------*/ afs_int32 uss_procs_PickADir(char *path, char *cp) { /*uss_procs_PickADir */ char cd[300]; /*Current directory for search */ int i, count, MinIndex = 0, mina = 10000; struct dirent *dp; DIR *dirp; char dirname[301]; if (uss_NumGroups == 0) { fprintf(stderr, "%s: No choice yet given to replace $AUTO\n", uss_whoami); fprintf(stderr, "%s: Use the G command before $AUTO in config file\n", uss_whoami); return (-1); } if (uss_Auto[0] != '\0') return (0); /* we have already done this for this user */ if (*(cp - 1) == '/') { /*it's ..../$AUTO */ for (i = 0; &path[i] != cp; i++) cd[i] = path[i]; cd[i] = '\0'; } else { if (path != cp) { fprintf(stderr, "%s: $AUTO must be used to replace the whole path or the whole name of a subdirectory. Found: %s$AUTO\n", uss_whoami, path); return (-1); } cd[0] = '/'; cd[1] = '\0'; } /* * We now have the current dir (cd). Search all of the given * subdirs (by G in template), count the number of entries in * each and pick the minimum. */ for (i = 0; i < uss_NumGroups; i++) { snprintf(dirname, sizeof(dirname), "%s/%s", cd, uss_DirPool[i]); if ((dirp = opendir(dirname)) == NULL) { if (errno != ENOTDIR) fprintf(stderr, "%s: Warning: Can't open dir '%s' (errno=%d). Skipped.\n", uss_whoami, dirname, errno); continue; /*Skip and continue anyway */ } count = 0; for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) if (dp->d_name[0] != '.') count++; /* forget about files starting with . */ #ifdef USS_PROCS_DB printf("debug: Dir '%s' has %d entries\n", dirname, count); #endif /* USS_PROCS_DB */ if (count < mina) { mina = count; MinIndex = i; } closedir(dirp); } if (mina == 10000) { /* We found nothing */ fprintf(stderr, "%s: Warning: No valid choice to replace $AUTO\n", uss_whoami); uss_Auto[0] = '\0'; } else { strcpy(uss_Auto, uss_DirPool[MinIndex]); if (uss_verbose) printf("Picking dir w/minimum number of entries: '%s'\n", uss_Auto); } return (0); } /*uss_procs_PickADir */ /*----------------------------------------------------------------------- * EXPORTED uss_procs_AddToDirPool * * Environment: * Called from the code generated by the uss grammar. * * Side Effects: * As advertised. *------------------------------------------------------------------------*/ int uss_procs_AddToDirPool(char *a_dirToAdd) { if (uss_NumGroups > 99) { return (-1); } strcpy(uss_DirPool[uss_NumGroups++], a_dirToAdd); return 0; } /*----------------------------------------------------------------------- * EXPORTED uss_procs_FindAndOpen * * Environment: * Nothing interesting. * * Side Effects: * As advertised. *------------------------------------------------------------------------*/ FILE * uss_procs_FindAndOpen(char *a_fileToOpen) { /*uss_procs_FindAndOpen */ #define NUM_TPL_PATHS 3 FILE *rv; /*Template file descriptor */ int i; /*Loop counter */ char tmp_str[uss_MAX_SIZE]; /*Tmp string */ static char TemplatePath[NUM_TPL_PATHS][1024]; /*Template directories */ int cant_read; /*Can't read the file? */ /* * If a full pathname was given, just take it as is. */ if (strchr(a_fileToOpen, '/')) { strcpy(tmp_str, a_fileToOpen); rv = fopen(a_fileToOpen, "r"); } else { /* * A relative pathname was given. Try to find the file in each of * the default template directories. */ cant_read = 0; sprintf(TemplatePath[0], "%s", "."); sprintf(TemplatePath[1], "/afs/%s/common/uss", uss_Cell); sprintf(TemplatePath[2], "%s", "/etc"); for (i = 0; i < NUM_TPL_PATHS; i++) { sprintf(tmp_str, "%s/%s", TemplatePath[i], a_fileToOpen); if ((rv = fopen(tmp_str, "r")) != NULL) break; /* * If the file was there but couldn't be opened, we have * to report this. */ if (errno != ENOENT) { cant_read = 1; break; } } /*Look in template directories */ /* * If we found and opened the file, we're happy. Otherwise, * print out what went wrong. */ if (rv != NULL) { if (uss_verbose) fprintf(stderr, "Using template '%s'\n", tmp_str); } /*Got it */ else { /* * Check to see if we specifically found the file but * couldn't read it. */ if (cant_read) fprintf(stderr, "%s: Can't open template '%s': %s\n", uss_whoami, tmp_str, strerror(errno)); else { fprintf(stderr, "%s: Can't find template '%s' in searchlist", uss_whoami, a_fileToOpen); for (i = 0; i < NUM_TPL_PATHS; i++) fprintf(stderr, " '%s'", TemplatePath[i]); fprintf(stderr, "\n"); } /*Can't find template */ } /*Didn't get it */ } /*Relative pathname given */ /* * Whatever happened, return what we got. */ return (rv); } /*uss_procs_FindAndOpen */ /*----------------------------------------------------------------------- * EXPORTED uss_procs_PrintErr * * Environment: * Nothing interesting. * * Side Effects: * As advertised. *------------------------------------------------------------------------*/ void uss_procs_PrintErr(int a_lineNum, char *a_fmt, ... ) { /*uss_procs_PrintErr */ va_list ap; va_start(ap, a_fmt); uss_syntax_err++; fprintf(stderr, "%s: Template file, line %d: ", uss_whoami, a_lineNum); vfprintf(stderr, a_fmt, ap); va_end(ap); } /*uss_procs_PrintErr */