aheinecke@137: /** aheinecke@137: * @file main.c aheinecke@137: * @brief Main entry point for the cinst process. aheinecke@25: * aheinecke@25: * The cinst process may or may not be run with elevated aheinecke@25: * privileges. When run with elevated privileges this aheinecke@25: * process will modify system wide certificate stores. aheinecke@25: * Otherwise only the users certificate stores are modified. aheinecke@25: * aheinecke@59: * It expects a certificatelist on stdin enclosed in a aheinecke@59: * -----BEGIN CERTIFICATE LIST----- aheinecke@59: * ... aheinecke@59: * -----END CERTIFICATE LIST----- aheinecke@59: * aheinecke@59: * Followed by additional instruction lines of: aheinecke@59: * I: aheinecke@59: * R: aheinecke@59: * aheinecke@59: * It will only execute the instructions if the aheinecke@59: * I and R instructions are also part of the signed aheinecke@59: * certificate list. The signature is validated with the aheinecke@59: * built in key. aheinecke@59: * aheinecke@59: * The special instruction "UNINSTALL" will cause the installer aheinecke@59: * to remove all certificates (Even those marked with I) that aheinecke@59: * are part of the list to be removed. aheinecke@25: * aheinecke@25: **/ aheinecke@25: #include aheinecke@64: #include aheinecke@59: #include aheinecke@59: #include aheinecke@148: #include aheinecke@59: aheinecke@59: #include "strhelp.h" aheinecke@59: #include "listutil.h" aheinecke@60: #include "errorcodes.h" aheinecke@137: #include "windowsstore.h" aheinecke@125: aheinecke@68: /* The certificate list + instructions may only be so long as aheinecke@68: * twice the accepted certificatelist size */ aheinecke@68: #define MAX_INPUT_SIZE MAX_LINE_LENGTH * MAX_LINES * 2 aheinecke@68: aheinecke@59: /* @brief Read stdin into data structures. aheinecke@59: * aheinecke@59: * Reads from stdin and sorts the input into the respective aheinecke@59: * variables. The pointers returned need to be freed by the caller. aheinecke@59: * Terminates in OOM conditions. aheinecke@59: * aheinecke@60: * The caller needs to free the memory allocated by this function aheinecke@60: * even when an error is returned. aheinecke@60: * aheinecke@148: * Uninstall certificates are all certificates that are pa aheinecke@148: * aheinecke@148: * @param[out] certificate_list the parsed certificate list aheinecke@148: * @param[out] to_install strv of installation instructions or NULL aheinecke@148: * @param[out] to_remove strv of remove instructions or NULL aheinecke@148: * @param[out] all_certs strv of uninstallation instructions or NULL aheinecke@148: * aheinecke@60: * @returns: 0 on success. An error code otherwise. aheinecke@59: */ aheinecke@163: int aheinecke@163: readInput (char **certificate_list, char ***to_install, aheinecke@163: char ***to_remove, char ***all_certs) aheinecke@59: { aheinecke@163: int lines_read = 0; aheinecke@163: int readingList = 0; aheinecke@163: size_t list_size = 0; aheinecke@163: char buf[MAX_LINE_LENGTH + 2]; aheinecke@59: aheinecke@163: if (*certificate_list || *to_install || *to_remove) aheinecke@163: { aheinecke@163: printf ("Error invalid parameters\n"); aheinecke@163: return -1; aheinecke@90: } aheinecke@90: aheinecke@163: while (fgets (buf, MAX_LINE_LENGTH + 1, stdin) ) aheinecke@163: { aheinecke@163: size_t len = strlen (buf); /* fgets ensures buf is terminated */ aheinecke@163: if (len <= 3) aheinecke@163: { aheinecke@163: printf ("Line too short.\n"); aheinecke@163: return ERR_INVALID_INPUT; aheinecke@91: } aheinecke@163: if (lines_read++ > MAX_LINES) aheinecke@163: { aheinecke@163: printf ("Too many lines\n"); aheinecke@163: return ERR_TOO_MUCH_INPUT; aheinecke@59: } aheinecke@126: aheinecke@163: if (buf[len - 2] != '\r') aheinecke@163: { aheinecke@163: if (buf[len - 1] != '\n') aheinecke@163: { aheinecke@163: printf ("Line too long.\n"); aheinecke@163: return ERR_INVALID_INPUT; aheinecke@126: } aheinecke@163: buf[len - 1] = '\r'; aheinecke@163: buf[len] = '\n'; aheinecke@163: buf[len + 1] = '\0'; aheinecke@163: len++; aheinecke@126: } aheinecke@126: aheinecke@163: if (strcmp ("-----BEGIN CERTIFICATE LIST-----\r\n", buf) == 0) aheinecke@163: { aheinecke@163: readingList = 1; aheinecke@163: continue; aheinecke@59: } aheinecke@163: if (strcmp ("-----END CERTIFICATE LIST-----\r\n", buf) == 0) aheinecke@163: { aheinecke@163: readingList = 0; aheinecke@163: continue; aheinecke@59: } aheinecke@163: if (readingList) aheinecke@163: { aheinecke@163: str_append_str (certificate_list, &list_size, buf, len); aheinecke@59: } aheinecke@163: else if (strcmp ("UNINSTALL\r\n", buf) == 0) aheinecke@163: { aheinecke@163: /* Remove trailing \r\n */ aheinecke@163: strv_append (to_remove, buf, len - 2); aheinecke@163: continue; aheinecke@163: } aheinecke@163: if (*buf == 'I') aheinecke@163: { aheinecke@163: /* Remove leading I: and trailing \r\n */ andre@216: strv_append (readingList ? all_certs : to_install, buf + 2, len - 4); aheinecke@163: continue; aheinecke@163: } aheinecke@163: if (*buf == 'R') aheinecke@163: { aheinecke@163: /* Remove leading R: and trailing \r\n */ aheinecke@163: strv_append (readingList ? all_certs : to_remove, buf + 2, len - 4); aheinecke@163: continue; aheinecke@59: } aheinecke@59: } aheinecke@59: aheinecke@163: return 0; aheinecke@59: } aheinecke@148: aheinecke@148: /** @brief Check that the insturctions match to the list aheinecke@148: * aheinecke@148: * Only certificates part of the certificate_list are allowed aheinecke@148: * for installation. aheinecke@148: * aheinecke@148: * @param[in] all_certs strv of all valid certificates in a list aheinecke@148: * @param[in] to_validate strv of instructions aheinecke@148: * aheinecke@148: * @returns 0 on success, an error otherwise aheinecke@148: */ aheinecke@163: int aheinecke@163: validate_instructions (char **all_certs, char **to_validate) aheinecke@64: { aheinecke@163: int i = 0, j = 0; aheinecke@148: aheinecke@163: if (!all_certs || strv_length (all_certs) < 1) aheinecke@163: { aheinecke@163: /* Invalid parameters */ aheinecke@163: return -1; aheinecke@148: } aheinecke@148: aheinecke@163: if (to_validate == NULL) aheinecke@163: { aheinecke@163: /* Nothing is valid */ aheinecke@163: return 0; aheinecke@148: } aheinecke@148: aheinecke@163: for (i = 0; to_validate[i]; i++) aheinecke@163: { aheinecke@163: bool found = false; aheinecke@163: for (j = 0; all_certs[j]; j++) aheinecke@163: { aheinecke@163: if (strncmp (to_validate[i], all_certs[j], MAX_LINE_LENGTH - 2) == aheinecke@163: 0) aheinecke@163: { aheinecke@163: found = true; aheinecke@163: break; aheinecke@148: } aheinecke@148: } aheinecke@163: if (!found) aheinecke@163: { aheinecke@163: printf ("Install instruction with invalid certificate\n."); aheinecke@163: return ERR_INVALID_INSTRUCTIONS; aheinecke@148: } aheinecke@148: } aheinecke@65: aheinecke@163: return 0; aheinecke@64: } aheinecke@148: aheinecke@68: aheinecke@163: int aheinecke@163: main () aheinecke@163: { aheinecke@163: char **to_install = NULL; aheinecke@163: char **to_remove = NULL; aheinecke@163: char **all_certs = NULL; aheinecke@163: char *certificate_list = NULL; aheinecke@163: size_t list_len = 0; aheinecke@163: int ret = -1; aheinecke@163: bool uninstall = false; aheinecke@60: aheinecke@163: ret = readInput (&certificate_list, &to_install, &to_remove, &all_certs); aheinecke@148: aheinecke@163: if (ret) aheinecke@163: { aheinecke@163: return ret; aheinecke@60: } aheinecke@60: aheinecke@163: if (!certificate_list) aheinecke@163: { aheinecke@163: return ERR_INVALID_INPUT_NO_LIST; aheinecke@60: } aheinecke@59: aheinecke@163: list_len = strnlen (certificate_list, MAX_INPUT_SIZE); aheinecke@59: aheinecke@163: ret = verify_list (certificate_list, list_len); aheinecke@64: aheinecke@163: if (ret) aheinecke@163: { aheinecke@163: return ERR_INVALID_SIGNATURE; aheinecke@64: } aheinecke@64: aheinecke@163: if (!strv_length (to_install) && !strv_length (to_remove) ) aheinecke@163: { aheinecke@163: return ERR_NO_INSTRUCTIONS; aheinecke@64: } aheinecke@64: aheinecke@68: aheinecke@163: /* Check that the instructions are ok to execute */ aheinecke@163: if (to_install) aheinecke@163: { aheinecke@163: ret = validate_instructions (all_certs, to_install); aheinecke@163: if (ret) aheinecke@163: { aheinecke@163: return ret; aheinecke@148: } aheinecke@64: } aheinecke@68: aheinecke@163: if (to_remove) aheinecke@163: { aheinecke@163: if (to_remove[0] aheinecke@163: && strncmp ("UNINSTALL", to_remove[0], MAX_LINE_LENGTH) == 0) aheinecke@163: { aheinecke@163: uninstall = true; aheinecke@163: strv_free (to_remove); aheinecke@163: to_remove = NULL; aheinecke@163: } aheinecke@163: else aheinecke@163: { aheinecke@163: ret = validate_instructions (all_certs, to_remove); aheinecke@163: if (ret) aheinecke@163: { aheinecke@163: return ret; aheinecke@68: } aheinecke@68: } aheinecke@68: } aheinecke@68: aheinecke@163: if (uninstall) aheinecke@163: { aheinecke@163: /* To uninstall does not have to be verified as it part of the aheinecke@163: * signed list.*/ aheinecke@163: to_remove = all_certs; aheinecke@163: } aheinecke@163: else aheinecke@163: { aheinecke@163: strv_free (all_certs); aheinecke@163: all_certs = NULL; aheinecke@68: } aheinecke@68: aheinecke@68: #ifdef WIN32 aheinecke@247: return write_stores_win (to_install, to_remove); aheinecke@68: #endif aheinecke@68: aheinecke@163: /* Make valgrind happy */ aheinecke@163: strv_free (to_install); aheinecke@163: strv_free (to_remove); aheinecke@163: free (certificate_list); aheinecke@59: aheinecke@163: return 0; aheinecke@25: }