I needed to add an extra argument maxlen: the maximal size of str that can be written to. I also modified the return type to something useful.
size_t do_insert(char *str, size_t maxlen, size_t where, char *ins)
{
    size_t len_str, len_ins;
    /* these could be arguments, too,
    ** if these are already known by the caller.
    */
    len_str = strlen(str);
    len_ins = strlen(ins);
    /* not enough space: return */
    if (len_str + len_ins >= maxlen)
        return len_str + len_ins;
    /* I don't know what should happen if the place to insert
    ** is beyond the length of str
    ** [ there also is a corner case lurking here if where == len_str]
    */
    if (where > len_str)
        return ???;
    /* make place for the insert by shifting str up.
    ** we move one byte extra: the nul character.
    */
    memmove( str + where + len_ins, str + where, len_ins + 1);
    /* put the insert where it belongs */
    memcpy ( str + where, ins, len_ins );
    /* return the length of the new string */
    return len_str + len_ins;
}
NOTE: Untested.