unsigned long check_password(const char* p){
    int* ip = (int*)p; // line 2, non-const not required
    int i;
    int res=0;
    for(i=0; i<5; i++){
        res += ip[i]; // line 6
    }
    return res;
}
Line 2 declares ip as a pointer to integer and initializes it to (int*)p. (int*)p is a C-style cast, which is in this case resolved to reinterpret_cast<int*>(const_cast<char*>(p)). See 
When should static_cast, dynamic_cast, const_cast and reinterpret_cast be used?
ip now points to the memory location of p. When using ip, the memory at that location is interpreted as holding an integer, even though it holds a char; and it can be modified, even though it was initially declared const.
Line 6 treats ip as if it was pointing to the first element of an array of int and adds the ith element of that array to res. It's likely that ip actually points to the first element of an array of char. That means, that each iteration sizeof(int) consecutive elements of that array are interpreted as the representation of a single int.
The intention of this code is probably, that a sequence of 5 ints is passed as an array of char of length sizeof(int)*5.
Note that this code is UB if p doesn't actually point to memory of a size of at least sizeof(int)*5, since the memory in that region is read. I.e. the char-array must be at least of length sizeof(int)*5.
Casting away the const in your code is not required
unsigned long check_password(const char* p){
    const int* ip = reinterpret_cast<const int*>(p);
    int res = 0;
    for (int i = 0; i < 5; ++i){
        res += ip[i];
    }
    return res;
}