int read_pseudo_def(struct pseudo **pseudo, char *def) { int n, bytes; unsigned int major = 0, minor = 0, mode; char filename[2048], type, suid[100], sgid[100], *ptr; long long uid, gid; struct pseudo_dev *dev; n = sscanf(def, "%s %c %o %s %s %n", filename, &type, &mode, suid, sgid, &bytes); if(n < 5) { ERROR("Not enough or invalid arguments in pseudo file " "definition\n"); goto error; } switch(type) { case 'b': case 'c': n = sscanf(def + bytes, "%u %u", &major, &minor); if(n < 2) { ERROR("Not enough or invalid arguments in pseudo file " "definition\n"); goto error; } if(major > 0xfff) { ERROR("Major %d out of range\n", major); goto error; } if(minor > 0xfffff) { ERROR("Minor %d out of range\n", minor); goto error; } case 'f': if(def[bytes] == '\0') { ERROR("Not enough arguments in pseudo file " "definition\n"); goto error; } break; case 'd': case 'm': break; default: ERROR("Unsupported type %c\n", type); goto error; } if(mode > 07777) { ERROR("Mode %o out of range\n", mode); goto error; } uid = strtoll(suid, &ptr, 10); if(*ptr == '\0') { if(uid < 0 || uid > ((1LL << 32) - 1)) { ERROR("Uid %s out of range\n", suid); goto error; } } else { struct passwd *pwuid = getpwnam(suid); if(pwuid) uid = pwuid->pw_uid; else { ERROR("Uid %s invalid uid or unknown user\n", suid); goto error; } } gid = strtoll(sgid, &ptr, 10); if(*ptr == '\0') { if(gid < 0 || gid > ((1LL << 32) - 1)) { ERROR("Gid %s out of range\n", sgid); goto error; } } else { struct group *grgid = getgrnam(sgid); if(grgid) gid = grgid->gr_gid; else { ERROR("Gid %s invalid uid or unknown user\n", sgid); goto error; } } switch(type) { case 'b': mode |= S_IFBLK; break; case 'c': mode |= S_IFCHR; break; case 'd': mode |= S_IFDIR; break; case 'f': mode |= S_IFREG; break; } dev = malloc(sizeof(struct pseudo_dev)); if(dev == NULL) BAD_ERROR("Failed to create pseudo_dev\n"); dev->type = type; dev->mode = mode; dev->uid = uid; dev->gid = gid; dev->major = major; dev->minor = minor; if(type == 'f') { int res; printf("Executing dynamic pseudo file\n"); printf("\t\"%s\"\n", def); res = exec_file(def + bytes, dev); if(res == -1) { ERROR("Failed to execute dynamic pseudo file definition" " \"%s\"\n", def); return FALSE; } add_pseudo_file(dev); } *pseudo = add_pseudo(*pseudo, dev, filename, filename); return TRUE; error: ERROR("Bad pseudo file definition \"%s\"\n", def); return FALSE; }
int read_pseudo_def(char *def) { int n, bytes; unsigned int major = 0, minor = 0, mode; char type, *ptr; char suid[100], sgid[100]; /* overflow safe */ char *filename, *name; char *orig_def = def; long long uid, gid; struct pseudo_dev *dev; /* * Scan for filename, don't use sscanf() and "%s" because * that can't handle filenames with spaces */ filename = malloc(strlen(def) + 1); if(filename == NULL) MEM_ERROR(); for(name = filename; !isspace(*def) && *def != '\0';) { if(*def == '\\') { def ++; if (*def == '\0') break; } *name ++ = *def ++; } *name = '\0'; if(*filename == '\0') { ERROR("Not enough or invalid arguments in pseudo file " "definition \"%s\"\n", orig_def); goto error; } n = sscanf(def, " %c %o %99s %99s %n", &type, &mode, suid, sgid, &bytes); def += bytes; if(n < 4) { ERROR("Not enough or invalid arguments in pseudo file " "definition \"%s\"\n", orig_def); switch(n) { case -1: /* FALLTHROUGH */ case 0: ERROR("Read filename, but failed to read or match " "type\n"); break; case 1: ERROR("Read filename and type, but failed to read or " "match octal mode\n"); break; case 2: ERROR("Read filename, type and mode, but failed to " "read or match uid\n"); break; default: ERROR("Read filename, type, mode and uid, but failed " "to read or match gid\n"); break; } goto error; } switch(type) { case 'b': /* FALLTHROUGH */ case 'c': n = sscanf(def, "%u %u %n", &major, &minor, &bytes); def += bytes; if(n < 2) { ERROR("Not enough or invalid arguments in %s device " "pseudo file definition \"%s\"\n", type == 'b' ? "block" : "character", orig_def); if(n < 1) ERROR("Read filename, type, mode, uid and gid, " "but failed to read or match major\n"); else ERROR("Read filename, type, mode, uid, gid " "and major, but failed to read or " "match minor\n"); goto error; } if(major > 0xfff) { ERROR("Major %d out of range\n", major); goto error; } if(minor > 0xfffff) { ERROR("Minor %d out of range\n", minor); goto error; } /* FALLTHROUGH */ case 'd': /* FALLTHROUGH */ case 'm': /* * Check for trailing junk after expected arguments */ if(def[0] != '\0') { ERROR("Unexpected tailing characters in pseudo file " "definition \"%s\"\n", orig_def); goto error; } break; case 'f': if(def[0] == '\0') { ERROR("Not enough arguments in dynamic file pseudo " "definition \"%s\"\n", orig_def); ERROR("Expected command, which can be an executable " "or a piece of shell script\n"); goto error; } break; default: ERROR("Unsupported type %c\n", type); goto error; } if(mode > 07777) { ERROR("Mode %o out of range\n", mode); goto error; } uid = strtoll(suid, &ptr, 10); if(*ptr == '\0') { if(uid < 0 || uid > ((1LL << 32) - 1)) { ERROR("Uid %s out of range\n", suid); goto error; } } else { struct passwd *pwuid = getpwnam(suid); if(pwuid) uid = pwuid->pw_uid; else { ERROR("Uid %s invalid uid or unknown user\n", suid); goto error; } } gid = strtoll(sgid, &ptr, 10); if(*ptr == '\0') { if(gid < 0 || gid > ((1LL << 32) - 1)) { ERROR("Gid %s out of range\n", sgid); goto error; } } else { struct group *grgid = getgrnam(sgid); if(grgid) gid = grgid->gr_gid; else { ERROR("Gid %s invalid uid or unknown user\n", sgid); goto error; } } switch(type) { case 'b': mode |= S_IFBLK; break; case 'c': mode |= S_IFCHR; break; case 'd': mode |= S_IFDIR; break; case 'f': mode |= S_IFREG; break; } dev = malloc(sizeof(struct pseudo_dev)); if(dev == NULL) MEM_ERROR(); dev->type = type; dev->mode = mode; dev->uid = uid; dev->gid = gid; dev->major = major; dev->minor = minor; if(type == 'f') { dev->command = strdup(def); add_pseudo_file(dev); } pseudo = add_pseudo(pseudo, dev, filename, filename); free(filename); return TRUE; error: ERROR("Pseudo definitions should be of format\n"); ERROR("\tfilename d mode uid gid\n"); ERROR("\tfilename m mode uid gid\n"); ERROR("\tfilename b mode uid gid major minor\n"); ERROR("\tfilename c mode uid gid major minor\n"); ERROR("\tfilename f mode uid command\n"); free(filename); return FALSE; }